diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index ba928931f303a..a824aa7ea29e2 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -73,6 +73,7 @@ enabled: - test/functional/apps/discover/config.ts - test/functional/apps/getting_started/config.ts - test/functional/apps/home/config.ts + - test/functional/apps/kibana_overview/config.ts - test/functional/apps/management/config.ts - test/functional/apps/saved_objects_management/config.ts - test/functional/apps/status_page/config.ts diff --git a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap index c185e1b85ae1a..a5b79401d0b46 100644 --- a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap +++ b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap @@ -1,22 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Overview during loading 1`] = ` -
-
- -
-
-`; - -exports[`Overview render 1`] = ` +exports[`Overview renders correctly 1`] = ` `; -exports[`Overview when there is no user data view 1`] = ` +exports[`Overview renders correctly when there is no user data view 1`] = ` `; -exports[`Overview without features 1`] = ` +exports[`Overview renders correctly without features 1`] = ` `; -exports[`Overview without solutions 1`] = ` +exports[`Overview renders correctly without solutions 1`] = ` `; + +exports[`Overview show loading spinner during loading 1`] = ` +
+
+ +
+
+`; diff --git a/src/plugins/kibana_overview/public/components/overview/overview.test.tsx b/src/plugins/kibana_overview/public/components/overview/overview.test.tsx index b433e7a39da13..99d8b45cdf27b 100644 --- a/src/plugins/kibana_overview/public/components/overview/overview.test.tsx +++ b/src/plugins/kibana_overview/public/components/overview/overview.test.tsx @@ -166,7 +166,7 @@ describe('Overview', () => { afterAll(() => jest.clearAllMocks()); - test('render', async () => { + test('renders correctly', async () => { const component = mountWithIntl( { expect(component.find(KibanaPageTemplate).length).toBe(1); }); - test('without solutions', async () => { + test('renders correctly without solutions', async () => { const component = mountWithIntl( ); @@ -191,7 +191,7 @@ describe('Overview', () => { expect(component).toMatchSnapshot(); }); - test('without features', async () => { + test('renders correctly without features', async () => { const component = mountWithIntl( ); @@ -201,7 +201,7 @@ describe('Overview', () => { expect(component).toMatchSnapshot(); }); - test('when there is no user data view', async () => { + test('renders correctly when there is no user data view', async () => { hasESData.mockResolvedValue(true); hasUserDataView.mockResolvedValue(false); @@ -221,7 +221,7 @@ describe('Overview', () => { expect(component.find(EuiLoadingSpinner).length).toBe(0); }); - test('during loading', async () => { + test('show loading spinner during loading', async () => { hasESData.mockImplementation(() => new Promise(() => {})); hasUserDataView.mockImplementation(() => new Promise(() => {})); diff --git a/src/plugins/kibana_overview/public/components/overview/overview.tsx b/src/plugins/kibana_overview/public/components/overview/overview.tsx index 2258c662fe94e..738b278b17b36 100644 --- a/src/plugins/kibana_overview/public/components/overview/overview.tsx +++ b/src/plugins/kibana_overview/public/components/overview/overview.tsx @@ -22,11 +22,11 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { CoreStart } from '@kbn/core/public'; import { useKibana, - KibanaPageTemplateSolutionNavAvatar, overviewPageActions, OverviewPageFooter, } from '@kbn/kibana-react-plugin/public'; import { KibanaPageTemplate } from '@kbn/shared-ux-components'; +import { KibanaSolutionAvatar } from '@kbn/shared-ux-avatar-solution'; import { AnalyticsNoDataPageKibanaProvider, AnalyticsNoDataPage, @@ -298,13 +298,7 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) => className={`kbnOverviewSolution ${id}`} description={description ? description : ''} href={addBasePath(path)} - icon={ - - } + icon={} image={addBasePath(getSolutionGraphicURL(snakeCase(id)))} title={title} titleElement="h3" diff --git a/test/functional/apps/kibana_overview/_analytics.ts b/test/functional/apps/kibana_overview/_analytics.ts new file mode 100644 index 0000000000000..296256d924b24 --- /dev/null +++ b/test/functional/apps/kibana_overview/_analytics.ts @@ -0,0 +1,63 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { WebElementWrapper } from '../../services/lib/web_element_wrapper'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'header']); + + describe('overview page - Analytics apps', function describeIndexTests() { + before(async () => { + await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + await PageObjects.common.navigateToUrl('kibana_overview', '', { useActualUrl: true }); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + }); + + const apps = ['dashboard', 'discover', 'canvas', 'maps', 'ml']; + + it('should display Analytics apps cards', async () => { + const kbnOverviewAppsCards = await find.allByCssSelector('.kbnOverviewApps__item'); + expect(kbnOverviewAppsCards.length).to.be(apps.length); + + const verifyImageUrl = async (el: WebElementWrapper, imgName: string) => { + const image = await el.findByCssSelector('img'); + const imageUrl = await image.getAttribute('src'); + expect(imageUrl.includes(imgName)).to.be(true); + }; + + for (let i = 0; i < apps.length; i++) { + verifyImageUrl(kbnOverviewAppsCards[i], `kibana_${apps[i]}_light.svg`); + } + }); + + it('click on a card should lead to the appropriate app', async () => { + const kbnOverviewAppsCards = await find.allByCssSelector('.kbnOverviewApps__item'); + const dashboardCard = kbnOverviewAppsCards.at(0); + expect(dashboardCard).not.to.be(undefined); + if (dashboardCard) { + await dashboardCard.click(); + await PageObjects.common.waitUntilUrlIncludes('app/dashboards'); + } + }); + }); +} diff --git a/test/functional/apps/kibana_overview/_footer.ts b/test/functional/apps/kibana_overview/_footer.ts new file mode 100644 index 0000000000000..c44d399154f14 --- /dev/null +++ b/test/functional/apps/kibana_overview/_footer.ts @@ -0,0 +1,55 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const esArchiver = getService('esArchiver'); + const retry = getService('retry'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'header']); + + const defaultSettings = { + default_route: 'app/home', + }; + + describe('overview page - footer', function describeIndexTests() { + before(async () => { + await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + await PageObjects.common.navigateToUrl('kibana_overview', '', { useActualUrl: true }); + await PageObjects.header.waitUntilLoadingHasFinished(); + await kibanaServer.uiSettings.replace(defaultSettings); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + await kibanaServer.uiSettings.replace(defaultSettings); + }); + + it('clicking footer updates landing page', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + let footerButton = await find.byCssSelector('.kbnOverviewPageFooter__button'); + await footerButton.click(); + + await retry.try(async () => { + footerButton = await find.byCssSelector('.kbnOverviewPageFooter__button'); + const text = await ( + await footerButton.findByCssSelector('.euiButtonEmpty__text') + ).getVisibleText(); + expect(text.toString().includes('Display a different page on log in')).to.be(true); + }); + }); + }); +} diff --git a/test/functional/apps/kibana_overview/_no_data.ts b/test/functional/apps/kibana_overview/_no_data.ts new file mode 100644 index 0000000000000..8dec616eb8afe --- /dev/null +++ b/test/functional/apps/kibana_overview/_no_data.ts @@ -0,0 +1,42 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const testSubjects = getService('testSubjects'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'header']); + + describe('overview page - no data', function describeIndexTests() { + before(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + kibanaServer.savedObjects.clean({ types: ['index-pattern'] }); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + await PageObjects.common.navigateToUrl('kibana_overview', '', { useActualUrl: true }); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + it('should display no data page', async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + const exists = await find.byClassName('kbnNoDataPageContents'); + expect(exists).not.to.be(undefined); + }); + + it('click on add data opens integrations', async () => { + const addIntegrations = await testSubjects.find('kbnOverviewAddIntegrations'); + await addIntegrations.click(); + await PageObjects.common.waitUntilUrlIncludes('integrations/browse'); + }); + }); +} diff --git a/test/functional/apps/kibana_overview/_page_header.ts b/test/functional/apps/kibana_overview/_page_header.ts new file mode 100644 index 0000000000000..8c254948676fc --- /dev/null +++ b/test/functional/apps/kibana_overview/_page_header.ts @@ -0,0 +1,69 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'header', 'dashboard']); + + describe('overview page - page header', function describeIndexTests() { + before(async () => { + await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + await PageObjects.common.navigateToUrl('kibana_overview', '', { useActualUrl: true }); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + }); + + it('click on integrations leads to integrations', async () => { + const headerItems = await find.byCssSelector('.euiPageHeaderContent__rightSideItems'); + const items = await headerItems.findAllByCssSelector('.kbnRedirectCrossAppLinks'); + expect(items!.length).to.be(3); + + const integrations = await items!.at(0); + await integrations!.click(); + await PageObjects.common.waitUntilUrlIncludes('app/integrations/browse'); + }); + + it('click on management leads to management', async () => { + await PageObjects.common.navigateToUrl('kibana_overview', '', { useActualUrl: true }); + await PageObjects.header.waitUntilLoadingHasFinished(); + + const headerItems = await find.byCssSelector('.euiPageHeaderContent__rightSideItems'); + const items = await headerItems.findAllByCssSelector('.kbnRedirectCrossAppLinks'); + + const management = await items!.at(1); + await management!.click(); + await PageObjects.common.waitUntilUrlIncludes('app/management'); + }); + + it('click on dev tools leads to dev tools', async () => { + await PageObjects.common.navigateToUrl('kibana_overview', '', { useActualUrl: true }); + await PageObjects.header.waitUntilLoadingHasFinished(); + + const headerItems = await find.byCssSelector('.euiPageHeaderContent__rightSideItems'); + const items = await headerItems.findAllByCssSelector('.kbnRedirectCrossAppLinks'); + + const devTools = await items!.at(2); + await devTools!.click(); + await PageObjects.common.waitUntilUrlIncludes('app/dev_tools'); + }); + }); +} diff --git a/test/functional/apps/kibana_overview/_solutions.ts b/test/functional/apps/kibana_overview/_solutions.ts new file mode 100644 index 0000000000000..97af7f7242eff --- /dev/null +++ b/test/functional/apps/kibana_overview/_solutions.ts @@ -0,0 +1,72 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const retry = getService('retry'); + const PageObjects = getPageObjects(['common', 'header']); + + describe('overview page - solutions', function describeIndexTests() { + before(async () => { + await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + await PageObjects.common.navigateToUrl('kibana_overview', '', { useActualUrl: true }); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + after(async () => { + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/kibana_sample_data_flights_index_pattern' + ); + }); + + it('contains the appropriate solutions', async () => { + const solutionCards = await find.allByCssSelector('.kbnOverviewMore__item'); + expect(solutionCards.length).to.be(2); + + const observabilityImage = await solutionCards[0].findByCssSelector('img'); + const observabilityImageUrl = await observabilityImage.getAttribute('src'); + expect(observabilityImageUrl.includes('/solutions_observability.svg')).to.be(true); + + const securityImage = await solutionCards[1].findByCssSelector('img'); + const securityImageUrl = await securityImage.getAttribute('src'); + expect(securityImageUrl.includes('/solutions_security_solution.svg')).to.be(true); + }); + + it('click on Observability card leads to Observability', async () => { + let solutionCards: string | any[] = []; + await retry.waitForWithTimeout('all solutions to be present', 5000, async () => { + solutionCards = await find.allByCssSelector('.kbnOverviewMore__item'); + return solutionCards.length === 2; + }); + await solutionCards[0].click(); + await PageObjects.common.waitUntilUrlIncludes('app/observability'); + }); + + it('click on Security card leads to Security', async () => { + await PageObjects.common.navigateToUrl('kibana_overview', '', { useActualUrl: true }); + await PageObjects.header.waitUntilLoadingHasFinished(); + + let solutionCards: string | any[] = []; + await retry.waitForWithTimeout('all solutions to be present', 5000, async () => { + solutionCards = await find.allByCssSelector('.kbnOverviewMore__item'); + return solutionCards.length === 2; + }); + await solutionCards[1].click(); + await PageObjects.common.waitUntilUrlIncludes('app/security'); + }); + }); +} diff --git a/test/functional/apps/kibana_overview/config.ts b/test/functional/apps/kibana_overview/config.ts new file mode 100644 index 0000000000000..e487d31dcb657 --- /dev/null +++ b/test/functional/apps/kibana_overview/config.ts @@ -0,0 +1,18 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../../config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + }; +} diff --git a/test/functional/apps/kibana_overview/index.js b/test/functional/apps/kibana_overview/index.js new file mode 100644 index 0000000000000..40dd47f6b69ef --- /dev/null +++ b/test/functional/apps/kibana_overview/index.js @@ -0,0 +1,23 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export default function ({ getService, loadTestFile }) { + const browser = getService('browser'); + + describe('kibana overview app', function () { + before(function () { + return browser.setWindowSize(1200, 800); + }); + + loadTestFile(require.resolve('./_no_data')); + loadTestFile(require.resolve('./_page_header')); + loadTestFile(require.resolve('./_analytics')); + loadTestFile(require.resolve('./_solutions')); + loadTestFile(require.resolve('./_footer')); + }); +} diff --git a/test/functional/config.base.js b/test/functional/config.base.js index 147fef2685f5d..f7f210aa7de32 100644 --- a/test/functional/config.base.js +++ b/test/functional/config.base.js @@ -90,6 +90,9 @@ export default async function ({ readConfigFile }) { integrations: { pathname: '/app/integrations', }, + kibana_overview: { + pathname: '/app/kibana_overview', + }, }, junit: { reportName: 'Chrome UI Functional Tests',