2019-09-17 20:57:53 +02:00
|
|
|
/*
|
|
|
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
|
|
* or more contributor license agreements. Licensed under the Elastic License;
|
|
|
|
* you may not use this file except in compliance with the Elastic License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
import React from 'react';
|
2019-10-03 17:24:45 +02:00
|
|
|
import { ReactWrapper } from 'enzyme';
|
|
|
|
import { act } from 'react-dom/test-utils';
|
|
|
|
import { buildExistsFilter } from '@kbn/es-query';
|
2019-09-17 20:57:53 +02:00
|
|
|
import { App } from './app';
|
|
|
|
import { EditorFrameInstance } from '../types';
|
|
|
|
import { Storage } from 'ui/storage';
|
|
|
|
import { Document, SavedObjectStore } from '../persistence';
|
|
|
|
import { mount } from 'enzyme';
|
2019-10-06 16:25:24 +02:00
|
|
|
|
|
|
|
import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks';
|
|
|
|
const dataStartMock = dataPluginMock.createStartContract();
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
import {
|
|
|
|
TopNavMenu,
|
|
|
|
TopNavMenuData,
|
|
|
|
} from '../../../../../../src/legacy/core_plugins/kibana_react/public';
|
|
|
|
import { DataStart } from '../../../../../../src/legacy/core_plugins/data/public';
|
2019-09-17 20:57:53 +02:00
|
|
|
import { coreMock } from 'src/core/public/mocks';
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
jest.mock('../../../../../../src/legacy/core_plugins/kibana_react/public', () => ({
|
|
|
|
TopNavMenu: jest.fn(() => null),
|
2019-09-17 20:57:53 +02:00
|
|
|
}));
|
|
|
|
|
|
|
|
jest.mock('ui/new_platform');
|
|
|
|
jest.mock('../persistence');
|
|
|
|
jest.mock('src/core/public');
|
|
|
|
|
|
|
|
const waitForPromises = () => new Promise(resolve => setTimeout(resolve));
|
|
|
|
|
|
|
|
function createMockFrame(): jest.Mocked<EditorFrameInstance> {
|
|
|
|
return {
|
|
|
|
mount: jest.fn((el, props) => {}),
|
|
|
|
unmount: jest.fn(() => {}),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
function createMockFilterManager() {
|
|
|
|
const unsubscribe = jest.fn();
|
|
|
|
|
|
|
|
let subscriber: () => void;
|
|
|
|
let filters: unknown = [];
|
|
|
|
|
|
|
|
return {
|
|
|
|
getUpdates$: () => ({
|
|
|
|
subscribe: ({ next }: { next: () => void }) => {
|
|
|
|
subscriber = next;
|
|
|
|
return unsubscribe;
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
setFilters: (newFilters: unknown[]) => {
|
|
|
|
filters = newFilters;
|
|
|
|
subscriber();
|
|
|
|
},
|
|
|
|
getFilters: () => filters,
|
|
|
|
removeAll: () => {
|
|
|
|
filters = [];
|
|
|
|
subscriber();
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-09-17 20:57:53 +02:00
|
|
|
describe('Lens App', () => {
|
|
|
|
let frame: jest.Mocked<EditorFrameInstance>;
|
|
|
|
let core: ReturnType<typeof coreMock['createStart']>;
|
|
|
|
|
|
|
|
function makeDefaultArgs(): jest.Mocked<{
|
|
|
|
editorFrame: EditorFrameInstance;
|
2019-10-06 16:25:24 +02:00
|
|
|
data: typeof dataStartMock;
|
2019-09-17 20:57:53 +02:00
|
|
|
core: typeof core;
|
2019-10-06 16:25:24 +02:00
|
|
|
dataShim: DataStart;
|
2019-09-17 20:57:53 +02:00
|
|
|
store: Storage;
|
|
|
|
docId?: string;
|
|
|
|
docStorage: SavedObjectStore;
|
|
|
|
redirectTo: (id?: string) => void;
|
|
|
|
}> {
|
|
|
|
return ({
|
|
|
|
editorFrame: createMockFrame(),
|
2019-10-03 17:24:45 +02:00
|
|
|
core: {
|
|
|
|
...core,
|
|
|
|
application: {
|
|
|
|
...core.application,
|
|
|
|
capabilities: {
|
|
|
|
...core.application.capabilities,
|
2019-10-15 21:03:12 +02:00
|
|
|
visualize: { save: true, saveQuery: true, show: true },
|
2019-10-03 17:24:45 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2019-10-06 16:25:24 +02:00
|
|
|
dataShim: {
|
2019-10-03 17:24:45 +02:00
|
|
|
indexPatterns: {
|
|
|
|
indexPatterns: {
|
|
|
|
get: jest.fn(id => {
|
|
|
|
return new Promise(resolve => resolve({ id }));
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
timefilter: { history: {} },
|
|
|
|
filter: {
|
|
|
|
filterManager: createMockFilterManager(),
|
|
|
|
},
|
|
|
|
},
|
2019-09-17 20:57:53 +02:00
|
|
|
store: {
|
|
|
|
get: jest.fn(),
|
|
|
|
},
|
|
|
|
docStorage: {
|
|
|
|
load: jest.fn(),
|
|
|
|
save: jest.fn(),
|
|
|
|
},
|
|
|
|
redirectTo: jest.fn(id => {}),
|
|
|
|
} as unknown) as jest.Mocked<{
|
|
|
|
editorFrame: EditorFrameInstance;
|
2019-10-06 16:25:24 +02:00
|
|
|
data: typeof dataStartMock;
|
2019-09-17 20:57:53 +02:00
|
|
|
core: typeof core;
|
2019-10-06 16:25:24 +02:00
|
|
|
dataShim: DataStart;
|
2019-09-17 20:57:53 +02:00
|
|
|
store: Storage;
|
|
|
|
docId?: string;
|
|
|
|
docStorage: SavedObjectStore;
|
|
|
|
redirectTo: (id?: string) => void;
|
|
|
|
}>;
|
|
|
|
}
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
frame = createMockFrame();
|
|
|
|
core = coreMock.createStart();
|
|
|
|
|
|
|
|
core.uiSettings.get.mockImplementation(
|
|
|
|
jest.fn(type => {
|
|
|
|
if (type === 'timepicker:timeDefaults') {
|
|
|
|
return { from: 'now-7d', to: 'now' };
|
|
|
|
} else if (type === 'search:queryLanguage') {
|
|
|
|
return 'kuery';
|
|
|
|
} else {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
(core.http.basePath.get as jest.Mock).mockReturnValue(`/testbasepath`);
|
|
|
|
(core.http.basePath.prepend as jest.Mock).mockImplementation(s => `/testbasepath${s}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders the editor frame', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
mount(<App {...args} />);
|
|
|
|
|
|
|
|
expect(frame.mount.mock.calls).toMatchInlineSnapshot(`
|
|
|
|
Array [
|
|
|
|
Array [
|
|
|
|
<div
|
2019-09-23 20:57:35 +02:00
|
|
|
class="lnsApp__frame"
|
2019-09-17 20:57:53 +02:00
|
|
|
/>,
|
|
|
|
Object {
|
|
|
|
"dateRange": Object {
|
|
|
|
"fromDate": "now-7d",
|
|
|
|
"toDate": "now",
|
|
|
|
},
|
|
|
|
"doc": undefined,
|
2019-10-03 17:24:45 +02:00
|
|
|
"filters": Array [],
|
2019-09-17 20:57:53 +02:00
|
|
|
"onChange": [Function],
|
|
|
|
"onError": [Function],
|
|
|
|
"query": Object {
|
|
|
|
"language": "kuery",
|
|
|
|
"query": "",
|
|
|
|
},
|
2019-10-03 17:24:45 +02:00
|
|
|
"savedQuery": undefined,
|
2019-09-17 20:57:53 +02:00
|
|
|
},
|
|
|
|
],
|
|
|
|
]
|
|
|
|
`);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('sets breadcrumbs when the document title changes', async () => {
|
|
|
|
const defaultArgs = makeDefaultArgs();
|
|
|
|
const instance = mount(<App {...defaultArgs} />);
|
|
|
|
|
|
|
|
expect(core.chrome.setBreadcrumbs).toHaveBeenCalledWith([
|
|
|
|
{ text: 'Visualize', href: '/testbasepath/app/kibana#/visualize' },
|
|
|
|
{ text: 'Create' },
|
|
|
|
]);
|
|
|
|
|
|
|
|
(defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({
|
|
|
|
id: '1234',
|
|
|
|
title: 'Daaaaaaadaumching!',
|
|
|
|
state: {
|
|
|
|
query: 'fake query',
|
|
|
|
datasourceMetaData: { filterableIndexPatterns: [{ id: '1', title: 'saved' }] },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
instance.setProps({ docId: '1234' });
|
|
|
|
await waitForPromises();
|
|
|
|
|
|
|
|
expect(defaultArgs.core.chrome.setBreadcrumbs).toHaveBeenCalledWith([
|
|
|
|
{ text: 'Visualize', href: '/testbasepath/app/kibana#/visualize' },
|
|
|
|
{ text: 'Daaaaaaadaumching!' },
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('persistence', () => {
|
|
|
|
it('does not load a document if there is no document id', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
|
|
|
|
mount(<App {...args} />);
|
|
|
|
|
|
|
|
expect(args.docStorage.load).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('loads a document and uses query if there is a document id', async () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
(args.docStorage.load as jest.Mock).mockResolvedValue({
|
|
|
|
id: '1234',
|
|
|
|
state: {
|
|
|
|
query: 'fake query',
|
|
|
|
datasourceMetaData: { filterableIndexPatterns: [{ id: '1', title: 'saved' }] },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
instance.setProps({ docId: '1234' });
|
|
|
|
await waitForPromises();
|
|
|
|
|
|
|
|
expect(args.docStorage.load).toHaveBeenCalledWith('1234');
|
2019-10-06 16:25:24 +02:00
|
|
|
expect(args.dataShim.indexPatterns.indexPatterns.get).toHaveBeenCalledWith('1');
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
2019-09-17 20:57:53 +02:00
|
|
|
expect.objectContaining({
|
|
|
|
query: 'fake query',
|
2019-10-03 17:24:45 +02:00
|
|
|
indexPatterns: [{ id: '1' }],
|
2019-09-17 20:57:53 +02:00
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
expect(frame.mount).toHaveBeenCalledWith(
|
|
|
|
expect.any(Element),
|
|
|
|
expect.objectContaining({
|
|
|
|
doc: {
|
|
|
|
id: '1234',
|
|
|
|
state: {
|
|
|
|
query: 'fake query',
|
|
|
|
datasourceMetaData: { filterableIndexPatterns: [{ id: '1', title: 'saved' }] },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not load documents on sequential renders unless the id changes', async () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
(args.docStorage.load as jest.Mock).mockResolvedValue({ id: '1234' });
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
instance.setProps({ docId: '1234' });
|
|
|
|
await waitForPromises();
|
|
|
|
instance.setProps({ docId: '1234' });
|
|
|
|
await waitForPromises();
|
|
|
|
|
|
|
|
expect(args.docStorage.load).toHaveBeenCalledTimes(1);
|
|
|
|
|
|
|
|
instance.setProps({ docId: '9876' });
|
|
|
|
await waitForPromises();
|
|
|
|
|
|
|
|
expect(args.docStorage.load).toHaveBeenCalledTimes(2);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('handles document load errors', async () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
(args.docStorage.load as jest.Mock).mockRejectedValue('failed to load');
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
instance.setProps({ docId: '1234' });
|
|
|
|
await waitForPromises();
|
|
|
|
|
|
|
|
expect(args.docStorage.load).toHaveBeenCalledWith('1234');
|
|
|
|
expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled();
|
|
|
|
expect(args.redirectTo).toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('save button', () => {
|
2019-10-03 17:24:45 +02:00
|
|
|
function getButton(instance: ReactWrapper): TopNavMenuData {
|
|
|
|
return (instance
|
|
|
|
.find('[data-test-subj="lnsApp_topNav"]')
|
|
|
|
.prop('config') as TopNavMenuData[]).find(
|
|
|
|
button => button.testId === 'lnsApp_saveButton'
|
|
|
|
)!;
|
|
|
|
}
|
|
|
|
|
|
|
|
it('shows a disabled save button when the user does not have permissions', async () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.core.application = {
|
|
|
|
...args.core.application,
|
|
|
|
capabilities: {
|
|
|
|
...args.core.application.capabilities,
|
2019-10-15 21:03:12 +02:00
|
|
|
visualize: { save: false, saveQuery: false, show: true },
|
2019-10-03 17:24:45 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
expect(getButton(instance).disableButton).toEqual(true);
|
|
|
|
|
|
|
|
const onChange = frame.mount.mock.calls[0][1].onChange;
|
|
|
|
onChange({ filterableIndexPatterns: [], doc: ('will save this' as unknown) as Document });
|
|
|
|
|
|
|
|
instance.update();
|
|
|
|
|
|
|
|
expect(getButton(instance).disableButton).toEqual(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('shows a save button that is enabled when the frame has provided its state', async () => {
|
2019-09-17 20:57:53 +02:00
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(getButton(instance).disableButton).toEqual(true);
|
2019-09-17 20:57:53 +02:00
|
|
|
|
|
|
|
const onChange = frame.mount.mock.calls[0][1].onChange;
|
2019-10-03 17:24:45 +02:00
|
|
|
onChange({ filterableIndexPatterns: [], doc: ('will save this' as unknown) as Document });
|
2019-09-17 20:57:53 +02:00
|
|
|
|
|
|
|
instance.update();
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(getButton(instance).disableButton).toEqual(false);
|
2019-09-17 20:57:53 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('saves the latest doc and then prevents more saving', async () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
(args.docStorage.save as jest.Mock).mockResolvedValue({ id: '1234' });
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
expect(frame.mount).toHaveBeenCalledTimes(1);
|
|
|
|
|
|
|
|
const onChange = frame.mount.mock.calls[0][1].onChange;
|
2019-10-03 17:24:45 +02:00
|
|
|
onChange({ filterableIndexPatterns: [], doc: ({ id: undefined } as unknown) as Document });
|
2019-09-17 20:57:53 +02:00
|
|
|
|
|
|
|
instance.update();
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(getButton(instance).disableButton).toEqual(false);
|
2019-09-17 20:57:53 +02:00
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
act(() => {
|
|
|
|
getButton(instance).run(instance.getDOMNode());
|
|
|
|
});
|
2019-09-17 20:57:53 +02:00
|
|
|
|
|
|
|
expect(args.docStorage.save).toHaveBeenCalledWith({ id: undefined });
|
|
|
|
|
|
|
|
await waitForPromises();
|
|
|
|
|
|
|
|
expect(args.redirectTo).toHaveBeenCalledWith('1234');
|
|
|
|
|
|
|
|
instance.setProps({ docId: '1234' });
|
|
|
|
|
|
|
|
expect(args.docStorage.load).not.toHaveBeenCalled();
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(getButton(instance).disableButton).toEqual(true);
|
2019-09-17 20:57:53 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('handles save failure by showing a warning, but still allows another save', async () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
(args.docStorage.save as jest.Mock).mockRejectedValue({ message: 'failed' });
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
const onChange = frame.mount.mock.calls[0][1].onChange;
|
2019-10-03 17:24:45 +02:00
|
|
|
onChange({ filterableIndexPatterns: [], doc: ({ id: undefined } as unknown) as Document });
|
2019-09-17 20:57:53 +02:00
|
|
|
|
|
|
|
instance.update();
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
act(() => {
|
|
|
|
getButton(instance).run(instance.getDOMNode());
|
|
|
|
});
|
2019-09-17 20:57:53 +02:00
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
await waitForPromises();
|
2019-09-17 20:57:53 +02:00
|
|
|
await waitForPromises();
|
|
|
|
|
|
|
|
expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled();
|
|
|
|
expect(args.redirectTo).not.toHaveBeenCalled();
|
|
|
|
await waitForPromises();
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(getButton(instance).disableButton).toEqual(false);
|
2019-09-17 20:57:53 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('query bar state management', () => {
|
|
|
|
it('uses the default time and query language settings', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
mount(<App {...args} />);
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
2019-09-17 20:57:53 +02:00
|
|
|
expect.objectContaining({
|
|
|
|
query: { query: '', language: 'kuery' },
|
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
expect(frame.mount).toHaveBeenCalledWith(
|
|
|
|
expect.any(Element),
|
|
|
|
expect.objectContaining({
|
|
|
|
dateRange: { fromDate: 'now-7d', toDate: 'now' },
|
|
|
|
query: { query: '', language: 'kuery' },
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
it('updates the index patterns when the editor frame is changed', async () => {
|
2019-09-17 20:57:53 +02:00
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
2019-09-17 20:57:53 +02:00
|
|
|
expect.objectContaining({
|
|
|
|
indexPatterns: [],
|
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
|
|
|
|
const onChange = frame.mount.mock.calls[0][1].onChange;
|
|
|
|
onChange({
|
2019-10-03 17:24:45 +02:00
|
|
|
filterableIndexPatterns: [{ id: '1', title: 'newIndex' }],
|
|
|
|
doc: ({ id: undefined } as unknown) as Document,
|
|
|
|
});
|
|
|
|
|
|
|
|
await waitForPromises();
|
|
|
|
instance.update();
|
|
|
|
|
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
|
|
|
expect.objectContaining({
|
|
|
|
indexPatterns: [{ id: '1' }],
|
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Do it again to verify that the dirty checking is done right
|
|
|
|
onChange({
|
|
|
|
filterableIndexPatterns: [{ id: '2', title: 'second index' }],
|
2019-09-17 20:57:53 +02:00
|
|
|
doc: ({ id: undefined } as unknown) as Document,
|
|
|
|
});
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
await waitForPromises();
|
2019-09-17 20:57:53 +02:00
|
|
|
instance.update();
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(TopNavMenu).toHaveBeenLastCalledWith(
|
2019-09-17 20:57:53 +02:00
|
|
|
expect.objectContaining({
|
2019-10-03 17:24:45 +02:00
|
|
|
indexPatterns: [{ id: '2' }],
|
2019-09-17 20:57:53 +02:00
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
it('updates the editor frame when the user changes query or time in the search bar', () => {
|
2019-09-17 20:57:53 +02:00
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
instance.find(TopNavMenu).prop('onQuerySubmit')!({
|
2019-09-17 20:57:53 +02:00
|
|
|
dateRange: { from: 'now-14d', to: 'now-7d' },
|
|
|
|
query: { query: 'new', language: 'lucene' },
|
2019-10-03 17:24:45 +02:00
|
|
|
});
|
2019-09-17 20:57:53 +02:00
|
|
|
|
|
|
|
instance.update();
|
|
|
|
|
2019-10-03 17:24:45 +02:00
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
2019-09-17 20:57:53 +02:00
|
|
|
expect.objectContaining({
|
|
|
|
query: { query: 'new', language: 'lucene' },
|
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
expect(frame.mount).toHaveBeenCalledWith(
|
|
|
|
expect.any(Element),
|
|
|
|
expect.objectContaining({
|
|
|
|
dateRange: { fromDate: 'now-14d', toDate: 'now-7d' },
|
|
|
|
query: { query: 'new', language: 'lucene' },
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
2019-10-03 17:24:45 +02:00
|
|
|
|
|
|
|
it('updates the filters when the user changes them', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
2019-10-06 16:25:24 +02:00
|
|
|
args.dataShim.filter.filterManager.setFilters([
|
2019-10-03 17:24:45 +02:00
|
|
|
buildExistsFilter({ name: 'myfield' }, { id: 'index1' }),
|
|
|
|
]);
|
|
|
|
|
|
|
|
instance.update();
|
|
|
|
|
|
|
|
expect(frame.mount).toHaveBeenCalledWith(
|
|
|
|
expect.any(Element),
|
|
|
|
expect.objectContaining({
|
|
|
|
filters: [buildExistsFilter({ name: 'myfield' }, { id: 'index1' })],
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('saved query handling', () => {
|
|
|
|
it('does not allow saving when the user is missing the saveQuery permission', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.core.application = {
|
|
|
|
...args.core.application,
|
|
|
|
capabilities: {
|
|
|
|
...args.core.application.capabilities,
|
2019-10-15 21:03:12 +02:00
|
|
|
visualize: { save: false, saveQuery: false, show: true },
|
2019-10-03 17:24:45 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
mount(<App {...args} />);
|
|
|
|
|
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
|
|
|
expect.objectContaining({ showSaveQuery: false }),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('persists the saved query ID when the query is saved', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
|
|
|
expect.objectContaining({
|
|
|
|
showSaveQuery: true,
|
|
|
|
savedQuery: undefined,
|
|
|
|
onSaved: expect.any(Function),
|
|
|
|
onSavedQueryUpdated: expect.any(Function),
|
|
|
|
onClearSavedQuery: expect.any(Function),
|
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
|
|
|
|
act(() => {
|
|
|
|
instance.find(TopNavMenu).prop('onSaved')!({
|
|
|
|
id: '1',
|
|
|
|
attributes: {
|
|
|
|
title: '',
|
|
|
|
description: '',
|
|
|
|
query: { query: '', language: 'lucene' },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
|
|
|
expect.objectContaining({
|
|
|
|
savedQuery: {
|
|
|
|
id: '1',
|
|
|
|
attributes: {
|
|
|
|
title: '',
|
|
|
|
description: '',
|
|
|
|
query: { query: '', language: 'lucene' },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('changes the saved query ID when the query is updated', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
act(() => {
|
|
|
|
instance.find(TopNavMenu).prop('onSaved')!({
|
|
|
|
id: '1',
|
|
|
|
attributes: {
|
|
|
|
title: '',
|
|
|
|
description: '',
|
|
|
|
query: { query: '', language: 'lucene' },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
act(() => {
|
|
|
|
instance.find(TopNavMenu).prop('onSavedQueryUpdated')!({
|
|
|
|
id: '2',
|
|
|
|
attributes: {
|
|
|
|
title: 'new title',
|
|
|
|
description: '',
|
|
|
|
query: { query: '', language: 'lucene' },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(TopNavMenu).toHaveBeenCalledWith(
|
|
|
|
expect.objectContaining({
|
|
|
|
savedQuery: {
|
|
|
|
id: '2',
|
|
|
|
attributes: {
|
|
|
|
title: 'new title',
|
|
|
|
description: '',
|
|
|
|
query: { query: '', language: 'lucene' },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
{}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('clears all existing filters when the active saved query is cleared', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
instance.find(TopNavMenu).prop('onQuerySubmit')!({
|
|
|
|
dateRange: { from: 'now-14d', to: 'now-7d' },
|
|
|
|
query: { query: 'new', language: 'lucene' },
|
|
|
|
});
|
|
|
|
|
2019-10-06 16:25:24 +02:00
|
|
|
args.dataShim.filter.filterManager.setFilters([
|
2019-10-03 17:24:45 +02:00
|
|
|
buildExistsFilter({ name: 'myfield' }, { id: 'index1' }),
|
|
|
|
]);
|
|
|
|
instance.update();
|
|
|
|
|
|
|
|
instance.find(TopNavMenu).prop('onClearSavedQuery')!();
|
|
|
|
instance.update();
|
|
|
|
|
|
|
|
expect(frame.mount).toHaveBeenLastCalledWith(
|
|
|
|
expect.any(Element),
|
|
|
|
expect.objectContaining({
|
|
|
|
filters: [],
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
2019-09-17 20:57:53 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it('displays errors from the frame in a toast', () => {
|
|
|
|
const args = makeDefaultArgs();
|
|
|
|
args.editorFrame = frame;
|
|
|
|
|
|
|
|
const instance = mount(<App {...args} />);
|
|
|
|
|
|
|
|
const onError = frame.mount.mock.calls[0][1].onError;
|
|
|
|
onError({ message: 'error' });
|
|
|
|
|
|
|
|
instance.update();
|
|
|
|
|
|
|
|
expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|