[Canvas] Functional tests for embeddables (#120024) (#121929)

* Adds embeddable functional tests for Canvas

* Add saved search tests

* Fixed saved search tests

* Added lens tests

* Fix test user permissions for saved search tests

* Added visualize and maps tests

* Revert change to saved object finder

* Fix imports

* Remove unused i18n labels

* Fix ts errors

* Fixed lens test descriptions
This commit is contained in:
Catherine Liu 2021-12-22 20:47:34 -07:00 committed by GitHub
parent 7dca9c0eab
commit 534acbd32f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 388 additions and 58 deletions

View file

@ -22,7 +22,7 @@ const strings = {
}),
getTitleText: () =>
i18n.translate('xpack.canvas.embedObject.titleText', {
defaultMessage: 'Add from Kibana',
defaultMessage: 'Add from library',
}),
};
export interface Props {

View file

@ -246,6 +246,7 @@ export class PageManager extends Component<Props, State> {
<button
onClick={onAddPage}
className="canvasPageManager__addPage kbn-resetFocusState"
data-test-subj="canvasAddPageButton"
>
<EuiIcon color="ghost" type="plusInCircle" size="l" />
</button>

View file

@ -109,7 +109,11 @@ export const Toolbar: FC<Props> = ({
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="text" onClick={() => toggleTray('pageManager')}>
<EuiButtonEmpty
color="text"
onClick={() => toggleTray('pageManager')}
data-test-subj="canvasPageManagerButton"
>
{strings.getPageButtonLabel(selectedPageNumber, totalPages)}
</EuiButtonEmpty>
</EuiFlexItem>

View file

@ -136,7 +136,12 @@ export const WorkpadConfig: FunctionComponent<Props> = (props) => {
<EuiSpacer size="m" />
<EuiFormRow label={strings.getNameLabel()} display="rowCompressed">
<EuiFieldText compressed value={name} onChange={(e) => setName(e.target.value)} />
<EuiFieldText
compressed
value={name}
onChange={(e) => setName(e.target.value)}
data-test-subj="canvas-workpad-name-text-field"
/>
</EuiFormRow>
<EuiSpacer size="s" />

View file

@ -464,6 +464,7 @@ export const EditMenu: FunctionComponent<Props> = ({
deleteNodes();
closePopover();
},
'data-test-subj': 'canvasEditMenuDeleteButton',
},
{
name: shortcutHelp.CLONE,

View file

@ -44,10 +44,6 @@ const strings = {
i18n.translate('xpack.canvas.workpadHeaderElementMenu.elementMenuLabel', {
defaultMessage: 'Add an element',
}),
getEmbedObjectMenuItemLabel: () =>
i18n.translate('xpack.canvas.workpadHeaderElementMenu.embedObjectMenuItemLabel', {
defaultMessage: 'Add from Kibana',
}),
getFilterMenuItemLabel: () =>
i18n.translate('xpack.canvas.workpadHeaderElementMenu.filterMenuItemLabel', {
defaultMessage: 'Filter',

View file

@ -169,7 +169,12 @@ export const WorkpadHeader: FC<Props> = ({
{{
primaryActionButton: <ElementMenu addElement={addElement} elements={elements} />,
quickButtonGroup: <QuickButtonGroup buttons={quickButtons} />,
addFromLibraryButton: <AddFromLibraryButton onClick={showEmbedPanel} />,
addFromLibraryButton: (
<AddFromLibraryButton
onClick={showEmbedPanel}
data-test-subj="canvas-add-from-library-button"
/>
),
extraButtons: [<EditorMenu addElement={addElement} />],
}}
</SolutionToolbar>

View file

@ -7454,7 +7454,6 @@
"xpack.canvas.workpadHeaderElementMenu.chartMenuItemLabel": "グラフ",
"xpack.canvas.workpadHeaderElementMenu.elementMenuButtonLabel": "エレメントを追加",
"xpack.canvas.workpadHeaderElementMenu.elementMenuLabel": "要素を追加",
"xpack.canvas.workpadHeaderElementMenu.embedObjectMenuItemLabel": "Kibanaから追加",
"xpack.canvas.workpadHeaderElementMenu.filterMenuItemLabel": "フィルター",
"xpack.canvas.workpadHeaderElementMenu.imageMenuItemLabel": "画像",
"xpack.canvas.workpadHeaderElementMenu.manageAssetsMenuItemLabel": "アセットの管理",

View file

@ -7508,7 +7508,6 @@
"xpack.canvas.workpadHeaderElementMenu.chartMenuItemLabel": "图表",
"xpack.canvas.workpadHeaderElementMenu.elementMenuButtonLabel": "添加元素",
"xpack.canvas.workpadHeaderElementMenu.elementMenuLabel": "添加元素",
"xpack.canvas.workpadHeaderElementMenu.embedObjectMenuItemLabel": "从 Kibana 添加",
"xpack.canvas.workpadHeaderElementMenu.filterMenuItemLabel": "筛选",
"xpack.canvas.workpadHeaderElementMenu.imageMenuItemLabel": "图像",
"xpack.canvas.workpadHeaderElementMenu.manageAssetsMenuItemLabel": "管理资产",

View file

@ -0,0 +1,100 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function canvasLensTest({ getService, getPageObjects }: FtrProviderContext) {
const retry = getService('retry');
const PageObjects = getPageObjects(['canvas', 'common', 'header', 'lens']);
const esArchiver = getService('esArchiver');
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const kibanaServer = getService('kibanaServer');
const testSubjects = getService('testSubjects');
const archives = {
es: 'x-pack/test/functional/es_archives/canvas/logstash_lens',
kbn: 'x-pack/test/functional/fixtures/kbn_archiver/canvas/lens',
};
describe('lens in canvas', function () {
before(async () => {
await esArchiver.load(archives.es);
await kibanaServer.importExport.load(archives.kbn);
await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-lens' });
// open canvas home
await PageObjects.common.navigateToApp('canvas');
// load test workpad
await PageObjects.common.navigateToApp('canvas', {
hash: '/workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31/page/1',
});
});
after(async () => {
await esArchiver.unload(archives.es);
await kibanaServer.importExport.unload(archives.kbn);
});
describe('by-reference', () => {
it('renders lens visualization using savedLens expression', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.lens.assertMetric('Maximum of bytes', '16,788');
});
it('adds existing lens embeddable from the visualize library', async () => {
await PageObjects.canvas.goToListingPageViaBreadcrumbs();
await PageObjects.canvas.createNewWorkpad();
await PageObjects.canvas.setWorkpadName('lens tests');
await PageObjects.canvas.clickAddFromLibrary();
await dashboardAddPanel.addEmbeddable('Artistpreviouslyknownaslens', 'lens');
await testSubjects.existOrFail('embeddablePanelHeading-Artistpreviouslyknownaslens');
});
it('edits lens by-reference embeddable', async () => {
await dashboardPanelActions.editPanelByTitle('Artistpreviouslyknownaslens');
await PageObjects.lens.save('Artistpreviouslyknownaslens v2', false, true);
await testSubjects.existOrFail('embeddablePanelHeading-Artistpreviouslyknownaslensv2');
});
});
describe('by-value', () => {
it('creates new lens embeddable', async () => {
await PageObjects.canvas.deleteSelectedElement();
const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount();
await PageObjects.canvas.createNewVis('lens');
await PageObjects.lens.goToTimeRange();
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
operation: 'date_histogram',
field: '@timestamp',
});
await PageObjects.lens.configureDimension({
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
operation: 'average',
field: 'bytes',
});
await PageObjects.lens.saveAndReturn();
await retry.try(async () => {
const embeddableCount = await PageObjects.canvas.getEmbeddableCount();
expect(embeddableCount).to.eql(originalEmbeddableCount + 1);
});
});
it('edits lens by-value embeddable', async () => {
const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount();
await dashboardPanelActions.toggleContextMenu();
await dashboardPanelActions.clickEdit();
await PageObjects.lens.saveAndReturn();
await retry.try(async () => {
const embeddableCount = await PageObjects.canvas.getEmbeddableCount();
expect(embeddableCount).to.eql(originalEmbeddableCount);
});
});
});
});
}

View file

@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['canvas', 'common', 'header', 'maps']);
const dashboardPanelActions = getService('dashboardPanelActions');
const dashboardAddPanel = getService('dashboardAddPanel');
const testSubjects = getService('testSubjects');
describe('maps in canvas', function () {
before(async () => {
// open canvas home
await PageObjects.common.navigateToApp('canvas');
// create new workpad
await PageObjects.canvas.createNewWorkpad();
await PageObjects.canvas.setWorkpadName('maps tests');
});
describe('by-value', () => {
it('creates new map embeddable', async () => {
const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount();
await PageObjects.canvas.createNewVis('maps');
await PageObjects.maps.clickSaveAndReturnButton();
const embeddableCount = await PageObjects.canvas.getEmbeddableCount();
expect(embeddableCount).to.eql(originalEmbeddableCount + 1);
});
it('edits map by-value embeddable', async () => {
const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount();
await dashboardPanelActions.toggleContextMenu();
await dashboardPanelActions.clickEdit();
await PageObjects.maps.saveMap('canvas test map');
const embeddableCount = await PageObjects.canvas.getEmbeddableCount();
expect(embeddableCount).to.eql(originalEmbeddableCount);
});
});
describe('by-reference', () => {
it('adds existing map embeddable from the visualize library', async () => {
await PageObjects.canvas.deleteSelectedElement();
await PageObjects.canvas.clickAddFromLibrary();
await dashboardAddPanel.addEmbeddable('canvas test map', 'map');
await testSubjects.existOrFail('embeddablePanelHeading-canvastestmap');
});
it('edits map by-reference embeddable', async () => {
await dashboardPanelActions.editPanelByTitle('canvas test map');
await PageObjects.maps.saveMap('canvas test map v2', true, false);
await testSubjects.existOrFail('embeddablePanelHeading-canvastestmapv2');
await PageObjects.canvas.deleteSelectedElement();
});
});
});
}

View file

@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['canvas', 'common', 'header', 'discover']);
const testSubjects = getService('testSubjects');
const esArchiver = getService('esArchiver');
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const archives = {
es: 'test/functional/fixtures/es_archiver/dashboard/current/kibana',
};
describe('saved search in canvas', function () {
before(async () => {
await esArchiver.load(archives.es);
// open canvas home
await PageObjects.common.navigateToApp('canvas');
// create new workpad
await PageObjects.canvas.createNewWorkpad();
await PageObjects.canvas.setWorkpadName('saved search tests');
});
after(async () => {
await esArchiver.unload(archives.es);
});
describe('by-reference', () => {
it('adds existing saved search embeddable from the visualize library', async () => {
await PageObjects.canvas.clickAddFromLibrary();
await dashboardAddPanel.addSavedSearch('Rendering-Test:-saved-search');
await testSubjects.existOrFail('embeddablePanelHeading-RenderingTest:savedsearch');
});
it('edits saved search by-reference embeddable', async () => {
await dashboardPanelActions.editPanelByTitle('Rendering Test: saved search');
await PageObjects.discover.saveSearch('Rendering Test: saved search v2');
await PageObjects.common.navigateToApp('canvas');
await PageObjects.canvas.loadFirstWorkpad('saved search tests');
await testSubjects.existOrFail('embeddablePanelHeading-RenderingTest:savedsearchv2');
});
});
});
}

View file

@ -0,0 +1,99 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const retry = getService('retry');
const PageObjects = getPageObjects(['canvas', 'common', 'header', 'visualize']);
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
const dashboardAddPanel = getService('dashboardAddPanel');
const dashboardPanelActions = getService('dashboardPanelActions');
const archives = {
es: 'test/functional/fixtures/es_archiver/dashboard/current/kibana',
};
describe('visualization in canvas', function () {
before(async () => {
await esArchiver.load(archives.es);
// open canvas home
await PageObjects.common.navigateToApp('canvas');
// create new workpad
await PageObjects.canvas.createNewWorkpad();
await PageObjects.canvas.setWorkpadName('visualization tests');
});
after(async () => {
await esArchiver.unload(archives.es);
});
describe('by-reference', () => {
it('adds existing visualize embeddable from the visualize library', async () => {
await PageObjects.canvas.clickAddFromLibrary();
await dashboardAddPanel.addVisualization('Rendering-Test: metric');
await testSubjects.existOrFail('embeddablePanelHeading-RenderingTest:metric');
});
it('edits visualize by-reference embeddable', async () => {
await dashboardPanelActions.editPanelByTitle('Rendering Test: metric');
await PageObjects.visualize.saveVisualization('Rendering Test: metric v2', {
saveAsNew: false,
redirectToOrigin: true,
});
await testSubjects.existOrFail('embeddablePanelHeading-RenderingTest:metricv2');
await PageObjects.canvas.deleteSelectedElement();
});
});
describe('by-value', () => {
it('creates new tsvb embeddable', async () => {
const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount();
await PageObjects.canvas.createNewVis('metrics');
await PageObjects.visualize.saveVisualizationAndReturn();
await retry.try(async () => {
const embeddableCount = await PageObjects.canvas.getEmbeddableCount();
expect(embeddableCount).to.eql(originalEmbeddableCount + 1);
});
});
it('edits tsvb by-value embeddable', async () => {
const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount();
await dashboardPanelActions.toggleContextMenu();
await dashboardPanelActions.clickEdit();
await PageObjects.visualize.saveVisualizationAndReturn();
await retry.try(async () => {
const embeddableCount = await PageObjects.canvas.getEmbeddableCount();
expect(embeddableCount).to.eql(originalEmbeddableCount);
});
await PageObjects.canvas.deleteSelectedElement();
});
it('creates new vega embeddable', async () => {
const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount();
await PageObjects.canvas.createNewVis('vega');
await PageObjects.visualize.saveVisualizationAndReturn();
await retry.try(async () => {
const embeddableCount = await PageObjects.canvas.getEmbeddableCount();
expect(embeddableCount).to.eql(originalEmbeddableCount + 1);
});
});
it('edits vega by-value embeddable', async () => {
const originalEmbeddableCount = await PageObjects.canvas.getEmbeddableCount();
await dashboardPanelActions.toggleContextMenu();
await dashboardPanelActions.clickEdit();
await PageObjects.visualize.saveVisualizationAndReturn();
await retry.try(async () => {
const embeddableCount = await PageObjects.canvas.getEmbeddableCount();
expect(embeddableCount).to.eql(originalEmbeddableCount);
});
});
});
});
}

View file

@ -12,7 +12,14 @@ export default function canvasApp({ loadTestFile, getService }) {
describe('Canvas app', function canvasAppTestSuite() {
before(async () => {
// init data
await security.testUser.setRoles(['test_logstash_reader', 'global_canvas_all']);
await security.testUser.setRoles([
'test_logstash_reader',
'global_canvas_all',
'global_discover_all',
'global_maps_all',
// TODO: Fix permission check, save and return button is disabled when dashboard is disabled
'global_dashboard_all',
]);
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
});
@ -27,7 +34,10 @@ export default function canvasApp({ loadTestFile, getService }) {
loadTestFile(require.resolve('./custom_elements'));
loadTestFile(require.resolve('./feature_controls/canvas_security'));
loadTestFile(require.resolve('./feature_controls/canvas_spaces'));
loadTestFile(require.resolve('./lens'));
loadTestFile(require.resolve('./embeddables/lens'));
loadTestFile(require.resolve('./embeddables/maps'));
loadTestFile(require.resolve('./embeddables/saved_search'));
loadTestFile(require.resolve('./embeddables/visualization'));
loadTestFile(require.resolve('./reports'));
loadTestFile(require.resolve('./saved_object_resolve'));
});

View file

@ -1,42 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrProviderContext } from '../../ftr_provider_context';
export default function canvasLensTest({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['canvas', 'common', 'header', 'lens']);
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const archives = {
es: 'x-pack/test/functional/es_archives/canvas/logstash_lens',
kbn: 'x-pack/test/functional/fixtures/kbn_archiver/canvas/lens',
};
describe('lens in canvas', function () {
before(async () => {
await esArchiver.load(archives.es);
await kibanaServer.importExport.load(archives.kbn);
// open canvas home
await PageObjects.common.navigateToApp('canvas');
// load test workpad
await PageObjects.common.navigateToApp('canvas', {
hash: '/workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31/page/1',
});
});
after(async () => {
await esArchiver.unload(archives.es);
await kibanaServer.importExport.unload(archives.kbn);
});
it('renders lens visualization', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.lens.assertMetric('Maximum of bytes', '16,788');
});
});
}

View file

@ -10,6 +10,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../ftr_provider_context';
export function CanvasPageProvider({ getService, getPageObjects }: FtrProviderContext) {
const log = getService('log');
const testSubjects = getService('testSubjects');
const find = getService('find');
const browser = getService('browser');
@ -46,6 +47,11 @@ export function CanvasPageProvider({ getService, getPageObjects }: FtrProviderCo
await testSubjects.existOrFail('canvasWorkpadPage');
},
async createNewWorkpad() {
log.debug('CanvasPage.createNewWorkpad');
await testSubjects.click('create-workpad-button');
},
async fillOutCustomElementForm(name: string, description: string) {
// Fill out the custom element form and submit it
await testSubjects.setValue('canvasCustomElementForm-name', name, {
@ -115,5 +121,41 @@ export function CanvasPageProvider({ getService, getPageObjects }: FtrProviderCo
return filters.and.filter((f: any) => f.filterType === 'exactly');
},
async clickAddFromLibrary() {
log.debug('CanvasPage.clickAddFromLibrary');
await testSubjects.click('canvas-add-from-library-button');
await testSubjects.existOrFail('dashboardAddPanel');
},
async setWorkpadName(name: string) {
log.debug('CanvasPage.setWorkpadName');
await testSubjects.setValue('canvas-workpad-name-text-field', name);
const lastBreadcrumb = await testSubjects.getVisibleText('breadcrumb last');
expect(lastBreadcrumb).to.eql(name);
},
async goToListingPageViaBreadcrumbs() {
log.debug('CanvasPage.goToListingPageViaBreadcrumbs');
await testSubjects.click('breadcrumb first');
},
async createNewVis(visType: string) {
log.debug('CanvasPage.createNewVisType', visType);
await testSubjects.click('canvasEditorMenuButton');
await testSubjects.click(`visType-${visType}`);
},
async getEmbeddableCount() {
log.debug('CanvasPage.getEmbeddableCount');
const panels = await testSubjects.findAll('embeddablePanel');
return panels.length;
},
async deleteSelectedElement() {
log.debug('CanvasPage.deleteSelectedElement');
await testSubjects.click('canvasWorkpadEditMenuButton');
await testSubjects.click('canvasEditMenuDeleteButton');
},
};
}

View file

@ -148,13 +148,13 @@ export class GisPageObject extends FtrService {
await this.renderable.waitForRender();
}
async saveMap(name: string, redirectToOrigin = true, tags?: string[]) {
async saveMap(name: string, redirectToOrigin = true, saveAsNew = true, tags?: string[]) {
await this.testSubjects.click('mapSaveButton');
await this.testSubjects.setValue('savedObjectTitle', name);
await this.visualize.setSaveModalValues(name, {
addToDashboard: false,
redirectToOrigin,
saveAsNew: true,
saveAsNew,
});
if (tags) {
await this.testSubjects.click('savedObjectTagSelector');

View file

@ -80,7 +80,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('allows to select tags for a new map', async () => {
await PageObjects.maps.saveMap('my-new-map', true, ['tag-1', 'tag-3']);
await PageObjects.maps.saveMap('my-new-map', true, true, ['tag-1', 'tag-3']);
await PageObjects.maps.gotoMapListingPage();
await selectFilterTags('tag-1');
@ -133,7 +133,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('allows to select tags for an existing map', async () => {
await listingTable.clickItemLink('map', 'map 4 (tag-1)');
await PageObjects.maps.saveMap('map 4 (tag-1)', true, ['tag-3']);
await PageObjects.maps.saveMap('map 4 (tag-1)', true, true, ['tag-3']);
await PageObjects.maps.gotoMapListingPage();
await selectFilterTags('tag-3');