mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
3891 lines
110 KiB
TypeScript
3891 lines
110 KiB
TypeScript
import shuffle from 'lodash/shuffle';
|
|
import {LocationFixture} from 'sentry-fixture/locationFixture';
|
|
import {OrganizationFixture} from 'sentry-fixture/organization';
|
|
import {PageFiltersFixture} from 'sentry-fixture/pageFilters';
|
|
|
|
import {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable';
|
|
import type {NewQuery, SavedQuery} from 'sentry/types';
|
|
import type {MetaType} from 'sentry/utils/discover/eventView';
|
|
import EventView, {
|
|
isAPIPayloadSimilar,
|
|
pickRelevantLocationQueryStrings,
|
|
} from 'sentry/utils/discover/eventView';
|
|
import type {Column} from 'sentry/utils/discover/fields';
|
|
import {
|
|
CHART_AXIS_OPTIONS,
|
|
DiscoverDatasets,
|
|
DISPLAY_MODE_OPTIONS,
|
|
DisplayModes,
|
|
} from 'sentry/utils/discover/types';
|
|
import {AggregationKey, WebVital} from 'sentry/utils/fields';
|
|
import {SpanOperationBreakdownFilter} from 'sentry/views/performance/transactionSummary/filter';
|
|
import {EventsDisplayFilterName} from 'sentry/views/performance/transactionSummary/transactionEvents/utils';
|
|
|
|
const generateFields = fields =>
|
|
fields.map(field => ({
|
|
field,
|
|
}));
|
|
|
|
const generateSorts = sorts =>
|
|
sorts.map(sortName => ({
|
|
field: sortName,
|
|
kind: 'desc',
|
|
}));
|
|
|
|
const REQUIRED_CONSTRUCTOR_PROPS = {
|
|
createdBy: undefined,
|
|
end: undefined,
|
|
environment: [],
|
|
fields: [],
|
|
name: undefined,
|
|
project: [],
|
|
query: '',
|
|
start: undefined,
|
|
team: [],
|
|
sorts: [],
|
|
statsPeriod: undefined,
|
|
topEvents: undefined,
|
|
id: undefined,
|
|
display: undefined,
|
|
};
|
|
|
|
describe('EventView constructor', function () {
|
|
it('instantiates default values', function () {
|
|
const eventView = new EventView(REQUIRED_CONSTRUCTOR_PROPS);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: undefined,
|
|
name: undefined,
|
|
fields: [],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: undefined,
|
|
environment: [],
|
|
yAxis: undefined,
|
|
display: undefined,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.fromLocation()', function () {
|
|
it('maps query strings', function () {
|
|
const location = LocationFixture({
|
|
query: {
|
|
id: '42',
|
|
name: 'best query',
|
|
field: ['count()', 'id'],
|
|
widths: ['123', '456'],
|
|
sort: ['title', '-count'],
|
|
query: 'event.type:transaction',
|
|
project: ['123'],
|
|
team: ['myteams', '1', '2'],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'p95',
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
},
|
|
});
|
|
|
|
const eventView = EventView.fromLocation(location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: [
|
|
{field: 'count()', width: 123},
|
|
{field: 'id', width: 456},
|
|
],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
team: ['myteams', 1, 2],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'p95',
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
});
|
|
});
|
|
|
|
it('includes first valid statsPeriod', function () {
|
|
const location = LocationFixture({
|
|
query: {
|
|
id: '42',
|
|
name: 'best query',
|
|
field: ['count()', 'id'],
|
|
widths: ['123', '456'],
|
|
sort: ['title', '-count'],
|
|
query: 'event.type:transaction',
|
|
project: ['123'],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: ['invalid', '28d'],
|
|
environment: ['staging'],
|
|
},
|
|
});
|
|
|
|
const eventView = EventView.fromLocation(location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: [
|
|
{field: 'count()', width: 123},
|
|
{field: 'id', width: 456},
|
|
],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: '28d',
|
|
environment: ['staging'],
|
|
});
|
|
});
|
|
|
|
it('includes start and end', function () {
|
|
const location = LocationFixture({
|
|
query: {
|
|
id: '42',
|
|
name: 'best query',
|
|
field: ['count()', 'id'],
|
|
widths: ['123', '456'],
|
|
sort: ['title', '-count'],
|
|
query: 'event.type:transaction',
|
|
project: ['123'],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
environment: ['staging'],
|
|
},
|
|
});
|
|
|
|
const eventView = EventView.fromLocation(location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: [
|
|
{field: 'count()', width: 123},
|
|
{field: 'id', width: 456},
|
|
],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
start: '2019-10-01T00:00:00.000',
|
|
end: '2019-10-02T00:00:00.000',
|
|
environment: ['staging'],
|
|
});
|
|
});
|
|
|
|
it('generates event view when there are no query strings', function () {
|
|
const location = LocationFixture({
|
|
query: {},
|
|
});
|
|
|
|
const eventView = EventView.fromLocation(location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: void 0,
|
|
name: void 0,
|
|
fields: [],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
start: void 0,
|
|
end: void 0,
|
|
statsPeriod: '14d',
|
|
environment: [],
|
|
yAxis: void 0,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.fromSavedQuery()', function () {
|
|
it('maps basic properties of saved query', function () {
|
|
const saved: SavedQuery = {
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: ['count()', 'id'],
|
|
query: 'event.type:transaction',
|
|
projects: [123],
|
|
teams: ['myteams', 1],
|
|
range: '14d',
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
orderby: '-id',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
version: 2,
|
|
};
|
|
const eventView = EventView.fromSavedQuery(saved);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
team: ['myteams', 1],
|
|
start: undefined,
|
|
end: undefined,
|
|
// statsPeriod has precedence
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: undefined,
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
});
|
|
|
|
const eventView2 = EventView.fromSavedQuery({
|
|
...saved,
|
|
range: undefined,
|
|
});
|
|
expect(eventView2).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
team: ['myteams', 1],
|
|
start: '2019-10-01T00:00:00.000',
|
|
end: '2019-10-02T00:00:00.000',
|
|
statsPeriod: undefined,
|
|
environment: ['staging'],
|
|
});
|
|
});
|
|
|
|
it('maps saved query with no conditions', function () {
|
|
const saved: SavedQuery = {
|
|
orderby: '-count',
|
|
name: 'foo bar',
|
|
fields: ['release', 'count()'],
|
|
widths: ['111', '222'],
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
environment: ['dev', 'production'],
|
|
version: 2,
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
id: '5',
|
|
projects: [1],
|
|
yAxis: ['count()'],
|
|
};
|
|
|
|
const eventView = EventView.fromSavedQuery(saved);
|
|
|
|
const expected = {
|
|
id: '5',
|
|
name: 'foo bar',
|
|
fields: [
|
|
{field: 'release', width: 111},
|
|
{field: 'count()', width: 222},
|
|
],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: '',
|
|
project: [1],
|
|
environment: ['dev', 'production'],
|
|
yAxis: 'count()',
|
|
};
|
|
|
|
expect(eventView).toMatchObject(expected);
|
|
});
|
|
|
|
it('maps properties from v2 saved query', function () {
|
|
const saved: SavedQuery = {
|
|
id: '42',
|
|
projects: [123],
|
|
name: 'best query',
|
|
fields: ['count()', 'title'],
|
|
range: '14d',
|
|
start: '',
|
|
end: '',
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
version: 2,
|
|
};
|
|
const eventView = EventView.fromSavedQuery(saved);
|
|
expect(eventView.fields).toEqual([
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'title', width: COL_WIDTH_UNDEFINED},
|
|
]);
|
|
expect(eventView.name).toEqual(saved.name);
|
|
expect(eventView.statsPeriod).toEqual('14d');
|
|
expect(eventView.start).toEqual(undefined);
|
|
expect(eventView.end).toEqual(undefined);
|
|
});
|
|
|
|
it('saved queries are equal when start and end datetime differ in format', function () {
|
|
const saved: SavedQuery = {
|
|
orderby: '-count_timestamp',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
name: 'release query',
|
|
fields: ['release', 'count(timestamp)'],
|
|
dateCreated: '2019-10-30T05:10:23.718937Z',
|
|
environment: ['dev', 'production'],
|
|
start: '2019-10-20T21:02:51+0000',
|
|
version: 2,
|
|
dateUpdated: '2019-10-30T07:25:58.291917Z',
|
|
id: '3',
|
|
projects: [1],
|
|
};
|
|
|
|
const eventView = EventView.fromSavedQuery(saved);
|
|
|
|
const eventView2 = EventView.fromSavedQuery({
|
|
...saved,
|
|
start: '2019-10-20T21:02:51Z',
|
|
end: '2019-10-23T19:27:04Z',
|
|
});
|
|
|
|
expect(eventView.isEqualTo(eventView2)).toBe(true);
|
|
|
|
const eventView3 = EventView.fromSavedQuery({
|
|
...saved,
|
|
start: '2019-10-20T21:02:51Z',
|
|
});
|
|
|
|
expect(eventView.isEqualTo(eventView3)).toBe(true);
|
|
|
|
const eventView4 = EventView.fromSavedQuery({
|
|
...saved,
|
|
end: '2019-10-23T19:27:04Z',
|
|
});
|
|
|
|
expect(eventView.isEqualTo(eventView4)).toBe(true);
|
|
});
|
|
|
|
it('saved queries are not equal when datetime selection are invalid', function () {
|
|
const saved: SavedQuery = {
|
|
orderby: '-count_timestamp',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
name: 'release query',
|
|
fields: ['release', 'count(timestamp)'],
|
|
dateCreated: '2019-10-30T05:10:23.718937Z',
|
|
environment: ['dev', 'production'],
|
|
start: '2019-10-20T21:02:51+0000',
|
|
version: 2,
|
|
dateUpdated: '2019-10-30T07:25:58.291917Z',
|
|
id: '3',
|
|
projects: [1],
|
|
};
|
|
|
|
const eventView = EventView.fromSavedQuery(saved);
|
|
|
|
const eventView2 = EventView.fromSavedQuery({
|
|
...saved,
|
|
start: '',
|
|
});
|
|
|
|
expect(eventView.isEqualTo(eventView2)).toBe(false);
|
|
|
|
const eventView3 = EventView.fromSavedQuery({
|
|
...saved,
|
|
end: '',
|
|
});
|
|
|
|
expect(eventView.isEqualTo(eventView3)).toBe(false);
|
|
|
|
// this is expected since datetime (start and end) are normalized
|
|
expect(eventView2.isEqualTo(eventView3)).toBe(true);
|
|
});
|
|
|
|
it('saved queries with undefined yAxis are defaulted to count() when comparing with isEqualTo', function () {
|
|
const saved: SavedQuery = {
|
|
orderby: '-count_timestamp',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
name: 'release query',
|
|
fields: ['release', 'count(timestamp)'],
|
|
dateCreated: '2019-10-30T05:10:23.718937Z',
|
|
environment: ['dev', 'production'],
|
|
start: '2019-10-20T21:02:51+0000',
|
|
version: 2,
|
|
dateUpdated: '2019-10-30T07:25:58.291917Z',
|
|
id: '3',
|
|
projects: [1],
|
|
};
|
|
|
|
const eventView = EventView.fromSavedQuery(saved);
|
|
|
|
const eventView2 = EventView.fromSavedQuery({
|
|
...saved,
|
|
yAxis: ['count()'],
|
|
});
|
|
|
|
expect(eventView.isEqualTo(eventView2)).toBe(true);
|
|
});
|
|
|
|
it('uses the first yAxis from the SavedQuery', function () {
|
|
const saved: SavedQuery = {
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: ['count()', 'id'],
|
|
query: 'event.type:transaction',
|
|
projects: [123],
|
|
teams: ['myteams', 1],
|
|
range: '14d',
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
orderby: '-id',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
yAxis: ['count()'],
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
version: 2,
|
|
};
|
|
const eventView = EventView.fromSavedQuery(saved);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
team: ['myteams', 1],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'count()',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
display: 'previous',
|
|
});
|
|
});
|
|
|
|
it('preserves utc with start/end', function () {
|
|
const saved: SavedQuery = {
|
|
id: '42',
|
|
version: 2,
|
|
projects: [123],
|
|
name: 'best query',
|
|
query: 'event.type:transaction',
|
|
fields: ['count()', 'title'],
|
|
start: '2019-10-20T21:02:51+0000',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
utc: 'true',
|
|
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
};
|
|
const eventView = EventView.fromSavedQuery(saved);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'title', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
query: 'event.type:transaction',
|
|
start: '2019-10-20T21:02:51.000',
|
|
end: '2019-10-23T19:27:04.000',
|
|
utc: 'true',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.fromNewQueryWithPageFilters()', function () {
|
|
const prebuiltQuery: NewQuery = {
|
|
id: undefined,
|
|
name: 'Page Filter Events',
|
|
query: '',
|
|
projects: undefined,
|
|
fields: ['title', 'project', 'timestamp'],
|
|
orderby: '-timestamp',
|
|
version: 2,
|
|
};
|
|
|
|
it('maps basic properties of a prebuilt query', function () {
|
|
const pageFilters = PageFiltersFixture();
|
|
|
|
const eventView = EventView.fromNewQueryWithPageFilters(prebuiltQuery, pageFilters);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: undefined,
|
|
name: 'Page Filter Events',
|
|
fields: [{field: 'title'}, {field: 'project'}, {field: 'timestamp'}],
|
|
sorts: [{field: 'timestamp', kind: 'desc'}],
|
|
query: '',
|
|
project: [],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: '14d',
|
|
environment: [],
|
|
yAxis: undefined,
|
|
});
|
|
});
|
|
|
|
it('merges page filter values', function () {
|
|
const pageFilters = PageFiltersFixture({
|
|
datetime: {
|
|
period: '3d',
|
|
start: null,
|
|
end: null,
|
|
utc: null,
|
|
},
|
|
projects: [42],
|
|
environments: ['prod'],
|
|
});
|
|
|
|
const eventView = EventView.fromNewQueryWithPageFilters(prebuiltQuery, pageFilters);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: undefined,
|
|
name: 'Page Filter Events',
|
|
fields: [{field: 'title'}, {field: 'project'}, {field: 'timestamp'}],
|
|
sorts: [{field: 'timestamp', kind: 'desc'}],
|
|
query: '',
|
|
project: [42],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: '3d',
|
|
environment: ['prod'],
|
|
yAxis: undefined,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.fromNewQueryWithLocation()', function () {
|
|
const prebuiltQuery: NewQuery = {
|
|
id: undefined,
|
|
name: 'Sampled Events',
|
|
query: '',
|
|
projects: [],
|
|
fields: ['title', 'event.type', 'project', 'user', 'timestamp'],
|
|
orderby: '-timestamp',
|
|
version: 2,
|
|
};
|
|
|
|
it('maps basic properties of a prebuilt query', function () {
|
|
const location = LocationFixture({
|
|
query: {
|
|
statsPeriod: '99d',
|
|
},
|
|
});
|
|
|
|
const eventView = EventView.fromNewQueryWithLocation(prebuiltQuery, location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: undefined,
|
|
name: 'Sampled Events',
|
|
fields: [
|
|
{field: 'title'},
|
|
{field: 'event.type'},
|
|
{field: 'project'},
|
|
{field: 'user'},
|
|
{field: 'timestamp'},
|
|
],
|
|
sorts: [{field: 'timestamp', kind: 'desc'}],
|
|
query: '',
|
|
project: [],
|
|
start: undefined,
|
|
end: undefined,
|
|
// statsPeriod has precedence
|
|
statsPeriod: '99d',
|
|
environment: [],
|
|
yAxis: undefined,
|
|
});
|
|
});
|
|
|
|
it('merges global selection values', function () {
|
|
const location = LocationFixture({
|
|
query: {
|
|
statsPeriod: '99d',
|
|
project: ['456'],
|
|
environment: ['prod'],
|
|
},
|
|
});
|
|
|
|
const eventView = EventView.fromNewQueryWithLocation(prebuiltQuery, location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: undefined,
|
|
name: 'Sampled Events',
|
|
fields: [
|
|
{field: 'title'},
|
|
{field: 'event.type'},
|
|
{field: 'project'},
|
|
{field: 'user'},
|
|
{field: 'timestamp'},
|
|
],
|
|
sorts: [{field: 'timestamp', kind: 'desc'}],
|
|
query: '',
|
|
project: [456],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: '99d',
|
|
environment: ['prod'],
|
|
yAxis: undefined,
|
|
});
|
|
});
|
|
|
|
it('new query takes precedence over global selection values', function () {
|
|
const location = LocationFixture({
|
|
query: {
|
|
statsPeriod: '99d',
|
|
project: ['456'],
|
|
environment: ['prod'],
|
|
},
|
|
});
|
|
|
|
const prebuiltQuery2: NewQuery = {
|
|
...prebuiltQuery,
|
|
range: '42d',
|
|
projects: [987],
|
|
environment: ['staging'],
|
|
};
|
|
|
|
const eventView = EventView.fromNewQueryWithLocation(prebuiltQuery2, location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: undefined,
|
|
name: 'Sampled Events',
|
|
fields: [
|
|
{field: 'title'},
|
|
{field: 'event.type'},
|
|
{field: 'project'},
|
|
{field: 'user'},
|
|
{field: 'timestamp'},
|
|
],
|
|
sorts: [{field: 'timestamp', kind: 'desc'}],
|
|
query: '',
|
|
project: [987],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: '42d',
|
|
environment: ['staging'],
|
|
yAxis: undefined,
|
|
});
|
|
|
|
// also test start and end
|
|
|
|
const location2 = LocationFixture({
|
|
query: {
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
project: ['456'],
|
|
environment: ['prod'],
|
|
},
|
|
});
|
|
|
|
const prebuiltQuery3: NewQuery = {
|
|
...prebuiltQuery,
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
projects: [987],
|
|
environment: ['staging'],
|
|
};
|
|
|
|
const eventView2 = EventView.fromNewQueryWithLocation(prebuiltQuery3, location2);
|
|
|
|
expect(eventView2).toMatchObject({
|
|
id: undefined,
|
|
name: 'Sampled Events',
|
|
fields: [
|
|
{field: 'title'},
|
|
{field: 'event.type'},
|
|
{field: 'project'},
|
|
{field: 'user'},
|
|
{field: 'timestamp'},
|
|
],
|
|
sorts: [{field: 'timestamp', kind: 'desc'}],
|
|
query: '',
|
|
project: [987],
|
|
start: '2019-10-01T00:00:00.000',
|
|
end: '2019-10-02T00:00:00.000',
|
|
statsPeriod: undefined,
|
|
environment: ['staging'],
|
|
yAxis: undefined,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.fromSavedQueryOrLocation()', function () {
|
|
it('maps basic properties of saved query', function () {
|
|
const saved: SavedQuery = {
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: ['count()', 'id'],
|
|
query: 'event.type:transaction',
|
|
projects: [123],
|
|
range: '14d',
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
orderby: '-id',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
version: 2,
|
|
};
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
statsPeriod: '14d',
|
|
project: ['123'],
|
|
team: ['myteams', '1', '2'],
|
|
environment: ['staging'],
|
|
},
|
|
});
|
|
const eventView = EventView.fromSavedQueryOrLocation(saved, location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
team: ['myteams', 1, 2],
|
|
start: undefined,
|
|
end: undefined,
|
|
// statsPeriod has precedence
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: undefined,
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
});
|
|
|
|
const savedQuery2: SavedQuery = {...saved, range: undefined};
|
|
const location2 = LocationFixture({
|
|
query: {
|
|
project: ['123'],
|
|
environment: ['staging'],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
},
|
|
});
|
|
|
|
const eventView2 = EventView.fromSavedQueryOrLocation(savedQuery2, location2);
|
|
|
|
expect(eventView2).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
start: '2019-10-01T00:00:00.000',
|
|
end: '2019-10-02T00:00:00.000',
|
|
statsPeriod: undefined,
|
|
environment: ['staging'],
|
|
});
|
|
});
|
|
|
|
it('overrides saved query params with location params', function () {
|
|
const saved: SavedQuery = {
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: ['count()', 'id'],
|
|
query: 'event.type:transaction',
|
|
projects: [123],
|
|
range: '14d',
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
orderby: '-id',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
version: 2,
|
|
};
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
id: '42',
|
|
statsPeriod: '7d',
|
|
project: ['3'],
|
|
},
|
|
});
|
|
const eventView = EventView.fromSavedQueryOrLocation(saved, location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: 'event.type:transaction',
|
|
project: [3],
|
|
start: undefined,
|
|
end: undefined,
|
|
// statsPeriod has precedence
|
|
statsPeriod: '7d',
|
|
environment: [],
|
|
yAxis: undefined,
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
});
|
|
});
|
|
|
|
it('maps saved query with no conditions', function () {
|
|
const saved: SavedQuery = {
|
|
orderby: '-count',
|
|
name: 'foo bar',
|
|
fields: ['release', 'count()'],
|
|
widths: ['111', '222'],
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
query: '',
|
|
environment: [],
|
|
version: 2,
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
projects: [123],
|
|
id: '5',
|
|
yAxis: ['count()'],
|
|
};
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
id: '5',
|
|
project: ['1'],
|
|
},
|
|
});
|
|
|
|
const eventView = EventView.fromSavedQueryOrLocation(saved, location);
|
|
|
|
const expected = {
|
|
id: '5',
|
|
name: 'foo bar',
|
|
fields: [
|
|
{field: 'release', width: 111},
|
|
{field: 'count()', width: 222},
|
|
],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: '',
|
|
project: [1],
|
|
yAxis: 'count()',
|
|
};
|
|
|
|
expect(eventView).toMatchObject(expected);
|
|
});
|
|
|
|
it('maps query with cleared conditions', function () {
|
|
const saved: SavedQuery = {
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: ['count()', 'id'],
|
|
query: 'event.type:transaction',
|
|
projects: [123],
|
|
range: '14d',
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
orderby: '-id',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
dateUpdated: '2019-10-30T06:13:17.632096Z',
|
|
dateCreated: '2019-10-30T06:13:17.632078Z',
|
|
version: 2,
|
|
};
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
id: '42',
|
|
statsPeriod: '7d',
|
|
},
|
|
});
|
|
const eventView = EventView.fromSavedQueryOrLocation(saved, location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: 'event.type:transaction',
|
|
start: undefined,
|
|
end: undefined,
|
|
// statsPeriod has precedence
|
|
statsPeriod: '7d',
|
|
environment: [],
|
|
yAxis: undefined,
|
|
display: 'previous',
|
|
});
|
|
|
|
const location2 = LocationFixture({
|
|
query: {
|
|
id: '42',
|
|
statsPeriod: '7d',
|
|
query: '',
|
|
},
|
|
});
|
|
const eventView2 = EventView.fromSavedQueryOrLocation(saved, location2);
|
|
|
|
expect(eventView2).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: '',
|
|
start: undefined,
|
|
end: undefined,
|
|
// statsPeriod has precedence
|
|
statsPeriod: '7d',
|
|
environment: [],
|
|
yAxis: undefined,
|
|
display: 'previous',
|
|
});
|
|
});
|
|
|
|
it('event views are equal when start and end datetime differ in format', function () {
|
|
const saved: SavedQuery = {
|
|
orderby: '-count_timestamp',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
name: 'release query',
|
|
fields: ['release', 'count(timestamp)'],
|
|
dateCreated: '2019-10-30T05:10:23.718937Z',
|
|
environment: ['dev', 'production'],
|
|
start: '2019-10-20T21:02:51+0000',
|
|
projects: [123],
|
|
version: 2,
|
|
dateUpdated: '2019-10-30T07:25:58.291917Z',
|
|
id: '3',
|
|
};
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
id: '3',
|
|
start: '2019-10-20T21:02:51+0000',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
},
|
|
});
|
|
|
|
const eventView = EventView.fromSavedQueryOrLocation(saved, location);
|
|
|
|
const location2 = LocationFixture({
|
|
query: {
|
|
id: '3',
|
|
start: '2019-10-20T21:02:51Z',
|
|
end: '2019-10-23T19:27:04Z',
|
|
},
|
|
});
|
|
const eventView2 = EventView.fromSavedQueryOrLocation(saved, location2);
|
|
|
|
expect(eventView.isEqualTo(eventView2)).toBe(true);
|
|
|
|
const location3 = LocationFixture({
|
|
query: {
|
|
id: '3',
|
|
start: '2019-10-20T21:02:51Z',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
},
|
|
});
|
|
const eventView3 = EventView.fromSavedQueryOrLocation(saved, location3);
|
|
|
|
expect(eventView.isEqualTo(eventView3)).toBe(true);
|
|
|
|
const location4 = LocationFixture({
|
|
query: {
|
|
id: '3',
|
|
start: '2019-10-20T21:02:51+0000',
|
|
end: '2019-10-23T19:27:04Z',
|
|
},
|
|
});
|
|
const eventView4 = EventView.fromSavedQueryOrLocation(saved, location4);
|
|
|
|
expect(eventView.isEqualTo(eventView4)).toBe(true);
|
|
});
|
|
|
|
it('event views are not equal when datetime selection are invalid', function () {
|
|
const saved: SavedQuery = {
|
|
orderby: '-count_timestamp',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
name: 'release query',
|
|
fields: ['release', 'count(timestamp)'],
|
|
dateCreated: '2019-10-30T05:10:23.718937Z',
|
|
environment: ['dev', 'production'],
|
|
start: '2019-10-20T21:02:51+0000',
|
|
version: 2,
|
|
dateUpdated: '2019-10-30T07:25:58.291917Z',
|
|
id: '3',
|
|
projects: [1],
|
|
};
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
id: '3',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
start: '2019-10-20T21:02:51+0000',
|
|
},
|
|
});
|
|
|
|
const eventView = EventView.fromSavedQueryOrLocation(saved, location);
|
|
|
|
const location2 = LocationFixture({
|
|
query: {
|
|
id: '3',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
start: '',
|
|
},
|
|
});
|
|
const eventView2 = EventView.fromSavedQueryOrLocation(saved, location2);
|
|
|
|
expect(eventView.isEqualTo(eventView2)).toBe(false);
|
|
|
|
const location3 = LocationFixture({
|
|
query: {
|
|
id: '3',
|
|
end: '',
|
|
start: '2019-10-20T21:02:51+0000',
|
|
},
|
|
});
|
|
const eventView3 = EventView.fromSavedQueryOrLocation(saved, location3);
|
|
|
|
expect(eventView.isEqualTo(eventView3)).toBe(false);
|
|
|
|
// this is expected since datetime (start and end) are normalized
|
|
expect(eventView2.isEqualTo(eventView3)).toBe(true);
|
|
});
|
|
|
|
it('uses the first yAxis from the SavedQuery', function () {
|
|
const saved: SavedQuery = {
|
|
id: '42',
|
|
name: 'best query',
|
|
fields: ['count()', 'id'],
|
|
query: 'event.type:transaction',
|
|
projects: [123],
|
|
range: '14d',
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
orderby: '-id',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
yAxis: ['count()', 'failure_count()'],
|
|
dateCreated: '2019-10-30T05:10:23.718937Z',
|
|
dateUpdated: '2019-10-30T07:25:58.291917Z',
|
|
version: 2,
|
|
};
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
statsPeriod: '14d',
|
|
project: ['123'],
|
|
team: ['myteams', '1', '2'],
|
|
environment: ['staging'],
|
|
},
|
|
});
|
|
const eventView = EventView.fromSavedQueryOrLocation(saved, location);
|
|
|
|
expect(eventView).toMatchObject({
|
|
id: saved.id,
|
|
name: saved.name,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'id', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [{field: 'id', kind: 'desc'}],
|
|
query: 'event.type:transaction',
|
|
project: [123],
|
|
team: ['myteams', 1, 2],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'count()',
|
|
display: 'previous',
|
|
});
|
|
});
|
|
|
|
it('filters out invalid teams', function () {
|
|
const eventView = EventView.fromSavedQueryOrLocation(
|
|
undefined,
|
|
LocationFixture({
|
|
query: {
|
|
statsPeriod: '14d',
|
|
project: ['123'],
|
|
team: ['myteams', '1', 'unassigned'],
|
|
environment: ['staging'],
|
|
},
|
|
})
|
|
);
|
|
|
|
expect(eventView.team).toEqual(['myteams', 1]);
|
|
});
|
|
});
|
|
|
|
describe('EventView.generateQueryStringObject()', function () {
|
|
it('skips empty values', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['id', 'title']),
|
|
sorts: [],
|
|
project: [],
|
|
environment: [],
|
|
statsPeriod: '',
|
|
start: undefined,
|
|
end: undefined,
|
|
yAxis: undefined,
|
|
display: 'previous',
|
|
});
|
|
|
|
const expected = {
|
|
id: undefined,
|
|
name: undefined,
|
|
field: ['id', 'title'],
|
|
widths: [],
|
|
sort: [],
|
|
query: '',
|
|
project: [],
|
|
environment: [],
|
|
display: 'previous',
|
|
yAxis: 'count()',
|
|
};
|
|
|
|
expect(eventView.generateQueryStringObject()).toEqual(expected);
|
|
});
|
|
|
|
it('generates query string object', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [
|
|
{field: 'count()', width: 123},
|
|
{field: 'project.id', width: 456},
|
|
],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'count()',
|
|
display: 'releases',
|
|
interval: '1m',
|
|
};
|
|
|
|
const eventView = new EventView(state);
|
|
|
|
const expected = {
|
|
id: '1234',
|
|
name: 'best query',
|
|
field: ['count()', 'project.id'],
|
|
widths: [123, 456],
|
|
sort: ['-count'],
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'count()',
|
|
display: 'releases',
|
|
interval: '1m',
|
|
};
|
|
|
|
expect(eventView.generateQueryStringObject()).toEqual(expected);
|
|
});
|
|
|
|
it('encodes fields', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'id'}, {field: 'title'}],
|
|
sorts: [],
|
|
});
|
|
const query = eventView.generateQueryStringObject();
|
|
expect(query.field).toEqual(['id', 'title']);
|
|
});
|
|
|
|
it('returns a copy of data preventing mutation', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'id'}, {field: 'title'}],
|
|
sorts: [],
|
|
});
|
|
const query = eventView.generateQueryStringObject();
|
|
if (Array.isArray(query.field)) {
|
|
query.field.push('newthing');
|
|
}
|
|
|
|
// Getting the query again should return the original values.
|
|
const secondQuery = eventView.generateQueryStringObject();
|
|
expect(secondQuery.field).toEqual(['id', 'title']);
|
|
|
|
expect(query).not.toEqual(secondQuery);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getEventsAPIPayload()', function () {
|
|
it('generates the API payload', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '34',
|
|
name: 'amazing query',
|
|
fields: generateFields(['id']),
|
|
sorts: generateSorts(['id']),
|
|
query: 'event.type:csp',
|
|
project: [567],
|
|
environment: ['prod'],
|
|
yAxis: 'users',
|
|
display: 'releases',
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(LocationFixture())).toEqual({
|
|
field: ['id'],
|
|
per_page: 50,
|
|
sort: '-id',
|
|
query: 'event.type:csp',
|
|
project: ['567'],
|
|
environment: ['prod'],
|
|
statsPeriod: '14d',
|
|
});
|
|
});
|
|
|
|
it('does not append query conditions in location', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['id']),
|
|
sorts: [],
|
|
query: 'event.type:csp',
|
|
});
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
query: 'TypeError',
|
|
},
|
|
});
|
|
expect(eventView.getEventsAPIPayload(location).query).toEqual('event.type:csp');
|
|
});
|
|
|
|
it('only includes at most one sort key', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['count()', 'title']),
|
|
sorts: generateSorts(['title', AggregationKey.COUNT]),
|
|
query: 'event.type:csp',
|
|
});
|
|
|
|
const location = LocationFixture({
|
|
query: {},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location).sort).toEqual('-title');
|
|
});
|
|
|
|
it('only includes sort keys that are defined in fields', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['title', 'count()']),
|
|
sorts: generateSorts(['project', AggregationKey.COUNT]),
|
|
query: 'event.type:csp',
|
|
});
|
|
|
|
const location = LocationFixture({
|
|
query: {},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location).sort).toEqual('-count');
|
|
});
|
|
|
|
it('only includes relevant query strings', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['title', 'count()']),
|
|
sorts: generateSorts(['project', AggregationKey.COUNT]),
|
|
query: 'event.type:csp',
|
|
});
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
start: '2020-08-12 12:13:14',
|
|
end: '2020-08-26 12:13:14',
|
|
utc: 'true',
|
|
statsPeriod: '14d',
|
|
cursor: 'some cursor',
|
|
yAxis: 'count()',
|
|
|
|
// irrelevant query strings
|
|
bestCountry: 'canada',
|
|
project: '1234',
|
|
environment: ['staging'],
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
project: [],
|
|
environment: [],
|
|
utc: 'true',
|
|
statsPeriod: '14d',
|
|
|
|
field: ['title', 'count()'],
|
|
per_page: 50,
|
|
query: 'event.type:csp',
|
|
sort: '-count',
|
|
cursor: 'some cursor',
|
|
});
|
|
});
|
|
|
|
it('includes default coerced statsPeriod when omitted or is invalid', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['title', 'count()']),
|
|
sorts: generateSorts(['project', AggregationKey.COUNT]),
|
|
query: 'event.type:csp',
|
|
project: [1234],
|
|
environment: ['staging'],
|
|
});
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
start: '',
|
|
end: '',
|
|
utc: 'true',
|
|
// invalid statsPeriod string
|
|
statsPeriod: 'invalid',
|
|
cursor: 'some cursor',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
project: ['1234'],
|
|
environment: ['staging'],
|
|
utc: 'true',
|
|
statsPeriod: '14d',
|
|
|
|
field: ['title', 'count()'],
|
|
per_page: 50,
|
|
query: 'event.type:csp',
|
|
sort: '-count',
|
|
cursor: 'some cursor',
|
|
});
|
|
|
|
const location2 = LocationFixture({
|
|
query: {
|
|
start: '',
|
|
end: '',
|
|
utc: 'true',
|
|
// statsPeriod is omitted here
|
|
cursor: 'some cursor',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location2)).toEqual({
|
|
project: ['1234'],
|
|
environment: ['staging'],
|
|
utc: 'true',
|
|
statsPeriod: '14d',
|
|
|
|
field: ['title', 'count()'],
|
|
per_page: 50,
|
|
query: 'event.type:csp',
|
|
sort: '-count',
|
|
cursor: 'some cursor',
|
|
});
|
|
});
|
|
|
|
it('includes default coerced statsPeriod when either start or end is only provided', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['title', 'count()']),
|
|
sorts: generateSorts(['project', AggregationKey.COUNT]),
|
|
query: 'event.type:csp',
|
|
project: [1234],
|
|
environment: ['staging'],
|
|
});
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
start: '',
|
|
utc: 'true',
|
|
statsPeriod: 'invalid',
|
|
cursor: 'some cursor',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
project: ['1234'],
|
|
environment: ['staging'],
|
|
utc: 'true',
|
|
statsPeriod: '14d',
|
|
|
|
field: ['title', 'count()'],
|
|
per_page: 50,
|
|
query: 'event.type:csp',
|
|
sort: '-count',
|
|
cursor: 'some cursor',
|
|
});
|
|
|
|
const location2 = LocationFixture({
|
|
query: {
|
|
end: '',
|
|
utc: 'true',
|
|
statsPeriod: 'invalid',
|
|
cursor: 'some cursor',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location2)).toEqual({
|
|
project: ['1234'],
|
|
environment: ['staging'],
|
|
utc: 'true',
|
|
statsPeriod: '14d',
|
|
|
|
field: ['title', 'count()'],
|
|
per_page: 50,
|
|
query: 'event.type:csp',
|
|
sort: '-count',
|
|
cursor: 'some cursor',
|
|
});
|
|
});
|
|
|
|
it('includes start and end', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['title', 'count()']),
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:csp',
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
environment: [],
|
|
project: [],
|
|
});
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
// these should not be part of the API payload
|
|
statsPeriod: '55d',
|
|
period: '55d',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
field: ['title', 'count()'],
|
|
sort: '-count',
|
|
query: 'event.type:csp',
|
|
start: '2019-10-01T00:00:00.000',
|
|
end: '2019-10-02T00:00:00.000',
|
|
per_page: 50,
|
|
project: [],
|
|
environment: [],
|
|
});
|
|
});
|
|
|
|
it("an eventview's date selection has higher precedence than the date selection in the query string", function () {
|
|
const initialState = {
|
|
fields: generateFields(['title', 'count()']),
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:csp',
|
|
environment: [],
|
|
project: [],
|
|
};
|
|
|
|
const output = {
|
|
field: ['title', 'count()'],
|
|
sort: '-count',
|
|
query: 'event.type:csp',
|
|
per_page: 50,
|
|
project: [],
|
|
environment: [],
|
|
};
|
|
|
|
// eventview's statsPeriod has highest precedence
|
|
|
|
let eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
...initialState,
|
|
statsPeriod: '90d',
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
});
|
|
|
|
let location = LocationFixture({
|
|
query: {
|
|
// these should not be part of the API payload
|
|
statsPeriod: '55d',
|
|
period: '30d',
|
|
start: '2020-10-01T00:00:00',
|
|
end: '2020-10-02T00:00:00',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
...output,
|
|
statsPeriod: '90d',
|
|
});
|
|
|
|
// eventview's start/end has higher precedence than the date selection in the query string
|
|
|
|
eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
...initialState,
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
});
|
|
|
|
location = LocationFixture({
|
|
query: {
|
|
// these should not be part of the API payload
|
|
statsPeriod: '55d',
|
|
period: '30d',
|
|
start: '2020-10-01T00:00:00',
|
|
end: '2020-10-02T00:00:00',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
...output,
|
|
start: '2019-10-01T00:00:00.000',
|
|
end: '2019-10-02T00:00:00.000',
|
|
});
|
|
|
|
// the date selection in the query string should be applied as expected
|
|
|
|
eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
...initialState,
|
|
});
|
|
|
|
location = LocationFixture({
|
|
query: {
|
|
statsPeriod: '55d',
|
|
period: '30d',
|
|
start: '2020-10-01T00:00:00',
|
|
end: '2020-10-02T00:00:00',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
...output,
|
|
statsPeriod: '55d',
|
|
});
|
|
|
|
location = LocationFixture({
|
|
query: {
|
|
period: '30d',
|
|
start: '2020-10-01T00:00:00',
|
|
end: '2020-10-02T00:00:00',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
...output,
|
|
statsPeriod: '30d',
|
|
});
|
|
|
|
location = LocationFixture({
|
|
query: {
|
|
start: '2020-10-01T00:00:00',
|
|
end: '2020-10-02T00:00:00',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getEventsAPIPayload(location)).toEqual({
|
|
...output,
|
|
start: '2020-10-01T00:00:00.000',
|
|
end: '2020-10-02T00:00:00.000',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.getFacetsAPIPayload()', function () {
|
|
it('only includes relevant query strings', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: generateFields(['title', 'count()']),
|
|
sorts: generateSorts(['project', AggregationKey.COUNT]),
|
|
query: 'event.type:csp',
|
|
});
|
|
|
|
const location = LocationFixture({
|
|
query: {
|
|
start: '',
|
|
end: '',
|
|
utc: 'true',
|
|
statsPeriod: '14d',
|
|
|
|
// irrelevant query strings
|
|
bestCountry: 'canada',
|
|
cursor: 'some cursor',
|
|
sort: 'the world',
|
|
project: '1234',
|
|
environment: ['staging'],
|
|
display: 'releases',
|
|
},
|
|
});
|
|
|
|
expect(eventView.getFacetsAPIPayload(location)).toEqual({
|
|
project: [],
|
|
environment: [],
|
|
utc: 'true',
|
|
statsPeriod: '14d',
|
|
|
|
query: 'event.type:csp',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.toNewQuery()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [
|
|
{field: 'count()', width: 123},
|
|
{field: 'project.id', width: 456},
|
|
],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
display: 'releases',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
|
|
it('outputs the right fields', function () {
|
|
const eventView = new EventView(state);
|
|
|
|
const output = eventView.toNewQuery();
|
|
|
|
const expected = {
|
|
version: 2,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: ['count()', 'project.id'],
|
|
widths: ['123', '456'],
|
|
orderby: '-count',
|
|
query: 'event.type:error',
|
|
projects: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
range: '14d',
|
|
environment: ['staging'],
|
|
display: 'releases',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
|
|
expect(output).toEqual(expected);
|
|
});
|
|
|
|
it('omits query when query is an empty string', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
};
|
|
|
|
modifiedState.query = '';
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
|
|
const output = eventView.toNewQuery();
|
|
|
|
const expected = {
|
|
version: 2,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: ['count()', 'project.id'],
|
|
widths: ['123', '456'],
|
|
orderby: '-count',
|
|
projects: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
range: '14d',
|
|
environment: ['staging'],
|
|
display: 'releases',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
|
|
expect(output).toEqual(expected);
|
|
});
|
|
|
|
it('omits query when query is not defined', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
};
|
|
|
|
modifiedState.query = '';
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
|
|
const output = eventView.toNewQuery();
|
|
|
|
const expected = {
|
|
version: 2,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: ['count()', 'project.id'],
|
|
widths: ['123', '456'],
|
|
orderby: '-count',
|
|
projects: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
range: '14d',
|
|
environment: ['staging'],
|
|
display: 'releases',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
|
|
expect(output).toEqual(expected);
|
|
});
|
|
});
|
|
|
|
describe('EventView.isValid()', function () {
|
|
it('event view is valid when there is at least one field', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: [],
|
|
project: [],
|
|
});
|
|
|
|
expect(eventView.isValid()).toBe(true);
|
|
});
|
|
|
|
it('event view is not valid when there are no fields', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
project: [],
|
|
});
|
|
|
|
expect(eventView.isValid()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getWidths()', function () {
|
|
it('returns widths', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'project.id', width: 2020},
|
|
{field: 'title', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'time', width: 420},
|
|
{field: 'lcp', width: 69},
|
|
{field: 'lcp', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'fcp', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'cls', width: COL_WIDTH_UNDEFINED},
|
|
],
|
|
sorts: [],
|
|
project: [],
|
|
});
|
|
|
|
expect(eventView.getWidths()).toEqual([
|
|
COL_WIDTH_UNDEFINED,
|
|
2020,
|
|
COL_WIDTH_UNDEFINED,
|
|
420,
|
|
69,
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getFields()', function () {
|
|
it('returns fields', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: [],
|
|
project: [],
|
|
});
|
|
|
|
expect(eventView.getFields()).toEqual(['count()', 'project.id']);
|
|
});
|
|
});
|
|
|
|
describe('EventView.numOfColumns()', function () {
|
|
it('returns correct number of columns', function () {
|
|
// has columns
|
|
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: [],
|
|
project: [],
|
|
});
|
|
|
|
expect(eventView.numOfColumns()).toBe(2);
|
|
|
|
// has no columns
|
|
|
|
const eventView2 = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
project: [],
|
|
});
|
|
|
|
expect(eventView2.numOfColumns()).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getDays()', function () {
|
|
it('returns the right number of days for statsPeriod', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
statsPeriod: '14d',
|
|
});
|
|
|
|
expect(eventView.getDays()).toBe(14);
|
|
|
|
const eventView2 = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
statsPeriod: '12h',
|
|
});
|
|
|
|
expect(eventView2.getDays()).toBe(0.5);
|
|
});
|
|
|
|
it('returns the right number of days for start/end', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
});
|
|
|
|
expect(eventView.getDays()).toBe(1);
|
|
|
|
const eventView2 = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-15T00:00:00',
|
|
});
|
|
expect(eventView2.getDays()).toBe(14);
|
|
});
|
|
});
|
|
|
|
describe('EventView.clone()', function () {
|
|
it('returns a unique instance', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
interval: '5m',
|
|
display: 'releases',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
|
|
const eventView = new EventView(state);
|
|
|
|
const eventView2 = eventView.clone();
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
|
|
expect(eventView).toMatchObject(state);
|
|
expect(eventView2).toMatchObject(state);
|
|
expect(eventView.isEqualTo(eventView2)).toBe(true);
|
|
expect(
|
|
eventView.additionalConditions === eventView2.additionalConditions
|
|
).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
describe('EventView.withColumns()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [
|
|
{field: 'count()', width: 30},
|
|
{field: 'project.id', width: 99},
|
|
{field: 'failure_count()', width: 30},
|
|
],
|
|
yAxis: 'failure_count()',
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
};
|
|
const eventView = new EventView(state);
|
|
|
|
it('adds new columns, and replaces existing ones', function () {
|
|
const newView = eventView.withColumns([
|
|
{kind: 'field', field: 'title'},
|
|
{kind: 'function', function: [AggregationKey.COUNT, '', undefined, undefined]},
|
|
{kind: 'field', field: 'project.id'},
|
|
{kind: 'field', field: 'culprit'},
|
|
]);
|
|
// Views should be different.
|
|
expect(newView.isEqualTo(eventView)).toBe(false);
|
|
expect(newView.fields).toEqual([
|
|
{field: 'title', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'project.id', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'culprit', width: COL_WIDTH_UNDEFINED},
|
|
]);
|
|
});
|
|
|
|
it('drops empty columns', function () {
|
|
const newView = eventView.withColumns([
|
|
{kind: 'field', field: 'issue'},
|
|
{kind: 'function', function: [AggregationKey.COUNT, '', undefined, undefined]},
|
|
{kind: 'field', field: ''},
|
|
{kind: 'function', function: ['', '', undefined, undefined]},
|
|
{kind: 'function', function: ['', '', undefined, undefined]},
|
|
]);
|
|
expect(newView.fields).toEqual([
|
|
{field: 'issue', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
]);
|
|
});
|
|
|
|
it('inherits widths from existing columns when names match', function () {
|
|
const newView = eventView.withColumns([
|
|
{kind: 'function', function: [AggregationKey.COUNT, '', undefined, undefined]},
|
|
{kind: 'field', field: 'project.id'},
|
|
{kind: 'field', field: 'title'},
|
|
{kind: 'field', field: 'time'},
|
|
]);
|
|
|
|
expect(newView.fields).toEqual([
|
|
{field: 'count()', width: 30},
|
|
{field: 'project.id', width: 99},
|
|
{field: 'title', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'time', width: COL_WIDTH_UNDEFINED},
|
|
]);
|
|
});
|
|
|
|
it('retains sorts when sorted field is included', function () {
|
|
const newView = eventView.withColumns([
|
|
{kind: 'field', field: 'title'},
|
|
{kind: 'function', function: [AggregationKey.COUNT, '', undefined, undefined]},
|
|
]);
|
|
expect(newView.fields).toEqual([
|
|
{field: 'title', width: COL_WIDTH_UNDEFINED},
|
|
{field: 'count()', width: COL_WIDTH_UNDEFINED},
|
|
]);
|
|
expect(newView.sorts).toEqual([{field: AggregationKey.COUNT, kind: 'desc'}]);
|
|
});
|
|
|
|
it('updates sorts when sorted field is removed', function () {
|
|
const newView = eventView.withColumns([{kind: 'field', field: 'title'}]);
|
|
expect(newView.fields).toEqual([{field: 'title', width: COL_WIDTH_UNDEFINED}]);
|
|
// Should pick a sortable field.
|
|
expect(newView.sorts).toEqual([{field: 'title', kind: 'desc'}]);
|
|
});
|
|
|
|
it('has no sort if no sortable fields remain', function () {
|
|
const newView = eventView.withColumns([{kind: 'field', field: 'issue'}]);
|
|
expect(newView.fields).toEqual([{field: 'issue', width: COL_WIDTH_UNDEFINED}]);
|
|
expect(newView.sorts).toEqual([]);
|
|
});
|
|
it('updates yAxis if column is dropped', function () {
|
|
const newView = eventView.withColumns([
|
|
{kind: 'field', field: 'count()'},
|
|
{kind: 'field', field: 'project.id'},
|
|
]);
|
|
|
|
expect(newView.fields).toEqual([
|
|
{field: 'count()', width: 30},
|
|
{field: 'project.id', width: 99},
|
|
]);
|
|
|
|
expect(eventView.yAxis).toEqual('failure_count()');
|
|
expect(newView.yAxis).toEqual('count()');
|
|
});
|
|
});
|
|
|
|
describe('EventView.withNewColumn()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [
|
|
{field: 'count()', width: 30},
|
|
{field: 'project.id', width: 99},
|
|
],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
};
|
|
|
|
it('adds a field', function () {
|
|
const eventView = new EventView(state);
|
|
const newColumn: Column = {
|
|
kind: 'field',
|
|
field: 'title',
|
|
};
|
|
const eventView2 = eventView.withNewColumn(newColumn);
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const nextState = {
|
|
...state,
|
|
fields: [...state.fields, {field: 'title'}],
|
|
};
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('adds an aggregate function with no arguments', function () {
|
|
const eventView = new EventView(state);
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.COUNT, '', undefined, undefined],
|
|
};
|
|
|
|
const eventView2 = eventView.withNewColumn(newColumn);
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const nextState = {
|
|
...state,
|
|
fields: [...state.fields, {field: 'count()'}],
|
|
};
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('add an aggregate function with field', function () {
|
|
const eventView = new EventView(state);
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.AVG, 'transaction.duration', undefined, undefined],
|
|
};
|
|
const eventView2 = eventView.withNewColumn(newColumn);
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const nextState = {
|
|
...state,
|
|
fields: [...state.fields, {field: 'avg(transaction.duration)'}],
|
|
};
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('add an aggregate function with field & refinement', function () {
|
|
const eventView = new EventView(state);
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.PERCENTILE, 'transaction.duration', '0.5', undefined],
|
|
};
|
|
const updated = eventView.withNewColumn(newColumn);
|
|
expect(updated.fields).toEqual([
|
|
...state.fields,
|
|
{field: 'percentile(transaction.duration,0.5)', width: COL_WIDTH_UNDEFINED},
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('EventView.withResizedColumn()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
};
|
|
const view = new EventView(state);
|
|
|
|
it('updates a column that exists', function () {
|
|
const newView = view.withResizedColumn(0, 99);
|
|
expect(view.fields[0].width).toBeUndefined();
|
|
expect(newView.fields[0].width).toEqual(99);
|
|
});
|
|
|
|
it('ignores columns that do not exist', function () {
|
|
const newView = view.withResizedColumn(100, 99);
|
|
expect(view.fields).toEqual(newView.fields);
|
|
});
|
|
});
|
|
|
|
describe('EventView.withUpdatedColumn()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
};
|
|
|
|
const meta: MetaType = {
|
|
count: 'integer',
|
|
title: 'string',
|
|
};
|
|
|
|
it('update a column with no changes', function () {
|
|
const eventView = new EventView(state);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.COUNT, '', undefined, undefined],
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(0, newColumn, meta);
|
|
|
|
expect(eventView2 === eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
});
|
|
|
|
it('update a column to a field', function () {
|
|
const eventView = new EventView(state);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'field',
|
|
field: 'title',
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(1, newColumn, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const nextState = {
|
|
...state,
|
|
fields: [state.fields[0], {field: 'title'}],
|
|
};
|
|
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('update a column to an aggregate function with no arguments', function () {
|
|
const eventView = new EventView(state);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.COUNT, '', undefined, undefined],
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(1, newColumn, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const nextState = {
|
|
...state,
|
|
fields: [state.fields[0], {field: 'count()'}],
|
|
};
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('update a column to an aggregate function with field', function () {
|
|
const eventView = new EventView(state);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.AVG, 'transaction.duration', undefined, undefined],
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(1, newColumn, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const nextState = {
|
|
...state,
|
|
fields: [state.fields[0], {field: 'avg(transaction.duration)'}],
|
|
};
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('update a column to an aggregate function with field & refinement', function () {
|
|
const eventView = new EventView(state);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.PERCENTILE, 'transaction.duration', '0.5', undefined],
|
|
};
|
|
|
|
const newView = eventView.withUpdatedColumn(1, newColumn, meta);
|
|
expect(newView.fields).toEqual([
|
|
state.fields[0],
|
|
{field: 'percentile(transaction.duration,0.5)', width: COL_WIDTH_UNDEFINED},
|
|
]);
|
|
});
|
|
|
|
describe('update a column that is sorted', function () {
|
|
it('the sorted column is the only sorted column', function () {
|
|
const eventView = new EventView(state);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'field',
|
|
field: 'title',
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(0, newColumn, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const nextState = {
|
|
...state,
|
|
sorts: [{field: 'title', kind: 'desc'}],
|
|
fields: [{field: 'title'}, state.fields[1]],
|
|
};
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('the sorted column occurs at least twice', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [...state.fields, {field: 'count()'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'field',
|
|
field: 'title',
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(0, newColumn, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
|
|
const nextState = {
|
|
...state,
|
|
fields: [{field: 'title'}, state.fields[1], {field: 'count()'}],
|
|
};
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('using no provided table meta', function () {
|
|
// table meta may not be provided in the invalid query state;
|
|
// we will still want to be able to update columns
|
|
|
|
const eventView = new EventView(state);
|
|
|
|
const expected = {
|
|
...state,
|
|
sorts: [{field: 'title', kind: 'desc'}],
|
|
fields: [{field: 'title'}, state.fields[1]],
|
|
};
|
|
|
|
const newColumn: Column = {
|
|
kind: 'field',
|
|
field: 'title',
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(0, newColumn, {});
|
|
expect(eventView2).toMatchObject(expected);
|
|
|
|
const eventView3 = eventView.withUpdatedColumn(0, newColumn, undefined);
|
|
expect(eventView3).toMatchObject(expected);
|
|
});
|
|
});
|
|
|
|
describe('update a column to a non-sortable column', function () {
|
|
it('default to a sortable column', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [{field: 'count()'}, {field: 'title'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
|
|
// this column is expected to be non-sortable
|
|
const newColumn: Column = {
|
|
kind: 'field',
|
|
field: 'project.id',
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(0, newColumn, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
|
|
const nextState = {
|
|
...state,
|
|
sorts: [{field: 'title', kind: 'desc'}],
|
|
fields: [{field: 'project.id'}, {field: 'title'}],
|
|
};
|
|
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('has no sort if there are no sortable columns', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [{field: 'count()'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
|
|
// this column is expected to be non-sortable
|
|
const newColumn: Column = {
|
|
kind: 'field',
|
|
field: 'project.id',
|
|
};
|
|
|
|
const eventView2 = eventView.withUpdatedColumn(0, newColumn, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
|
|
const nextState = {
|
|
...state,
|
|
sorts: [],
|
|
fields: [{field: 'project.id'}],
|
|
};
|
|
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.withDeletedColumn()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
};
|
|
|
|
const meta: MetaType = {
|
|
count: 'integer',
|
|
title: 'string',
|
|
};
|
|
|
|
it('returns itself when attempting to delete the last remaining column', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [{field: 'count()'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
|
|
const eventView2 = eventView.withDeletedColumn(0, meta);
|
|
|
|
expect(eventView2 === eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
});
|
|
|
|
describe('deletes column, and use any remaining sortable column', function () {
|
|
it('using no provided table meta', function () {
|
|
// table meta may not be provided in the invalid query state;
|
|
// we will still want to be able to delete columns
|
|
|
|
const state2 = {
|
|
...state,
|
|
fields: [{field: 'title'}, {field: 'timestamp'}, {field: 'count()'}],
|
|
sorts: generateSorts(['timestamp']),
|
|
};
|
|
|
|
const eventView = new EventView(state2);
|
|
|
|
const expected = {
|
|
...state,
|
|
sorts: generateSorts(['title']),
|
|
fields: [{field: 'title'}, {field: 'count()'}],
|
|
};
|
|
|
|
const eventView2 = eventView.withDeletedColumn(1, {});
|
|
expect(eventView2).toMatchObject(expected);
|
|
|
|
const eventView3 = eventView.withDeletedColumn(1, undefined);
|
|
expect(eventView3).toMatchObject(expected);
|
|
});
|
|
|
|
it('has no remaining sortable column', function () {
|
|
const eventView = new EventView(state);
|
|
|
|
const eventView2 = eventView.withDeletedColumn(0, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const nextState = {
|
|
...state,
|
|
// we expect sorts to be empty since project.id is non-sortable
|
|
sorts: [],
|
|
fields: [state.fields[1]],
|
|
};
|
|
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('has a remaining sortable column', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [{field: 'count()'}, {field: 'project.id'}, {field: 'title'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
|
|
const eventView2 = eventView.withDeletedColumn(0, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
|
|
const nextState = {
|
|
...state,
|
|
sorts: [{field: 'title', kind: 'desc'}],
|
|
fields: [{field: 'project.id'}, {field: 'title'}],
|
|
};
|
|
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('sorted column occurs at least twice', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [...state.fields, state.fields[0]],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
|
|
const eventView2 = eventView.withDeletedColumn(0, meta);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
|
|
const nextState = {
|
|
...state,
|
|
fields: [state.fields[1], state.fields[0]],
|
|
};
|
|
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('ensures there is at one auto-width column on deletion', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [
|
|
{field: 'id', width: 75},
|
|
{field: 'title', width: 100},
|
|
{field: 'project', width: 80},
|
|
{field: 'environment', width: 99},
|
|
],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
let updated = eventView.withDeletedColumn(0, meta);
|
|
let updatedFields = [
|
|
{field: 'title', width: -1},
|
|
{field: 'project', width: 80},
|
|
{field: 'environment', width: 99},
|
|
];
|
|
expect(updated.fields).toEqual(updatedFields);
|
|
|
|
updated = updated.withDeletedColumn(0, meta);
|
|
updatedFields = [
|
|
{field: 'project', width: -1},
|
|
{field: 'environment', width: 99},
|
|
];
|
|
expect(updated.fields).toEqual(updatedFields);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.getSorts()', function () {
|
|
it('returns fields', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
project: [],
|
|
});
|
|
|
|
expect(eventView.getSorts()).toEqual([
|
|
{
|
|
key: AggregationKey.COUNT,
|
|
order: 'desc',
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getQuery()', function () {
|
|
it('with query', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
project: [],
|
|
query: 'event.type:error',
|
|
});
|
|
|
|
expect(eventView.getQuery()).toEqual('event.type:error');
|
|
expect(eventView.getQuery(null)).toEqual('event.type:error');
|
|
expect(eventView.getQuery('hello')).toEqual('event.type:error hello');
|
|
expect(eventView.getQuery(['event.type:error', 'hello'])).toEqual(
|
|
'event.type:error hello'
|
|
);
|
|
});
|
|
|
|
it('without query', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
project: [],
|
|
});
|
|
|
|
expect(eventView.getQuery()).toEqual('');
|
|
expect(eventView.getQuery(null)).toEqual('');
|
|
expect(eventView.getQuery('hello')).toEqual('hello');
|
|
expect(eventView.getQuery(['event.type:error', 'hello'])).toEqual(
|
|
'event.type:error hello'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getQueryWithAdditionalConditions', function () {
|
|
it('with overlapping conditions', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
project: [],
|
|
query: 'event.type:transaction foo:bar',
|
|
});
|
|
|
|
eventView.additionalConditions.setFilterValues('event.type', ['transaction']);
|
|
|
|
expect(eventView.getQueryWithAdditionalConditions()).toEqual(
|
|
'event.type:transaction foo:bar'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('EventView.sortForField()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
};
|
|
const eventView = new EventView(state);
|
|
const meta: MetaType = {count: 'integer'};
|
|
|
|
it('returns the sort when selected field is sorted', function () {
|
|
const field = {
|
|
field: 'count()',
|
|
};
|
|
|
|
const actual = eventView.sortForField(field, meta);
|
|
|
|
expect(actual).toEqual({
|
|
field: AggregationKey.COUNT,
|
|
kind: 'desc',
|
|
});
|
|
});
|
|
|
|
it('returns undefined when selected field is not sorted', function () {
|
|
const field = {
|
|
field: 'project.id',
|
|
};
|
|
|
|
expect(eventView.sortForField(field, meta)).toBeUndefined();
|
|
});
|
|
|
|
it('returns undefined when no meta is provided', function () {
|
|
const field = {
|
|
field: 'project.id',
|
|
};
|
|
|
|
expect(eventView.sortForField(field, undefined)).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('EventView.sortOnField()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
};
|
|
|
|
const meta: MetaType = {count: 'integer', title: 'string'};
|
|
|
|
it('returns itself when attempting to sort on un-sortable field', function () {
|
|
const eventView = new EventView(state);
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const field = state.fields[1];
|
|
|
|
const eventView2 = eventView.sortOnField(field, meta);
|
|
expect(eventView2 === eventView).toBe(true);
|
|
});
|
|
|
|
it('reverses the sorted field', function () {
|
|
const eventView = new EventView(state);
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const field = state.fields[0];
|
|
|
|
const eventView2 = eventView.sortOnField(field, meta);
|
|
|
|
expect(eventView2 !== eventView).toBe(true);
|
|
|
|
const nextState = {
|
|
...state,
|
|
sorts: [{field: AggregationKey.COUNT, kind: 'asc'}],
|
|
};
|
|
|
|
expect(eventView2).toMatchObject(nextState);
|
|
});
|
|
|
|
it('enforce sort order on sorted field', function () {
|
|
const eventView = new EventView(state);
|
|
expect(eventView).toMatchObject(state);
|
|
|
|
const field = state.fields[0];
|
|
|
|
const eventView2 = eventView.sortOnField(field, meta, 'asc');
|
|
expect(eventView2).toMatchObject({
|
|
...state,
|
|
sorts: [{field: AggregationKey.COUNT, kind: 'asc'}],
|
|
});
|
|
|
|
const eventView3 = eventView.sortOnField(field, meta, 'desc');
|
|
expect(eventView3).toMatchObject({
|
|
...state,
|
|
sorts: [{field: AggregationKey.COUNT, kind: 'desc'}],
|
|
});
|
|
});
|
|
|
|
it('supports function format on equation sorts', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [{field: 'count()'}, {field: 'equation|count() + 100'}],
|
|
sorts: [{field: 'equation|count() + 100', kind: 'desc'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
});
|
|
|
|
it('supports index format on equation sorts', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [{field: 'count()'}, {field: 'equation|count() + 100'}],
|
|
sorts: [{field: 'equation[0]', kind: 'desc'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
});
|
|
|
|
it('sort on new field', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [...state.fields, {field: 'title'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
|
|
const field = modifiedState.fields[2];
|
|
|
|
const eventView2 = eventView.sortOnField(field, meta);
|
|
|
|
expect(eventView2 !== eventView).toBe(true);
|
|
|
|
const nextState = {
|
|
...modifiedState,
|
|
sorts: [{field: 'title', kind: 'desc'}],
|
|
};
|
|
|
|
expect(eventView2).toMatchObject(nextState);
|
|
|
|
// enforce asc sort order
|
|
|
|
const eventView3 = eventView.sortOnField(field, meta, 'asc');
|
|
|
|
expect(eventView3).toMatchObject({
|
|
...modifiedState,
|
|
sorts: [{field: 'title', kind: 'asc'}],
|
|
});
|
|
|
|
// enforce desc sort order
|
|
|
|
const eventView4 = eventView.sortOnField(field, meta, 'desc');
|
|
|
|
expect(eventView4).toMatchObject({
|
|
...modifiedState,
|
|
sorts: [{field: 'title', kind: 'desc'}],
|
|
});
|
|
});
|
|
|
|
it('sorts on a field using function format', function () {
|
|
const modifiedState: ConstructorParameters<typeof EventView>[0] = {
|
|
...state,
|
|
fields: [...state.fields, {field: 'count()'}],
|
|
};
|
|
|
|
const eventView = new EventView(modifiedState);
|
|
expect(eventView).toMatchObject(modifiedState);
|
|
|
|
const field = modifiedState.fields[2];
|
|
|
|
let sortedEventView = eventView.sortOnField(field, meta, undefined, true);
|
|
expect(sortedEventView.sorts).toEqual([{field: 'count()', kind: 'asc'}]);
|
|
sortedEventView = sortedEventView.sortOnField(field, meta, undefined, true);
|
|
expect(sortedEventView.sorts).toEqual([{field: 'count()', kind: 'desc'}]);
|
|
});
|
|
});
|
|
|
|
describe('EventView.withSorts()', function () {
|
|
it('returns a clone', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'event.type'}],
|
|
});
|
|
const updated = eventView.withSorts([{kind: 'desc', field: 'event.type'}]);
|
|
expect(updated.sorts).not.toEqual(eventView.sorts);
|
|
});
|
|
|
|
it('only accepts sorting on fields in the view', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'event.type'}],
|
|
});
|
|
const updated = eventView.withSorts([
|
|
{kind: 'desc', field: 'event.type'},
|
|
{kind: 'asc', field: 'unknown'},
|
|
]);
|
|
expect(updated.sorts).toEqual([{kind: 'desc', field: 'event.type'}]);
|
|
});
|
|
|
|
it('accepts aggregate field sorts', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'p50()'}],
|
|
});
|
|
const updated = eventView.withSorts([
|
|
{kind: 'desc', field: 'p50'},
|
|
{kind: 'asc', field: 'unknown'},
|
|
]);
|
|
expect(updated.sorts).toEqual([{kind: 'desc', field: 'p50'}]);
|
|
});
|
|
});
|
|
|
|
describe('EventView.isEqualTo()', function () {
|
|
it('should be true when equal', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'fam',
|
|
display: 'releases',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
|
|
const eventView = new EventView(state);
|
|
const eventView2 = new EventView(state);
|
|
|
|
expect(eventView2 !== eventView).toBeTruthy();
|
|
|
|
expect(eventView).toMatchObject(state);
|
|
expect(eventView2).toMatchObject(state);
|
|
expect(eventView.isEqualTo(eventView2)).toBe(true);
|
|
|
|
// commutativity property holds
|
|
expect(eventView2.isEqualTo(eventView)).toBe(true);
|
|
});
|
|
|
|
it('should be true when datetime are equal but differ in format', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-20T21:02:51+0000',
|
|
end: '2019-10-23T19:27:04+0000',
|
|
environment: ['staging'],
|
|
};
|
|
|
|
const eventView = new EventView(state);
|
|
const eventView2 = new EventView({
|
|
...state,
|
|
start: '2019-10-20T21:02:51Z',
|
|
end: '2019-10-23T19:27:04Z',
|
|
});
|
|
|
|
expect(eventView.isEqualTo(eventView2)).toBe(true);
|
|
});
|
|
|
|
it('should be false when not equal', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'fam',
|
|
display: 'releases',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
|
|
const differences = {
|
|
id: '12',
|
|
name: 'new query',
|
|
fields: [{field: 'project.id'}, {field: 'count()'}],
|
|
sorts: [{field: AggregationKey.COUNT, kind: 'asc'}],
|
|
query: 'event.type:transaction',
|
|
project: [24],
|
|
start: '2019-09-01T00:00:00',
|
|
end: '2020-09-01T00:00:00',
|
|
statsPeriod: '24d',
|
|
environment: [],
|
|
yAxis: 'ok boomer',
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.ISSUE_PLATFORM,
|
|
};
|
|
const eventView = new EventView(state);
|
|
|
|
for (const key in differences) {
|
|
const eventView2 = new EventView({...state, [key]: differences[key]});
|
|
expect(eventView.isEqualTo(eventView2)).toBe(false);
|
|
}
|
|
});
|
|
|
|
it('undefined display type equals default display type', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
yAxis: 'fam',
|
|
};
|
|
const eventView = new EventView(state);
|
|
const eventView2 = new EventView({...state, display: 'default'});
|
|
expect(eventView.isEqualTo(eventView2)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getResultsViewUrlTarget()', function () {
|
|
beforeEach(function () {
|
|
window.__initialData = {
|
|
...window.__initialData,
|
|
customerDomain: {
|
|
subdomain: 'albertos-apples',
|
|
organizationUrl: 'https://albertos-apples.sentry.io',
|
|
sentryUrl: 'https://sentry.io',
|
|
},
|
|
};
|
|
});
|
|
|
|
afterEach(function () {
|
|
window.__initialData = {
|
|
...window.__initialData,
|
|
customerDomain: null,
|
|
};
|
|
});
|
|
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
const organization = OrganizationFixture();
|
|
|
|
it('generates a URL with non-customer domain context', function () {
|
|
window.__initialData.customerDomain = null;
|
|
const view = new EventView(state);
|
|
const result = view.getResultsViewUrlTarget(organization.slug);
|
|
expect(result.pathname).toEqual('/organizations/org-slug/discover/results/');
|
|
expect(result.query.query).toEqual(state.query);
|
|
expect(result.query.project).toEqual(state.project);
|
|
expect(result.query.display).toEqual(state.display);
|
|
});
|
|
|
|
it('generates a URL with customer domain context', function () {
|
|
const view = new EventView(state);
|
|
const result = view.getResultsViewUrlTarget(organization.slug);
|
|
expect(result.pathname).toEqual('/discover/results/');
|
|
expect(result.query.query).toEqual(state.query);
|
|
expect(result.query.project).toEqual(state.project);
|
|
expect(result.query.display).toEqual(state.display);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getResultsViewShortUrlTarget()', function () {
|
|
beforeEach(function () {
|
|
window.__initialData = {
|
|
...window.__initialData,
|
|
customerDomain: {
|
|
subdomain: 'albertos-apples',
|
|
organizationUrl: 'https://albertos-apples.sentry.io',
|
|
sentryUrl: 'https://sentry.io',
|
|
},
|
|
};
|
|
});
|
|
|
|
afterEach(function () {
|
|
window.__initialData = {
|
|
...window.__initialData,
|
|
customerDomain: null,
|
|
};
|
|
});
|
|
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
const organization = OrganizationFixture();
|
|
|
|
it('generates a URL with non-customer domain context', function () {
|
|
window.__initialData.customerDomain = null;
|
|
const view = new EventView(state);
|
|
const result = view.getResultsViewShortUrlTarget(organization.slug);
|
|
expect(result.pathname).toEqual('/organizations/org-slug/discover/results/');
|
|
expect(result.query).not.toHaveProperty('name');
|
|
expect(result.query).not.toHaveProperty('fields');
|
|
expect(result.query).not.toHaveProperty('query');
|
|
expect(result.query.id).toEqual(state.id);
|
|
expect(result.query.statsPeriod).toEqual(state.statsPeriod);
|
|
expect(result.query.project).toEqual(state.project);
|
|
expect(result.query.environment).toEqual(state.environment);
|
|
});
|
|
|
|
it('generates a URL with customer domain context', function () {
|
|
const view = new EventView(state);
|
|
const result = view.getResultsViewShortUrlTarget(organization.slug);
|
|
expect(result.pathname).toEqual('/discover/results/');
|
|
expect(result.query).not.toHaveProperty('name');
|
|
expect(result.query).not.toHaveProperty('fields');
|
|
expect(result.query).not.toHaveProperty('query');
|
|
expect(result.query.id).toEqual(state.id);
|
|
expect(result.query.statsPeriod).toEqual(state.statsPeriod);
|
|
expect(result.query.project).toEqual(state.project);
|
|
expect(result.query.environment).toEqual(state.environment);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getPerformanceTransactionEventsViewUrlTarget()', function () {
|
|
beforeEach(function () {
|
|
window.__initialData = {
|
|
...window.__initialData,
|
|
customerDomain: {
|
|
subdomain: 'albertos-apples',
|
|
organizationUrl: 'https://albertos-apples.sentry.io',
|
|
sentryUrl: 'https://sentry.io',
|
|
},
|
|
};
|
|
});
|
|
|
|
afterEach(function () {
|
|
window.__initialData = {
|
|
...window.__initialData,
|
|
customerDomain: null,
|
|
};
|
|
});
|
|
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
display: 'previous',
|
|
dataset: DiscoverDatasets.DISCOVER,
|
|
};
|
|
const organization = OrganizationFixture();
|
|
const showTransactions = EventsDisplayFilterName.P99;
|
|
const breakdown = SpanOperationBreakdownFilter.HTTP;
|
|
const webVital = WebVital.LCP;
|
|
|
|
it('generates a URL with non-customer domain context', function () {
|
|
window.__initialData.customerDomain = null;
|
|
const view = new EventView(state);
|
|
const result = view.getPerformanceTransactionEventsViewUrlTarget(organization.slug, {
|
|
showTransactions,
|
|
breakdown,
|
|
webVital,
|
|
});
|
|
expect(result.pathname).toEqual(
|
|
'/organizations/org-slug/performance/summary/events/'
|
|
);
|
|
expect(result.query.query).toEqual(state.query);
|
|
expect(result.query.project).toEqual(state.project);
|
|
expect(result.query.sort).toEqual(['-count']);
|
|
expect(result.query.transaction).toEqual(state.name);
|
|
expect(result.query.showTransactions).toEqual(showTransactions);
|
|
expect(result.query.breakdown).toEqual(breakdown);
|
|
expect(result.query.webVital).toEqual(webVital);
|
|
});
|
|
|
|
it('generates a URL with customer domain context', function () {
|
|
const view = new EventView(state);
|
|
const result = view.getPerformanceTransactionEventsViewUrlTarget(organization.slug, {
|
|
showTransactions,
|
|
breakdown,
|
|
webVital,
|
|
});
|
|
expect(result.pathname).toEqual('/performance/summary/events/');
|
|
expect(result.query.query).toEqual(state.query);
|
|
expect(result.query.project).toEqual(state.project);
|
|
expect(result.query.sort).toEqual(['-count']);
|
|
expect(result.query.transaction).toEqual(state.name);
|
|
expect(result.query.showTransactions).toEqual(showTransactions);
|
|
expect(result.query.breakdown).toEqual(breakdown);
|
|
expect(result.query.webVital).toEqual(webVital);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getPageFilters()', function () {
|
|
it('return default global selection', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
});
|
|
|
|
expect(eventView.getPageFilters()).toMatchObject({
|
|
projects: [],
|
|
environments: [],
|
|
datetime: {
|
|
start: null,
|
|
end: null,
|
|
period: null,
|
|
|
|
// event views currently do not support the utc option,
|
|
// see comment in EventView.getPageFilters
|
|
utc: true,
|
|
},
|
|
});
|
|
});
|
|
|
|
it('returns global selection query', function () {
|
|
const state2 = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
project: [42],
|
|
start: 'start',
|
|
end: 'end',
|
|
statsPeriod: '42d',
|
|
environment: ['prod'],
|
|
};
|
|
|
|
const eventView = new EventView(state2);
|
|
|
|
expect(eventView.getPageFilters()).toMatchObject({
|
|
projects: state2.project,
|
|
environments: state2.environment,
|
|
datetime: {
|
|
start: state2.start,
|
|
end: state2.end,
|
|
period: state2.statsPeriod,
|
|
|
|
// event views currently do not support the utc option,
|
|
// see comment in EventView.getPageFilters
|
|
utc: true,
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.getPageFiltersQuery()', function () {
|
|
it('return default global selection query', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
});
|
|
|
|
expect(eventView.getPageFiltersQuery()).toMatchObject({
|
|
project: [],
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: undefined,
|
|
environment: [],
|
|
|
|
// event views currently do not support the utc option,
|
|
// see comment in EventView.getPageFilters
|
|
utc: 'true',
|
|
});
|
|
});
|
|
|
|
it('returns global selection query', function () {
|
|
const state2 = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
project: [42],
|
|
start: 'start',
|
|
end: 'end',
|
|
statsPeriod: '42d',
|
|
environment: ['prod'],
|
|
};
|
|
|
|
const eventView = new EventView(state2);
|
|
|
|
expect(eventView.getPageFiltersQuery()).toEqual({
|
|
end: 'end',
|
|
start: 'start',
|
|
statsPeriod: '42d',
|
|
environment: ['prod'],
|
|
project: ['42'],
|
|
utc: 'true',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.generateBlankQueryStringObject()', function () {
|
|
it('should return blank values', function () {
|
|
const eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
});
|
|
|
|
expect(eventView.generateBlankQueryStringObject()).toEqual({
|
|
id: undefined,
|
|
name: undefined,
|
|
fields: undefined,
|
|
sorts: undefined,
|
|
query: undefined,
|
|
project: undefined,
|
|
start: undefined,
|
|
end: undefined,
|
|
statsPeriod: undefined,
|
|
environment: undefined,
|
|
yAxis: undefined,
|
|
cursor: undefined,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('EventView.getYAxisOptions()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
statsPeriod: '42d',
|
|
environment: [],
|
|
};
|
|
|
|
function generateYaxis(value) {
|
|
return {
|
|
value,
|
|
label: value,
|
|
};
|
|
}
|
|
|
|
it('should return default options', function () {
|
|
const thisEventView = new EventView(state);
|
|
|
|
expect(thisEventView.getYAxisOptions()).toEqual(CHART_AXIS_OPTIONS);
|
|
});
|
|
|
|
it('should add aggregate fields as options', function () {
|
|
let thisEventView = new EventView({
|
|
...state,
|
|
fields: generateFields(['ignored-field', 'count_unique(issue)']),
|
|
});
|
|
|
|
expect(thisEventView.getYAxisOptions()).toEqual([
|
|
generateYaxis('count_unique(issue)'),
|
|
...CHART_AXIS_OPTIONS,
|
|
]);
|
|
|
|
// should de-duplicate entries
|
|
thisEventView = new EventView({
|
|
...state,
|
|
fields: generateFields(['ignored-field', 'count()']),
|
|
});
|
|
|
|
expect(thisEventView.getYAxisOptions()).toEqual([...CHART_AXIS_OPTIONS]);
|
|
});
|
|
|
|
it('should exclude yAxis options that are not useful', function () {
|
|
const thisEventView = new EventView({
|
|
...state,
|
|
fields: generateFields([
|
|
'ignored-field',
|
|
'count_unique(issue)',
|
|
'last_seen()',
|
|
'max(timestamp)',
|
|
]),
|
|
});
|
|
|
|
expect(thisEventView.getYAxisOptions()).toEqual([
|
|
generateYaxis('count_unique(issue)'),
|
|
...CHART_AXIS_OPTIONS,
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getYAxis()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
statsPeriod: '42d',
|
|
environment: [],
|
|
};
|
|
|
|
it('should return first default yAxis', function () {
|
|
const thisEventView = new EventView(state);
|
|
|
|
expect(thisEventView.getYAxis()).toEqual('count()');
|
|
});
|
|
|
|
it('should return valid yAxis', function () {
|
|
const thisEventView = new EventView({
|
|
...state,
|
|
fields: generateFields(['ignored-field', 'count_unique(user)', 'last_seen']),
|
|
yAxis: 'count_unique(user)',
|
|
});
|
|
|
|
expect(thisEventView.getYAxis()).toEqual('count_unique(user)');
|
|
});
|
|
|
|
it('should ignore invalid yAxis', function () {
|
|
const invalid = [
|
|
'last_seen',
|
|
'latest_event',
|
|
'count_unique(issue)', // this is not one of the selected fields
|
|
];
|
|
|
|
for (const option of invalid) {
|
|
const thisEventView = new EventView({
|
|
...state,
|
|
fields: generateFields(['ignored-field', 'last_seen', 'latest_event']),
|
|
yAxis: option,
|
|
});
|
|
|
|
// yAxis defaults to the first entry of the default yAxis options
|
|
expect(thisEventView.getYAxis()).toEqual('count()');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('EventView.getDisplayOptions()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
statsPeriod: '42d',
|
|
environment: [],
|
|
};
|
|
|
|
it('should return default options', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
// there needs to exist an aggregate or TOP 5 modes will be disabled
|
|
fields: [{field: 'count()'}],
|
|
});
|
|
|
|
expect(eventView.getDisplayOptions()).toEqual(DISPLAY_MODE_OPTIONS);
|
|
});
|
|
|
|
it('should disable previous when start/end are used.', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
end: '2020-04-13T12:13:14',
|
|
start: '2020-04-01T12:13:14',
|
|
});
|
|
|
|
const options = eventView.getDisplayOptions();
|
|
expect(options[1].value).toEqual('previous');
|
|
expect(options[1].disabled).toBeTruthy();
|
|
});
|
|
|
|
it('should disable top 5 period/daily if no aggregates present', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
});
|
|
|
|
const options = eventView.getDisplayOptions();
|
|
expect(options[2].value).toEqual('top5');
|
|
expect(options[2].disabled).toBeTruthy();
|
|
expect(options[4].value).toEqual('dailytop5');
|
|
expect(options[4].disabled).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('EventView.getDisplayMode()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
statsPeriod: '42d',
|
|
environment: [],
|
|
};
|
|
|
|
it('should have default', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
});
|
|
const displayMode = eventView.getDisplayMode();
|
|
expect(displayMode).toEqual(DisplayModes.DEFAULT);
|
|
});
|
|
|
|
it('should return current mode when not disabled', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
display: DisplayModes.DAILY,
|
|
});
|
|
const displayMode = eventView.getDisplayMode();
|
|
expect(displayMode).toEqual(DisplayModes.DAILY);
|
|
});
|
|
|
|
it('should return default mode when disabled', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
// the existence of start and end will disable the PREVIOUS mode
|
|
end: '2020-04-13T12:13:14',
|
|
start: '2020-04-01T12:13:14',
|
|
display: DisplayModes.PREVIOUS,
|
|
});
|
|
const displayMode = eventView.getDisplayMode();
|
|
expect(displayMode).toEqual(DisplayModes.DEFAULT);
|
|
});
|
|
|
|
it('top 5 should fallback to default when disabled', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
// the lack of an aggregate will disable the TOP5 mode
|
|
display: DisplayModes.TOP5,
|
|
});
|
|
const displayMode = eventView.getDisplayMode();
|
|
expect(displayMode).toEqual(DisplayModes.DEFAULT);
|
|
});
|
|
|
|
it('top 5 daily should fallback to daily when disabled', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
// the lack of an aggregate will disable the DAILYTOP5 mode
|
|
display: DisplayModes.DAILYTOP5,
|
|
});
|
|
const displayMode = eventView.getDisplayMode();
|
|
expect(displayMode).toEqual(DisplayModes.DAILY);
|
|
});
|
|
|
|
it('daily mode should fall back to default when disabled', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
// the period being less than 24h will disable the DAILY mode
|
|
statsPeriod: '1h',
|
|
display: DisplayModes.DAILY,
|
|
});
|
|
const displayMode = eventView.getDisplayMode();
|
|
expect(displayMode).toEqual(DisplayModes.DEFAULT);
|
|
});
|
|
|
|
it('top 5 daily mode should fall back to default when daily is disabled', function () {
|
|
const eventView = new EventView({
|
|
...state,
|
|
// the period being less than 24h will disable the DAILY mode
|
|
statsPeriod: undefined,
|
|
start: '2020-04-01T12:13:14',
|
|
end: '2020-04-02T12:10:14',
|
|
display: DisplayModes.DAILYTOP5,
|
|
});
|
|
const displayMode = eventView.getDisplayMode();
|
|
expect(displayMode).toEqual(DisplayModes.DEFAULT);
|
|
});
|
|
});
|
|
|
|
describe('EventView.getAggregateFields()', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [
|
|
{field: 'title'},
|
|
{field: 'count()'},
|
|
{field: 'count_unique(user)'},
|
|
{field: 'apdex(300)'},
|
|
{field: 'transaction'},
|
|
],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
statsPeriod: '42d',
|
|
environment: [],
|
|
};
|
|
|
|
it('getAggregateFields() returns only aggregates', function () {
|
|
const eventView = new EventView(state);
|
|
const expected = [
|
|
{field: 'count()'},
|
|
{field: 'count_unique(user)'},
|
|
{field: 'apdex(300)'},
|
|
];
|
|
|
|
expect(eventView.getAggregateFields()).toEqual(expected);
|
|
});
|
|
});
|
|
|
|
describe('EventView.hasAggregateField', function () {
|
|
it('ensures an eventview has an aggregate field', function () {
|
|
let eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'foobar'}],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
environment: [],
|
|
});
|
|
|
|
expect(eventView.hasAggregateField()).toBe(false);
|
|
|
|
eventView = new EventView({
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
fields: [{field: 'count(foo.bar.is-Enterprise_42)'}],
|
|
sorts: [],
|
|
query: '',
|
|
project: [],
|
|
environment: [],
|
|
});
|
|
|
|
expect(eventView.hasAggregateField()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('isAPIPayloadSimilar', function () {
|
|
const state: ConstructorParameters<typeof EventView>[0] = {
|
|
...REQUIRED_CONSTRUCTOR_PROPS,
|
|
id: '1234',
|
|
name: 'best query',
|
|
fields: [{field: 'count()'}, {field: 'project.id'}],
|
|
sorts: generateSorts([AggregationKey.COUNT]),
|
|
query: 'event.type:error',
|
|
project: [42],
|
|
start: '2019-10-01T00:00:00',
|
|
end: '2019-10-02T00:00:00',
|
|
statsPeriod: '14d',
|
|
environment: ['staging'],
|
|
};
|
|
|
|
const meta: MetaType = {
|
|
count: 'integer',
|
|
title: 'string',
|
|
};
|
|
|
|
describe('getEventsAPIPayload', function () {
|
|
it('is not similar when relevant query string keys are present in the Location object', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({
|
|
query: {
|
|
project: 'project',
|
|
environment: 'environment',
|
|
start: 'start',
|
|
end: 'end',
|
|
utc: 'utc',
|
|
statsPeriod: 'statsPeriod',
|
|
cursor: 'cursor',
|
|
},
|
|
});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = thisEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(false);
|
|
});
|
|
|
|
it('is similar when irrelevant query string keys are present in the Location object', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({
|
|
query: {
|
|
bestCountry: 'canada',
|
|
},
|
|
});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = thisEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(true);
|
|
});
|
|
|
|
it('is not similar on sort key sorted in opposite directions', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const otherEventView = thisEventView.sortOnField({field: 'count()'}, meta);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(false);
|
|
});
|
|
|
|
it('is not similar when a new column is added', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const otherEventView = new EventView({
|
|
...state,
|
|
fields: [...state.fields, {field: 'title', width: COL_WIDTH_UNDEFINED}],
|
|
});
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(false);
|
|
});
|
|
|
|
it('is similar when a column is updated with no changes', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.COUNT, '', undefined, undefined],
|
|
};
|
|
|
|
const otherEventView = thisEventView.withUpdatedColumn(0, newColumn, meta);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(true);
|
|
});
|
|
|
|
it('is not similar when a column is updated with a replaced field', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'field',
|
|
field: 'title',
|
|
};
|
|
|
|
const otherEventView = thisEventView.withUpdatedColumn(0, newColumn, meta);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(false);
|
|
});
|
|
|
|
it('is not similar when a column is updated with a replaced aggregation', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.AVG, '', undefined, undefined],
|
|
};
|
|
|
|
const otherEventView = thisEventView.withUpdatedColumn(0, newColumn, meta);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(false);
|
|
});
|
|
|
|
it('is similar when a column is renamed', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.COUNT, '', undefined, undefined],
|
|
};
|
|
|
|
const otherEventView = thisEventView.withUpdatedColumn(0, newColumn, meta);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(true);
|
|
});
|
|
|
|
it('is not similar when a column is deleted', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const otherEventView = thisEventView.withDeletedColumn(0, meta);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(false);
|
|
});
|
|
|
|
it('is similar if column order changes', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
const otherEventView = new EventView({...state, fields: shuffle(state.fields)});
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(true);
|
|
});
|
|
|
|
it('is similar if equation order relatively same', function () {
|
|
const equationField = {field: 'equation|failure_count() / count()'};
|
|
const otherEquationField = {field: 'equation|failure_count() / 2'};
|
|
state.fields = [
|
|
{field: 'project.id'},
|
|
{field: 'count()'},
|
|
equationField,
|
|
otherEquationField,
|
|
];
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
state.fields = [
|
|
equationField,
|
|
{field: 'project.id'},
|
|
{field: 'count()'},
|
|
otherEquationField,
|
|
];
|
|
const otherEventView = new EventView(state);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(true);
|
|
});
|
|
|
|
it('is not similar if equation order changes', function () {
|
|
const equationField = {field: 'equation|failure_count() / count()'};
|
|
const otherEquationField = {field: 'equation|failure_count() / 2'};
|
|
state.fields = [
|
|
{field: 'project.id'},
|
|
{field: 'count()'},
|
|
equationField,
|
|
otherEquationField,
|
|
];
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getEventsAPIPayload(location);
|
|
|
|
state.fields = [
|
|
{field: 'project.id'},
|
|
{field: 'count()'},
|
|
otherEquationField,
|
|
equationField,
|
|
];
|
|
const otherEventView = new EventView(state);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getEventsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
|
|
expect(results).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('getFacetsAPIPayload', function () {
|
|
it('only includes relevant parameters', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const results = thisEventView.getFacetsAPIPayload(location);
|
|
const expected = {
|
|
query: state.query,
|
|
project: ['42'],
|
|
statsPeriod: state.statsPeriod,
|
|
environment: state.environment,
|
|
};
|
|
|
|
expect(results).toEqual(expected);
|
|
});
|
|
|
|
it('is similar on sort key sorted in opposite directions', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getFacetsAPIPayload(location);
|
|
|
|
const newColumn: Column = {
|
|
kind: 'function',
|
|
function: [AggregationKey.COUNT, '', undefined, undefined],
|
|
};
|
|
|
|
const otherEventView = thisEventView.withUpdatedColumn(0, newColumn, meta);
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getFacetsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
expect(results).toBe(true);
|
|
});
|
|
|
|
it('is similar when a columns are different', function () {
|
|
const thisEventView = new EventView(state);
|
|
const location = LocationFixture({});
|
|
const thisAPIPayload = thisEventView.getFacetsAPIPayload(location);
|
|
|
|
const otherEventView = new EventView({
|
|
...state,
|
|
fields: [...state.fields, {field: 'title', width: COL_WIDTH_UNDEFINED}],
|
|
});
|
|
const otherLocation = LocationFixture({});
|
|
const otherAPIPayload = otherEventView.getFacetsAPIPayload(otherLocation);
|
|
|
|
const results = isAPIPayloadSimilar(thisAPIPayload, otherAPIPayload);
|
|
expect(results).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('pickRelevantLocationQueryStrings', function () {
|
|
it('picks relevant query strings', function () {
|
|
const location = LocationFixture({
|
|
query: {
|
|
project: 'project',
|
|
environment: 'environment',
|
|
start: 'start',
|
|
end: 'end',
|
|
utc: 'utc',
|
|
statsPeriod: 'statsPeriod',
|
|
cursor: 'cursor',
|
|
|
|
// irrelevant query strings
|
|
bestCountry: 'canada',
|
|
},
|
|
});
|
|
|
|
const actual = pickRelevantLocationQueryStrings(location);
|
|
|
|
const expected = {
|
|
start: 'start',
|
|
end: 'end',
|
|
utc: 'utc',
|
|
statsPeriod: 'statsPeriod',
|
|
cursor: 'cursor',
|
|
};
|
|
|
|
expect(actual).toEqual(expected);
|
|
});
|
|
});
|