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',