diff --git a/.buildkite/ftr_platform_stateful_configs.yml b/.buildkite/ftr_platform_stateful_configs.yml index bc564624f8a5e..d8a538f82779e 100644 --- a/.buildkite/ftr_platform_stateful_configs.yml +++ b/.buildkite/ftr_platform_stateful_configs.yml @@ -272,6 +272,7 @@ enabled: - x-pack/test/functional/config.upgrade_assistant.ts - x-pack/test/functional_cloud/config.ts - x-pack/test/functional_solution_sidenav/config.ts + - x-pack/test/functional_search/config.ts - x-pack/test/kubernetes_security/basic/config.ts - x-pack/test/licensing_plugin/config.public.ts - x-pack/test/licensing_plugin/config.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d4c8f52704e96..47911e49ee1a4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1480,6 +1480,7 @@ x-pack/test/api_integration/apis/management/index_management/inference_endpoints /x-pack/test_serverless/api_integration/test_suites/search @elastic/search-kibana /x-pack/test_serverless/functional/page_objects/svl_api_keys.ts @elastic/search-kibana /x-pack/test_serverless/functional/page_objects/svl_search_* @elastic/search-kibana +/x-pack/test/functional_search/ @elastic/search-kibana # Management Experience - Deployment Management /x-pack/test_serverless/**/test_suites/common/index_management/ @elastic/kibana-management diff --git a/test/functional/page_objects/solution_navigation.ts b/test/functional/page_objects/solution_navigation.ts index a0544e1100507..79e13b0f24943 100644 --- a/test/functional/page_objects/solution_navigation.ts +++ b/test/functional/page_objects/solution_navigation.ts @@ -138,6 +138,18 @@ export function SolutionNavigationProvider(ctx: Pick false, + getIsActive: ({ pathNameSerialized, prepend }) => { + return pathNameSerialized.startsWith( + prepend('/app/enterprise_search/app_search') + ); + }, link: 'appSearch:engines', title: i18n.translate( 'xpack.enterpriseSearch.searchNav.entsearch.appSearch', @@ -235,7 +239,11 @@ export const getNavigationTreeDefinition = ({ : {}), }, { - getIsActive: () => false, + getIsActive: ({ pathNameSerialized, prepend }) => { + return pathNameSerialized.startsWith( + prepend('/app/enterprise_search/workplace_search') + ); + }, link: 'workplaceSearch', ...(workplaceSearch ? { diff --git a/x-pack/test/functional_search/config.ts b/x-pack/test/functional_search/config.ts new file mode 100644 index 0000000000000..f997aaea7c5e2 --- /dev/null +++ b/x-pack/test/functional_search/config.ts @@ -0,0 +1,31 @@ +/* + * 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 { FtrConfigProviderContext } from '@kbn/test'; + +/** + * NOTE: The solution view is currently only available in the cloud environment. + * This test suite fakes a cloud environement by setting the cloud.id and cloud.base_url + */ + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js')); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('.')], + kbnTestServer: { + ...functionalConfig.get('kbnTestServer'), + serverArgs: [ + ...functionalConfig.get('kbnTestServer.serverArgs'), + // Note: the base64 string in the cloud.id config contains the ES endpoint required in the functional tests + '--xpack.cloud.id=ftr_fake_cloud_id:aGVsbG8uY29tOjQ0MyRFUzEyM2FiYyRrYm4xMjNhYmM=', + '--xpack.cloud.base_url=https://cloud.elastic.co', + ], + }, + }; +} diff --git a/x-pack/test/functional_search/ftr_provider_context.ts b/x-pack/test/functional_search/ftr_provider_context.ts new file mode 100644 index 0000000000000..d6c0afa5ceffd --- /dev/null +++ b/x-pack/test/functional_search/ftr_provider_context.ts @@ -0,0 +1,13 @@ +/* + * 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 { GenericFtrProviderContext } from '@kbn/test'; +import { pageObjects } from '../functional/page_objects'; +import { services } from './services'; + +export type FtrProviderContext = GenericFtrProviderContext; +export { pageObjects }; diff --git a/x-pack/test/functional_search/index.ts b/x-pack/test/functional_search/index.ts new file mode 100644 index 0000000000000..149b3dbcf7eca --- /dev/null +++ b/x-pack/test/functional_search/index.ts @@ -0,0 +1,15 @@ +/* + * 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. + */ +/* eslint-disable import/no-default-export */ + +import { FtrProviderContext } from './ftr_provider_context'; + +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Search solution tests', function () { + loadTestFile(require.resolve('./tests/solution_navigation')); + }); +}; diff --git a/x-pack/test/functional_search/services.ts b/x-pack/test/functional_search/services.ts new file mode 100644 index 0000000000000..9508ce5eba16d --- /dev/null +++ b/x-pack/test/functional_search/services.ts @@ -0,0 +1,10 @@ +/* + * 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 { services as functionalServices } from '../functional/services'; + +export const services = functionalServices; diff --git a/x-pack/test/functional_search/tests/solution_navigation.ts b/x-pack/test/functional_search/tests/solution_navigation.ts new file mode 100644 index 0000000000000..8a06ad1193372 --- /dev/null +++ b/x-pack/test/functional_search/tests/solution_navigation.ts @@ -0,0 +1,314 @@ +/* + * 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 searchSolutionNavigation({ + getPageObjects, + getService, +}: FtrProviderContext) { + const { common, solutionNavigation } = getPageObjects(['common', 'solutionNavigation']); + const spaces = getService('spaces'); + const browser = getService('browser'); + + describe('Search Solution Navigation', () => { + let cleanUp: () => Promise; + let spaceCreated: { id: string } = { id: '' }; + + before(async () => { + // Navigate to the spaces management page which will log us in Kibana + await common.navigateToUrl('management', 'kibana/spaces', { + shouldUseHashForSubUrl: false, + }); + + // Create a space with the search solution and navigate to its home page + ({ cleanUp, space: spaceCreated } = await spaces.create({ solution: 'es' })); + await browser.navigateTo(spaces.getRootUrl(spaceCreated.id)); + }); + + after(async () => { + // Clean up space created + await cleanUp(); + }); + + it('renders expected side nav items', async () => { + // Verify all expected top-level links exist + await solutionNavigation.sidenav.expectLinkExists({ text: 'Overview' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Dev Tools' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Discover' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Dashboards' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Indices' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Connectors' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Web crawlers' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Playground' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Search applications' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Behavioral Analytics' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'App Search' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Workplace Search' }); + await solutionNavigation.sidenav.expectLinkExists({ text: 'Other tools' }); + }); + + it('has expected navigation', async () => { + const expectNoPageReload = await solutionNavigation.createNoPageReloadCheck(); + + // check side nav links + await solutionNavigation.sidenav.expectSectionExists('search_project_nav'); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'enterpriseSearch', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'enterpriseSearch', + }); + + // check Dev tools + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'dev_tools', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'dev_tools', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Dev Tools' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'dev_tools', + }); + + // check Kibana + // > Discover + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'discover', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'discover', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Kibana' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Discover' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'discover', + }); + // > Dashboards + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'dashboards', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'dashboards', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Kibana' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Dashboards' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'dashboards', + }); + + // check the Content + // > Indices section + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'enterpriseSearchContent:searchIndices', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'enterpriseSearchContent:searchIndices', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Content' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Indices' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'enterpriseSearchContent:searchIndices', + }); + // > Connectors + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'enterpriseSearchContent:connectors', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'enterpriseSearchContent:connectors', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Content' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Connectors' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'enterpriseSearchContent:connectors', + }); + // > Web Crawlers + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'enterpriseSearchContent:webCrawlers', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'enterpriseSearchContent:webCrawlers', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Content' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Web crawlers' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'enterpriseSearchContent:webCrawlers', + }); + + // check Build + // > Playground + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'enterpriseSearchApplications:playground', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'enterpriseSearchApplications:playground', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Build' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Playground' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'enterpriseSearchApplications:playground', + }); + // > Search applications + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'enterpriseSearchApplications:searchApplications', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'enterpriseSearchApplications:searchApplications', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Build' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + text: 'Search applications', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'enterpriseSearchApplications:searchApplications', + }); + // > Behavioral Analytics + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'enterpriseSearchAnalytics', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'enterpriseSearchAnalytics', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Build' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + text: 'Behavioral Analytics', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'enterpriseSearchAnalytics', + }); + + // check Relevance + // > Inference Endpoints + // TODO: FTRs don't have enterprise license, so inference endpoints not shown + // await solutionNavigation.sidenav.clickLink({ + // deepLinkId: 'enterpriseSearchRelevance:inferenceEndpoints', + // }); + // await solutionNavigation.sidenav.expectLinkActive({ + // deepLinkId: 'enterpriseSearchRelevance:inferenceEndpoints', + // }); + // await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Relevance' }); + // await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + // text: 'Inference Endpoints', + // }); + // await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + // deepLinkId: 'enterpriseSearchRelevance:inferenceEndpoints', + // }); + + // check Enterprise Search + // > App Search + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'appSearch:engines', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'appSearch:engines', + }); + // ent-search node not running for FTRs, so we see setup guide without breadcrumbs + // await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + // text: 'App Search', + // }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'appSearch:engines', + }); + // > Workplace Search + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'workplaceSearch', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'workplaceSearch', + }); + // ent-search node not running for FTRs, so we see setup guide without breadcrumbs + // await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + // text: 'Workplace Search', + // }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'workplaceSearch', + }); + + // Other tools + await solutionNavigation.sidenav.openSection('search_project_nav.otherTools'); + // > Maps + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'maps', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'maps', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Other tools' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + text: 'Maps', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'maps', + }); + // > Canvas + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'canvas', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'canvas', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Other tools' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + text: 'Canvas', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'canvas', + }); + // > Graph + await solutionNavigation.sidenav.clickLink({ + deepLinkId: 'graph', + }); + await solutionNavigation.sidenav.expectLinkActive({ + deepLinkId: 'graph', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Other tools' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + text: 'Graph', + }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ + deepLinkId: 'graph', + }); + await solutionNavigation.sidenav.closeSection('search_project_nav.otherTools'); + + await expectNoPageReload(); + }); + + it('renders only expected items', async () => { + await solutionNavigation.sidenav.openSection('search_project_nav.otherTools'); + await solutionNavigation.sidenav.openSection('project_settings_project_nav'); + await solutionNavigation.sidenav.expectOnlyDefinedLinks([ + 'search_project_nav', + 'enterpriseSearch', + 'dev_tools', + 'kibana', + 'discover', + 'dashboards', + 'content', + 'enterpriseSearchContent:searchIndices', + 'enterpriseSearchContent:connectors', + 'enterpriseSearchContent:webCrawlers', + 'build', + 'enterpriseSearchApplications:playground', + 'enterpriseSearchApplications:searchApplications', + 'enterpriseSearchAnalytics', + // 'relevance', + // 'enterpriseSearchRelevance:inferenceEndpoints', + 'entsearch', + 'appSearch:engines', + 'workplaceSearch', + 'otherTools', + 'maps', + 'canvas', + 'graph', + 'project_settings_project_nav', + 'ml:modelManagement', + 'stack_management', + ]); + }); + }); +}