add relationship test on Saved Objects (#59968)

* just a demo of function to return saved object table elements

* fix esArchive data, extend import objects test case for relationships

* improved data-test-subjs

* update snapshot for jest test

* unskip other half of the tests

* removed commented-out code

* use new findByTestSubject methods

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Lee Drengenberg 2020-03-23 19:18:35 +00:00 committed by GitHub
parent ef48205f15
commit afca33b520
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 125 additions and 78 deletions

View file

@ -53,6 +53,7 @@ exports[`Relationships should render dashboards normally 1`] = `
"width": "50px",
},
Object {
"data-test-subj": "directRelationship",
"dataType": "string",
"field": "relationship",
"name": "Direct relationship",
@ -72,6 +73,7 @@ exports[`Relationships should render dashboards normally 1`] = `
"actions": Array [
Object {
"available": [Function],
"data-test-subj": "relationshipsTableAction-inspect",
"description": "Inspect this saved object",
"icon": "inspect",
"name": "Inspect",
@ -117,6 +119,7 @@ exports[`Relationships should render dashboards normally 1`] = `
}
pagination={true}
responsive={true}
rowProps={[Function]}
search={
Object {
"filters": Array [
@ -263,6 +266,7 @@ exports[`Relationships should render index patterns normally 1`] = `
"width": "50px",
},
Object {
"data-test-subj": "directRelationship",
"dataType": "string",
"field": "relationship",
"name": "Direct relationship",
@ -282,6 +286,7 @@ exports[`Relationships should render index patterns normally 1`] = `
"actions": Array [
Object {
"available": [Function],
"data-test-subj": "relationshipsTableAction-inspect",
"description": "Inspect this saved object",
"icon": "inspect",
"name": "Inspect",
@ -327,6 +332,7 @@ exports[`Relationships should render index patterns normally 1`] = `
}
pagination={true}
responsive={true}
rowProps={[Function]}
search={
Object {
"filters": Array [
@ -429,6 +435,7 @@ exports[`Relationships should render searches normally 1`] = `
"width": "50px",
},
Object {
"data-test-subj": "directRelationship",
"dataType": "string",
"field": "relationship",
"name": "Direct relationship",
@ -448,6 +455,7 @@ exports[`Relationships should render searches normally 1`] = `
"actions": Array [
Object {
"available": [Function],
"data-test-subj": "relationshipsTableAction-inspect",
"description": "Inspect this saved object",
"icon": "inspect",
"name": "Inspect",
@ -493,6 +501,7 @@ exports[`Relationships should render searches normally 1`] = `
}
pagination={true}
responsive={true}
rowProps={[Function]}
search={
Object {
"filters": Array [
@ -595,6 +604,7 @@ exports[`Relationships should render visualizations normally 1`] = `
"width": "50px",
},
Object {
"data-test-subj": "directRelationship",
"dataType": "string",
"field": "relationship",
"name": "Direct relationship",
@ -614,6 +624,7 @@ exports[`Relationships should render visualizations normally 1`] = `
"actions": Array [
Object {
"available": [Function],
"data-test-subj": "relationshipsTableAction-inspect",
"description": "Inspect this saved object",
"icon": "inspect",
"name": "Inspect",
@ -659,6 +670,7 @@ exports[`Relationships should render visualizations normally 1`] = `
}
pagination={true}
responsive={true}
rowProps={[Function]}
search={
Object {
"filters": Array [

View file

@ -135,6 +135,7 @@ export class Relationships extends Component {
aria-label={getSavedObjectLabel(type)}
type={object.meta.icon || 'apps'}
size="s"
data-test-subj="relationshipsObjectType"
/>
</EuiToolTip>
);
@ -149,6 +150,7 @@ export class Relationships extends Component {
dataType: 'string',
sortable: false,
width: '125px',
'data-test-subj': 'directRelationship',
render: relationship => {
if (relationship === 'parent') {
return (
@ -187,10 +189,16 @@ export class Relationships extends Component {
const { path } = object.meta.inAppUrl || {};
const canGoInApp = this.props.canGoInApp(object);
if (!canGoInApp) {
return <EuiText size="s">{title || getDefaultTitle(object)}</EuiText>;
return (
<EuiText size="s" data-test-subj="relationshipsTitle">
{title || getDefaultTitle(object)}
</EuiText>
);
}
return (
<EuiLink href={chrome.addBasePath(path)}>{title || getDefaultTitle(object)}</EuiLink>
<EuiLink href={chrome.addBasePath(path)} data-test-subj="relationshipsTitle">
{title || getDefaultTitle(object)}
</EuiLink>
);
},
},
@ -211,6 +219,7 @@ export class Relationships extends Component {
),
type: 'icon',
icon: 'inspect',
'data-test-subj': 'relationshipsTableAction-inspect',
onClick: object => goInspectObject(object),
available: object => !!object.meta.editUrl,
},
@ -295,6 +304,9 @@ export class Relationships extends Component {
columns={columns}
pagination={true}
search={search}
rowProps={() => ({
'data-test-subj': `relationshipsTableRow`,
})}
/>
</div>
);

View file

@ -186,6 +186,7 @@ export class Table extends PureComponent {
aria-label={getSavedObjectLabel(type)}
type={object.meta.icon || 'apps'}
size="s"
data-test-subj="objectType"
/>
</EuiToolTip>
);

View file

@ -19,12 +19,14 @@
import expect from '@kbn/expect';
import path from 'path';
import { indexBy } from 'lodash';
export default function({ getService, getPageObjects }) {
const kibanaServer = getService('kibanaServer');
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['common', 'settings', 'header']);
const testSubjects = getService('testSubjects');
const log = getService('log');
describe('import objects', function describeIndexTests() {
describe('.ndjson file', () => {
@ -33,6 +35,7 @@ export default function({ getService, getPageObjects }) {
await kibanaServer.uiSettings.replace({});
await PageObjects.settings.navigateTo();
await esArchiver.load('management');
await PageObjects.settings.clickKibanaSavedObjects();
});
afterEach(async function() {
@ -40,20 +43,31 @@ export default function({ getService, getPageObjects }) {
});
it('should import saved objects', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects.ndjson')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('Log Agents');
expect(isSavedObjectImported).to.be(true);
// get all the elements in the table, and index them by the 'title' visible text field
const elements = indexBy(
await PageObjects.settings.getSavedObjectElementsInTable(),
'title'
);
log.debug("check that 'Log Agents' is in table as a visualization");
expect(elements['Log Agents'].objectType).to.eql('visualization');
await elements['logstash-*'].relationshipsElement.click();
const flyout = indexBy(await PageObjects.settings.getRelationshipFlyout(), 'title');
log.debug(
"check that 'Shared-Item Visualization AreaChart' shows 'logstash-*' as it's Parent"
);
expect(flyout['Shared-Item Visualization AreaChart'].relationship).to.eql('Parent');
log.debug("check that 'Log Agents' shows 'logstash-*' as it's Parent");
expect(flyout['Log Agents'].relationship).to.eql('Parent');
});
it('should provide dialog to allow the importing of saved objects with index pattern conflicts', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_conflicts.ndjson')
);
@ -65,15 +79,12 @@ export default function({ getService, getPageObjects }) {
await PageObjects.settings.clickConfirmChanges();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object with index pattern conflict');
expect(isSavedObjectImported).to.be(true);
});
it('should allow the user to override duplicate saved objects', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
// This data has already been loaded by the "visualize" esArchive. We'll load it again
// so that we can override the existing visualization.
await PageObjects.settings.importFile(
@ -93,8 +104,6 @@ export default function({ getService, getPageObjects }) {
});
it('should allow the user to cancel overriding duplicate saved objects', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
// This data has already been loaded by the "visualize" esArchive. We'll load it again
// so that we can be prompted to override the existing visualization.
await PageObjects.settings.importFile(
@ -114,21 +123,17 @@ export default function({ getService, getPageObjects }) {
});
it('should import saved objects linked to saved searches', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_saved_search.ndjson')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.ndjson')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object connected to saved search');
@ -136,14 +141,11 @@ export default function({ getService, getPageObjects }) {
});
it('should not import saved objects linked to saved searches when saved search does not exist', async function() {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.ndjson')
);
await PageObjects.settings.checkNoneImported();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object connected to saved search');
@ -151,12 +153,13 @@ export default function({ getService, getPageObjects }) {
});
it('should not import saved objects linked to saved searches when saved search index pattern does not exist', async function() {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.removeLogstashIndexPatternIfExist();
const elements = indexBy(
await PageObjects.settings.getSavedObjectElementsInTable(),
'title'
);
await elements['logstash-*'].checkbox.click();
await PageObjects.settings.clickSavedObjectsDelete();
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_with_saved_search.ndjson')
);
@ -164,7 +167,6 @@ export default function({ getService, getPageObjects }) {
await PageObjects.settings.checkImportConflictsWarning();
await PageObjects.settings.clickConfirmChanges();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object connected to saved search');
@ -173,14 +175,11 @@ export default function({ getService, getPageObjects }) {
it('should import saved objects with index patterns when index patterns already exists', async () => {
// First, import the objects
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_with_index_patterns.ndjson')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
// Wait for all the saves to happen
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object imported with index pattern');
@ -189,19 +188,19 @@ export default function({ getService, getPageObjects }) {
it('should import saved objects with index patterns when index patterns does not exists', async () => {
// First, we need to delete the index pattern
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.removeLogstashIndexPatternIfExist();
const elements = indexBy(
await PageObjects.settings.getSavedObjectElementsInTable(),
'title'
);
await elements['logstash-*'].checkbox.click();
await PageObjects.settings.clickSavedObjectsDelete();
// Then, import the objects
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_with_index_patterns.ndjson')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
// Wait for all the saves to happen
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object imported with index pattern');
@ -215,6 +214,7 @@ export default function({ getService, getPageObjects }) {
await kibanaServer.uiSettings.replace({});
await PageObjects.settings.navigateTo();
await esArchiver.load('management');
await PageObjects.settings.clickKibanaSavedObjects();
});
afterEach(async function() {
@ -222,20 +222,17 @@ export default function({ getService, getPageObjects }) {
});
it('should import saved objects', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects.json')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('Log Agents');
expect(isSavedObjectImported).to.be(true);
});
it('should provide dialog to allow the importing of saved objects with index pattern conflicts', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects-conflicts.json')
);
@ -248,15 +245,12 @@ export default function({ getService, getPageObjects }) {
await PageObjects.settings.clickConfirmChanges();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object with index pattern conflict');
expect(isSavedObjectImported).to.be(true);
});
it('should allow the user to override duplicate saved objects', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
// This data has already been loaded by the "visualize" esArchive. We'll load it again
// so that we can override the existing visualization.
await PageObjects.settings.importFile(
@ -277,8 +271,6 @@ export default function({ getService, getPageObjects }) {
});
it('should allow the user to cancel overriding duplicate saved objects', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
// This data has already been loaded by the "visualize" esArchive. We'll load it again
// so that we can be prompted to override the existing visualization.
await PageObjects.settings.importFile(
@ -299,21 +291,17 @@ export default function({ getService, getPageObjects }) {
});
it('should import saved objects linked to saved searches', async function() {
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_saved_search.json')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.json')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object connected to saved search');
@ -321,14 +309,11 @@ export default function({ getService, getPageObjects }) {
});
it('should not import saved objects linked to saved searches when saved search does not exist', async function() {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.json')
);
await PageObjects.settings.checkImportFailedWarning();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object connected to saved search');
@ -337,7 +322,6 @@ export default function({ getService, getPageObjects }) {
it('should not import saved objects linked to saved searches when saved search index pattern does not exist', async function() {
// First, import the saved search
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_saved_search.json')
);
@ -346,21 +330,21 @@ export default function({ getService, getPageObjects }) {
await PageObjects.settings.clickImportDone();
// Second, we need to delete the index pattern
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.removeLogstashIndexPatternIfExist();
const elements = indexBy(
await PageObjects.settings.getSavedObjectElementsInTable(),
'title'
);
await elements['logstash-*'].checkbox.click();
await PageObjects.settings.clickSavedObjectsDelete();
// Last, import a saved object connected to the saved search
// This should NOT show the conflicts
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.json')
);
// Wait for all the saves to happen
await PageObjects.settings.checkNoneImported();
await PageObjects.settings.clickImportDone();
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object connected to saved search');
@ -369,14 +353,11 @@ export default function({ getService, getPageObjects }) {
it('should import saved objects with index patterns when index patterns already exists', async () => {
// First, import the objects
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_with_index_patterns.json')
);
await PageObjects.settings.checkImportFailedWarning();
await PageObjects.settings.clickImportDone();
// Wait for all the saves to happen
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object imported with index pattern');
@ -385,19 +366,19 @@ export default function({ getService, getPageObjects }) {
it('should import saved objects with index patterns when index patterns does not exists', async () => {
// First, we need to delete the index pattern
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.removeLogstashIndexPatternIfExist();
const elements = indexBy(
await PageObjects.settings.getSavedObjectElementsInTable(),
'title'
);
await elements['logstash-*'].checkbox.click();
await PageObjects.settings.clickSavedObjectsDelete();
// Then, import the objects
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(
path.join(__dirname, 'exports', '_import_objects_with_index_patterns.json')
);
await PageObjects.settings.checkImportSucceeded();
await PageObjects.settings.clickImportDone();
// Wait for all the saves to happen
await PageObjects.settings.waitUntilSavedObjectsTableIsNotLoading();
const objects = await PageObjects.settings.getSavedObjectsInTable();
const isSavedObjectImported = objects.includes('saved object imported with index pattern');

View file

@ -47,6 +47,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
async clickKibanaSavedObjects() {
await testSubjects.click('objects');
await this.waitUntilSavedObjectsTableIsNotLoading();
}
async clickKibanaIndexPatterns() {
@ -648,6 +649,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
async clickImportDone() {
await testSubjects.click('importSavedObjectsDoneBtn');
await this.waitUntilSavedObjectsTableIsNotLoading();
}
async clickConfirmChanges() {
@ -681,9 +683,38 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
});
}
async getSavedObjectElementsInTable() {
const rows = await testSubjects.findAll('~savedObjectsTableRow');
return mapAsync(rows, async row => {
const checkbox = await row.findByCssSelector('[data-test-subj*="checkboxSelectRow"]');
// return the object type aria-label="index patterns"
const objectType = await row.findByTestSubject('objectType');
const titleElement = await row.findByTestSubject('savedObjectsTableRowTitle');
// not all rows have inspect button - Advanced Settings objects don't
let inspectElement;
const innerHtml = await row.getAttribute('innerHTML');
if (innerHtml.includes('Inspect')) {
inspectElement = await row.findByTestSubject('savedObjectsTableAction-inspect');
} else {
inspectElement = null;
}
const relationshipsElement = await row.findByTestSubject(
'savedObjectsTableAction-relationships'
);
return {
checkbox,
objectType: await objectType.getAttribute('aria-label'),
titleElement,
title: await titleElement.getVisibleText(),
inspectElement,
relationshipsElement,
};
});
}
async getSavedObjectsInTable() {
const table = await testSubjects.find('savedObjectsTable');
const cells = await table.findAllByCssSelector('td:nth-child(3)');
const cells = await table.findAllByTestSubject('savedObjectsTableRowTitle');
const objects = [];
for (const cell of cells) {
@ -693,6 +724,23 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
return objects;
}
async getRelationshipFlyout() {
const rows = await testSubjects.findAll('relationshipsTableRow');
return mapAsync(rows, async row => {
const objectType = await row.findByTestSubject('relationshipsObjectType');
const relationship = await row.findByTestSubject('directRelationship');
const titleElement = await row.findByTestSubject('relationshipsTitle');
const inspectElement = await row.findByTestSubject('relationshipsTableAction-inspect');
return {
objectType: await objectType.getAttribute('aria-label'),
relationship: await relationship.getVisibleText(),
titleElement,
title: await titleElement.getVisibleText(),
inspectElement,
};
});
}
async getSavedObjectsTableSummary() {
const table = await testSubjects.find('savedObjectsTable');
const rows = await table.findAllByCssSelector('tbody tr');
@ -723,17 +771,10 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
return await deleteButton.isEnabled();
}
async canSavedObjectBeDeleted(id: string) {
const allCheckBoxes = await testSubjects.findAll('checkboxSelectRow*');
for (const checkBox of allCheckBoxes) {
if (await checkBox.isSelected()) {
await checkBox.click();
}
}
const checkBox = await testSubjects.find(`checkboxSelectRow-${id}`);
await checkBox.click();
return await this.canSavedObjectsBeDeleted();
async clickSavedObjectsDelete() {
await testSubjects.click('savedObjectsManagementDelete');
await testSubjects.click('confirmModalConfirmButton');
await this.waitUntilSavedObjectsTableIsNotLoading();
}
}