diff --git a/test/functional/apps/management/_import_objects.js b/test/functional/apps/management/_import_objects.js index 03db3a2b108f..5fbeb978f9a1 100644 --- a/test/functional/apps/management/_import_objects.js +++ b/test/functional/apps/management/_import_objects.js @@ -49,12 +49,13 @@ export default function ({ getService, getPageObjects }) { await PageObjects.savedObjects.checkImportSucceeded(); await PageObjects.savedObjects.clickImportDone(); - // get all the elements in the table, and index them by the 'title' visible text field - const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title'); log.debug("check that 'Log Agents' is in table as a visualization"); - expect(elements['Log Agents'].objectType).to.eql('visualization'); + expect(await PageObjects.savedObjects.getObjectTypeByTitle('Log Agents')).to.eql( + 'visualization' + ); + + await PageObjects.savedObjects.clickRelationshipsByTitle('logstash-*'); - await elements['logstash-*'].relationshipsElement.click(); const flyout = keyBy(await PageObjects.savedObjects.getRelationshipFlyout(), 'title'); log.debug( "check that 'Shared-Item Visualization AreaChart' shows 'logstash-*' as it's Parent" @@ -150,8 +151,7 @@ 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 () { - const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title'); - await elements['logstash-*'].checkbox.click(); + await PageObjects.savedObjects.clickCheckboxByTitle('logstash-*'); await PageObjects.savedObjects.clickDelete(); await PageObjects.savedObjects.importFile( @@ -182,8 +182,7 @@ 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 - const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title'); - await elements['logstash-*'].checkbox.click(); + await PageObjects.savedObjects.clickCheckboxByTitle('logstash-*'); await PageObjects.savedObjects.clickDelete(); // Then, import the objects @@ -321,8 +320,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.savedObjects.clickImportDone(); // Second, we need to delete the index pattern - const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title'); - await elements['logstash-*'].checkbox.click(); + await PageObjects.savedObjects.clickCheckboxByTitle('logstash-*'); await PageObjects.savedObjects.clickDelete(); // Last, import a saved object connected to the saved search @@ -353,8 +351,7 @@ 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 - const elements = keyBy(await PageObjects.savedObjects.getElementsInTable(), 'title'); - await elements['logstash-*'].checkbox.click(); + await PageObjects.savedObjects.clickCheckboxByTitle('logstash-*'); await PageObjects.savedObjects.clickDelete(); // Then, import the objects diff --git a/test/functional/page_objects/management/saved_objects_page.ts b/test/functional/page_objects/management/saved_objects_page.ts index 03d21aa4aa52..ad82ea9b6fbc 100644 --- a/test/functional/page_objects/management/saved_objects_page.ts +++ b/test/functional/page_objects/management/saved_objects_page.ts @@ -17,6 +17,7 @@ * under the License. */ +import { keyBy } from 'lodash'; import { map as mapAsync } from 'bluebird'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -34,6 +35,8 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv await searchBox.clearValue(); await searchBox.type(objectName); await searchBox.pressKeys(browser.keys.ENTER); + await PageObjects.header.waitUntilLoadingHasFinished(); + await this.waitTableIsLoaded(); } async importFile(path: string, overwriteAll = true) { @@ -99,6 +102,56 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv }); } + async clickRelationshipsByTitle(title: string) { + const table = keyBy(await this.getElementsInTable(), 'title'); + // should we check if table size > 0 and log error if not? + if (table[title].menuElement) { + log.debug(`we found a context menu element for (${title}) so click it`); + await table[title].menuElement?.click(); + // Wait for context menu to render + const menuPanel = await find.byCssSelector('.euiContextMenuPanel'); + await (await menuPanel.findByTestSubject('savedObjectsTableAction-relationships')).click(); + } else { + log.debug( + `we didn't find a menu element so should be a relastionships element for (${title}) to click` + ); + // or the action elements are on the row without the menu + await table[title].relationshipsElement?.click(); + } + } + + async clickCopyToSpaceByTitle(title: string) { + const table = keyBy(await this.getElementsInTable(), 'title'); + // should we check if table size > 0 and log error if not? + if (table[title].menuElement) { + log.debug(`we found a context menu element for (${title}) so click it`); + await table[title].menuElement?.click(); + // Wait for context menu to render + const menuPanel = await find.byCssSelector('.euiContextMenuPanel'); + await ( + await menuPanel.findByTestSubject('savedObjectsTableAction-copy_saved_objects_to_space') + ).click(); + } else { + log.debug( + `we didn't find a menu element so should be a "copy to space" element for (${title}) to click` + ); + // or the action elements are on the row without the menu + await table[title].copySaveObjectsElement?.click(); + } + } + + async clickCheckboxByTitle(title: string) { + const table = keyBy(await this.getElementsInTable(), 'title'); + // should we check if table size > 0 and log error if not? + await table[title].checkbox.click(); + } + + async getObjectTypeByTitle(title: string) { + const table = keyBy(await this.getElementsInTable(), 'title'); + // should we check if table size > 0 and log error if not? + return table[title].objectType; + } + async getElementsInTable() { const rows = await testSubjects.findAll('~savedObjectsTableRow'); return mapAsync(rows, async (row) => { @@ -107,23 +160,45 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv 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; + // Advanced Settings has 2 actions, + // data-test-subj="savedObjectsTableAction-relationships" + // data-test-subj="savedObjectsTableAction-copy_saved_objects_to_space" + // Some other objects have the ... + // data-test-subj="euiCollapsedItemActionsButton" + // Maybe some objects still have the inspect element visible? + // !!! Also note that since we don't have spaces on OSS, the actions for the same object can be different depending on OSS or not + let menuElement = null; + let inspectElement = null; + let relationshipsElement = null; + let copySaveObjectsElement = null; + const actions = await row.findByClassName('euiTableRowCell--hasActions'); + // getting the innerHTML and checking if it 'includes' a string is faster than a timeout looking for each element + const actionsHTML = await actions.getAttribute('innerHTML'); + if (actionsHTML.includes('euiCollapsedItemActionsButton')) { + menuElement = await row.findByTestSubject('euiCollapsedItemActionsButton'); + } + if (actionsHTML.includes('savedObjectsTableAction-inspect')) { + inspectElement = await row.findByTestSubject('savedObjectsTableAction-inspect'); + } + if (actionsHTML.includes('savedObjectsTableAction-relationships')) { + relationshipsElement = await row.findByTestSubject( + 'savedObjectsTableAction-relationships' + ); + } + if (actionsHTML.includes('savedObjectsTableAction-copy_saved_objects_to_space')) { + copySaveObjectsElement = await row.findByTestSubject( + 'savedObjectsTableAction-copy_saved_objects_to_space' + ); } - const relationshipsElement = await row.findByTestSubject( - 'savedObjectsTableAction-relationships' - ); return { checkbox, objectType: await objectType.getAttribute('aria-label'), titleElement, title: await titleElement.getVisibleText(), + menuElement, inspectElement, relationshipsElement, + copySaveObjectsElement, }; }); } diff --git a/x-pack/test/functional/apps/spaces/copy_saved_objects.ts b/x-pack/test/functional/apps/spaces/copy_saved_objects.ts index 074b6fc52815..05d497c235da 100644 --- a/x-pack/test/functional/apps/spaces/copy_saved_objects.ts +++ b/x-pack/test/functional/apps/spaces/copy_saved_objects.ts @@ -15,8 +15,7 @@ export default function spaceSelectorFunctonalTests({ const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['security', 'settings', 'copySavedObjectsToSpace']); - // TODO: Flakey again https://github.com/elastic/kibana/issues/44575#issuecomment-528864287 - describe.skip('Copy Saved Objects to Space', function () { + describe('Copy Saved Objects to Space', function () { before(async () => { await esArchiver.load('spaces/copy_saved_objects'); @@ -32,6 +31,7 @@ export default function spaceSelectorFunctonalTests({ disabledFeatures: [], }); + await PageObjects.security.forceLogout(); await PageObjects.security.login(undefined, undefined, { expectSpaceSelector: true, }); diff --git a/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts b/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts index 03596aa68dbc..629a86520389 100644 --- a/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts +++ b/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts @@ -15,31 +15,15 @@ export function CopySavedObjectsToSpacePageProvider({ getPageObjects, }: FtrProviderContext) { const testSubjects = getService('testSubjects'); - const find = getService('find'); - const { savedObjects } = getPageObjects(['savedObjects']); + const { savedObjects, common } = getPageObjects(['savedObjects', 'common']); return { async openCopyToSpaceFlyoutForObject(objectName: string) { + // This searchForObject narrows down the objects to those matching ANY of the words in the objectName. + // Hopefully the one we want is on the first page of results. await savedObjects.searchForObject(objectName); - - // Click action button to show context menu - await find.clickByCssSelector( - 'table.euiTable tbody tr.euiTableRow td.euiTableRowCell:last-child .euiButtonIcon' - ); - - // Wait for context menu to render - await find.existsByCssSelector('.euiContextMenuPanel'); - - const actions = await find.allByCssSelector('.euiContextMenuItem'); - - for (const action of actions) { - const actionText = await action.getVisibleText(); - if (actionText === 'Copy to space') { - await action.click(); - break; - } - } - + await common.sleep(1000); + await savedObjects.clickCopyToSpaceByTitle(objectName); await testSubjects.existOrFail('copy-to-space-flyout'); },