123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- import {initializeOrg} from 'sentry-test/initializeOrg';
- import {mountGlobalModal} from 'sentry-test/modal';
- import {mountWithTheme, screen, userEvent} from 'sentry-test/reactTestingLibrary';
- import * as modal from 'sentry/actionCreators/modal';
- import {Client} from 'sentry/api';
- import {DisplayType, Widget, WidgetType} from 'sentry/views/dashboardsV2/types';
- import WidgetCard from 'sentry/views/dashboardsV2/widgetCard';
- describe('Dashboards > WidgetCard', function () {
- const initialData = initializeOrg({
- organization: TestStubs.Organization({
- features: ['connect-discover-and-dashboards', 'dashboards-edit', 'discover-basic'],
- projects: [TestStubs.Project()],
- }),
- router: {orgId: 'orgId'},
- } as Parameters<typeof initializeOrg>[0]);
- const multipleQueryWidget: Widget = {
- title: 'Errors',
- interval: '5m',
- displayType: DisplayType.LINE,
- widgetType: WidgetType.DISCOVER,
- queries: [
- {
- conditions: 'event.type:error',
- fields: ['count()', 'failure_count()'],
- name: 'errors',
- orderby: '',
- },
- {
- conditions: 'event.type:default',
- fields: ['count()', 'failure_count()'],
- name: 'default',
- orderby: '',
- },
- ],
- };
- const selection = {
- projects: [1],
- environments: ['prod'],
- datetime: {
- period: '14d',
- start: null,
- end: null,
- utc: false,
- },
- };
- const api = new Client();
- let eventsMock;
- beforeEach(function () {
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-stats/',
- body: [],
- });
- MockApiClient.addMockResponse({
- url: '/organizations/org-slug/events-geo/',
- body: [],
- });
- eventsMock = MockApiClient.addMockResponse({
- url: '/organizations/org-slug/eventsv2/',
- body: {
- meta: {title: 'string'},
- data: [{title: 'title'}],
- },
- });
- });
- afterEach(function () {
- MockApiClient.clearMockResponses();
- });
- it('renders with Open in Discover button and opens the Query Selector Modal when clicked', async function () {
- const spy = jest.spyOn(modal, 'openDashboardWidgetQuerySelectorModal');
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={multipleQueryWidget}
- selection={selection}
- isEditing={false}
- onDelete={() => undefined}
- onEdit={() => undefined}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Open in Discover')).toBeInTheDocument();
- userEvent.click(screen.getByText('Open in Discover'));
- expect(spy).toHaveBeenCalledWith({
- organization: initialData.organization,
- widget: multipleQueryWidget,
- });
- });
- it('renders with Open in Discover button and opens in Discover when clicked', async function () {
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{...multipleQueryWidget, queries: [multipleQueryWidget.queries[0]]}}
- selection={selection}
- isEditing={false}
- onDelete={() => undefined}
- onEdit={() => undefined}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Open in Discover')).toBeInTheDocument();
- expect(screen.getByText('Open in Discover').closest('a')).toHaveAttribute(
- 'href',
- '/organizations/org-slug/discover/results/?environment=prod&field=count%28%29&field=failure_count%28%29&name=Errors&project=1&query=event.type%3Aerror&statsPeriod=14d&yAxis=count%28%29&yAxis=failure_count%28%29'
- );
- });
- it('Opens in Discover with World Map', async function () {
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- displayType: DisplayType.WORLD_MAP,
- queries: [{...multipleQueryWidget.queries[0], fields: ['count()']}],
- }}
- selection={selection}
- isEditing={false}
- onDelete={() => undefined}
- onEdit={() => undefined}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Open in Discover')).toBeInTheDocument();
- expect(screen.getByText('Open in Discover').closest('a')).toHaveAttribute(
- 'href',
- '/organizations/org-slug/discover/results/?display=worldmap&environment=prod&field=geo.country_code&field=count%28%29&name=Errors&project=1&query=event.type%3Aerror%20has%3Ageo.country_code&statsPeriod=14d&yAxis=count%28%29'
- );
- });
- it('Opens in Discover with prepended fields pulled from equations', async function () {
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- queries: [
- {
- ...multipleQueryWidget.queries[0],
- fields: [
- 'equation|(count() + failure_count()) / count_if(transaction.duration,equals,300)',
- ],
- },
- ],
- }}
- selection={selection}
- isEditing={false}
- onDelete={() => undefined}
- onEdit={() => undefined}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Open in Discover')).toBeInTheDocument();
- expect(screen.getByText('Open in Discover').closest('a')).toHaveAttribute(
- 'href',
- '/organizations/org-slug/discover/results/?environment=prod&field=count_if%28transaction.duration%2Cequals%2C300%29&field=failure_count%28%29&field=count%28%29&field=equation%7C%28count%28%29%20%2B%20failure_count%28%29%29%20%2F%20count_if%28transaction.duration%2Cequals%2C300%29&name=Errors&project=1&query=event.type%3Aerror&statsPeriod=14d&yAxis=equation%7C%28count%28%29%20%2B%20failure_count%28%29%29%20%2F%20count_if%28transaction.duration%2Cequals%2C300%29'
- );
- });
- it('Opens in Discover with Top N', async function () {
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- displayType: DisplayType.TOP_N,
- queries: [
- {...multipleQueryWidget.queries[0], fields: ['transaction', 'count()']},
- ],
- }}
- selection={selection}
- isEditing={false}
- onDelete={() => undefined}
- onEdit={() => undefined}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Open in Discover')).toBeInTheDocument();
- expect(screen.getByText('Open in Discover').closest('a')).toHaveAttribute(
- 'href',
- '/organizations/org-slug/discover/results/?display=top5&environment=prod&field=transaction&name=Errors&project=1&query=event.type%3Aerror&statsPeriod=14d&yAxis=count%28%29'
- );
- });
- it('calls onDuplicate when Duplicate Widget is clicked', async function () {
- const mock = jest.fn();
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- displayType: DisplayType.WORLD_MAP,
- queries: [{...multipleQueryWidget.queries[0], fields: ['count()']}],
- }}
- selection={selection}
- isEditing={false}
- onDelete={() => undefined}
- onEdit={() => undefined}
- onDuplicate={mock}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Duplicate Widget')).toBeInTheDocument();
- userEvent.click(screen.getByText('Duplicate Widget'));
- expect(mock).toHaveBeenCalledTimes(1);
- });
- it('does not add duplicate widgets if max widget is reached', async function () {
- const mock = jest.fn();
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- displayType: DisplayType.WORLD_MAP,
- queries: [{...multipleQueryWidget.queries[0], fields: ['count()']}],
- }}
- selection={selection}
- isEditing={false}
- onDelete={() => undefined}
- onEdit={() => undefined}
- onDuplicate={mock}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Duplicate Widget')).toBeInTheDocument();
- userEvent.click(screen.getByText('Duplicate Widget'));
- expect(mock).toHaveBeenCalledTimes(0);
- });
- it('calls onEdit when Edit Widget is clicked', async function () {
- const mock = jest.fn();
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- displayType: DisplayType.WORLD_MAP,
- queries: [{...multipleQueryWidget.queries[0], fields: ['count()']}],
- }}
- selection={selection}
- isEditing={false}
- onDelete={() => undefined}
- onEdit={mock}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Edit Widget')).toBeInTheDocument();
- userEvent.click(screen.getByText('Edit Widget'));
- expect(mock).toHaveBeenCalledTimes(1);
- });
- it('renders delete widget option', async function () {
- const mock = jest.fn();
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- displayType: DisplayType.WORLD_MAP,
- queries: [{...multipleQueryWidget.queries[0], fields: ['count()']}],
- }}
- selection={selection}
- isEditing={false}
- onDelete={mock}
- onEdit={() => undefined}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- userEvent.click(screen.getByTestId('context-menu'));
- expect(screen.getByText('Delete Widget')).toBeInTheDocument();
- userEvent.click(screen.getByText('Delete Widget'));
- // Confirm Modal
- mountGlobalModal();
- await screen.findByRole('dialog');
- userEvent.click(screen.getByTestId('confirm-button'));
- expect(mock).toHaveBeenCalled();
- });
- it('calls eventsV2 with a limit of 20 items', async function () {
- const mock = jest.fn();
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- displayType: DisplayType.TABLE,
- queries: [{...multipleQueryWidget.queries[0], fields: ['count()']}],
- }}
- selection={selection}
- isEditing={false}
- onDelete={mock}
- onEdit={() => undefined}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- tableItemLimit={20}
- />
- );
- await tick();
- expect(eventsMock).toHaveBeenCalledWith(
- '/organizations/org-slug/eventsv2/',
- expect.objectContaining({
- query: expect.objectContaining({
- per_page: 20,
- }),
- })
- );
- });
- it('calls eventsV2 with a default limit of 5 items', async function () {
- const mock = jest.fn();
- mountWithTheme(
- <WidgetCard
- api={api}
- organization={initialData.organization}
- widget={{
- ...multipleQueryWidget,
- displayType: DisplayType.TABLE,
- queries: [{...multipleQueryWidget.queries[0], fields: ['count()']}],
- }}
- selection={selection}
- isEditing={false}
- onDelete={mock}
- onEdit={() => undefined}
- onDuplicate={() => undefined}
- renderErrorMessage={() => undefined}
- isSorting={false}
- currentWidgetDragging={false}
- showContextMenu
- widgetLimitReached={false}
- />
- );
- await tick();
- expect(eventsMock).toHaveBeenCalledWith(
- '/organizations/org-slug/eventsv2/',
- expect.objectContaining({
- query: expect.objectContaining({
- per_page: 5,
- }),
- })
- );
- });
- });
|