mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[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:
parent
cc7fdba142
commit
71fd96ad8b
9 changed files with 408 additions and 3 deletions
|
@ -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
1
.github/CODEOWNERS
vendored
|
@ -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
|
||||
|
|
|
@ -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}`);
|
||||
},
|
||||
|
|
|
@ -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
|
||||
? {
|
||||
|
|
31
x-pack/test/functional_search/config.ts
Normal file
31
x-pack/test/functional_search/config.ts
Normal 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',
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
13
x-pack/test/functional_search/ftr_provider_context.ts
Normal file
13
x-pack/test/functional_search/ftr_provider_context.ts
Normal 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 };
|
15
x-pack/test/functional_search/index.ts
Normal file
15
x-pack/test/functional_search/index.ts
Normal 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'));
|
||||
});
|
||||
};
|
10
x-pack/test/functional_search/services.ts
Normal file
10
x-pack/test/functional_search/services.ts
Normal 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;
|
314
x-pack/test/functional_search/tests/solution_navigation.ts
Normal file
314
x-pack/test/functional_search/tests/solution_navigation.ts
Normal 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',
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue