[Search][FTR] Solution Nav (#195327)

## Summary

Adding `functional_search` suite with a set of test for the search
solution navigation. But this suite will also grow to test search
solution pages that do not require the enterprise search node.

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
This commit is contained in:
Rodney Norris 2024-10-09 08:45:24 -05:00 committed by GitHub
parent cc7fdba142
commit 71fd96ad8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 408 additions and 3 deletions

View file

@ -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

1
.github/CODEOWNERS vendored
View file

@ -1482,6 +1482,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

View file

@ -138,6 +138,18 @@ export function SolutionNavigationProvider(ctx: Pick<FtrProviderContext, 'getSer
});
}
},
async expectOnlyDefinedLinks(navItemIds: string[]) {
const navItemIdRegEx = /nav-item-id-[^\s]+/g;
const allSideNavLinks = await testSubjects.findAll('*nav-item-id-');
for (const sideNavItem of allSideNavLinks) {
const dataTestSubjs = await sideNavItem.getAttribute('data-test-subj');
const navItemIdMatch = dataTestSubjs?.match(navItemIdRegEx);
expect(navItemIdMatch).to.be.ok();
const navItemId = navItemIdMatch![0].replace('nav-item-id-', '');
expect(navItemIds).to.contain(navItemId);
}
expect(allSideNavLinks.length).to.equal(navItemIds.length);
},
async clickPanelLink(deepLinkId: string) {
await testSubjects.click(`~panelNavItem-id-${deepLinkId}`);
},

View file

@ -93,7 +93,7 @@ export const getNavigationTreeDefinition = ({
return pathNameSerialized.startsWith(prepend('/app/dev_tools'));
},
id: 'dev_tools',
link: 'dev_tools:console',
link: 'dev_tools',
title: i18n.translate('xpack.enterpriseSearch.searchNav.devTools', {
defaultMessage: 'Dev Tools',
}),
@ -218,7 +218,11 @@ export const getNavigationTreeDefinition = ({
{
children: [
{
getIsActive: () => 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
? {

View file

@ -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',
],
},
};
}

View file

@ -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<typeof services, typeof pageObjects>;
export { pageObjects };

View file

@ -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'));
});
};

View file

@ -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;

View file

@ -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<unknown>;
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',
]);
});
});
}