mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Stateful sidenav] Functional tests helpers (#189804)
This commit is contained in:
parent
c6126d9235
commit
3db781d1f2
28 changed files with 849 additions and 376 deletions
|
@ -272,6 +272,7 @@ enabled:
|
|||
- x-pack/test/functional/config.firefox.js
|
||||
- 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/kubernetes_security/basic/config.ts
|
||||
- x-pack/test/licensing_plugin/config.public.ts
|
||||
- x-pack/test/licensing_plugin/config.ts
|
||||
|
|
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -1762,6 +1762,8 @@ x-pack/plugins/observability_solution/observability_shared/public/components/pro
|
|||
|
||||
# Shared UX
|
||||
packages/react @elastic/appex-sharedux
|
||||
test/functional/page_objects/solution_navigation.ts @elastic/appex-sharedux
|
||||
/x-pack/test_serverless/functional/page_objects/svl_common_navigation.ts @elastic/appex-sharedux
|
||||
|
||||
# OpenAPI spec files
|
||||
/x-pack/plugins/fleet/common/openapi @elastic/platform-docs
|
||||
|
|
|
@ -20,6 +20,7 @@ import type {
|
|||
} from '@kbn/core-chrome-browser';
|
||||
import type { InternalHttpStart } from '@kbn/core-http-browser-internal';
|
||||
import {
|
||||
Subject,
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
map,
|
||||
|
@ -32,6 +33,7 @@ import {
|
|||
of,
|
||||
type Observable,
|
||||
type Subscription,
|
||||
timer,
|
||||
} from 'rxjs';
|
||||
import { type Location, createLocation } from 'history';
|
||||
import deepEqual from 'react-fast-compare';
|
||||
|
@ -326,20 +328,50 @@ export class ProjectNavigationService {
|
|||
}
|
||||
|
||||
const { sideNavComponent, homePage = '' } = definition;
|
||||
const homePageLink = this.navLinksService?.get(homePage);
|
||||
|
||||
if (sideNavComponent) {
|
||||
this.setSideNavComponent(sideNavComponent);
|
||||
}
|
||||
|
||||
if (homePageLink) {
|
||||
this.setProjectHome(homePageLink.href);
|
||||
}
|
||||
this.waitForLink(homePage, (navLink: ChromeNavLink) => {
|
||||
this.setProjectHome(navLink.href);
|
||||
});
|
||||
|
||||
this.initNavigation(nextId, definition.navigationTree$);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method waits for the chrome nav link to be available and then calls the callback.
|
||||
* This is necessary to avoid race conditions when we register the solution navigation
|
||||
* before the deep links are available (plugins can register them later).
|
||||
*
|
||||
* @param linkId The chrome nav link id
|
||||
* @param cb The callback to call when the link is found
|
||||
* @returns
|
||||
*/
|
||||
private waitForLink(linkId: string, cb: (chromeNavLink: ChromeNavLink) => undefined): void {
|
||||
if (!this.navLinksService) return;
|
||||
|
||||
let navLink: ChromeNavLink | undefined = this.navLinksService.get(linkId);
|
||||
if (navLink) {
|
||||
cb(navLink);
|
||||
return;
|
||||
}
|
||||
|
||||
const stop$ = new Subject<void>();
|
||||
const tenSeconds = timer(10000);
|
||||
|
||||
this.deepLinksMap$.pipe(takeUntil(tenSeconds), takeUntil(stop$)).subscribe((navLinks) => {
|
||||
navLink = navLinks[linkId];
|
||||
|
||||
if (navLink) {
|
||||
cb(navLink);
|
||||
stop$.next();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setProjectHome(homeHref: string) {
|
||||
this.projectHome$.next(homeHref);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ export const NavigationItemOpenPanel: FC<Props> = ({ item, navigateToUrl, active
|
|||
[`nav-item-isActive`]: isActive,
|
||||
});
|
||||
const buttonDataTestSubj = classNames(`panelOpener`, `panelOpener-${path}`, {
|
||||
[`panelOpener-id-${id}`]: id,
|
||||
[`panelOpener-deepLinkId-${deepLink?.id}`]: !!deepLink,
|
||||
});
|
||||
|
||||
|
|
|
@ -19,6 +19,17 @@ import classNames from 'classnames';
|
|||
|
||||
import { usePanel } from './context';
|
||||
import { getNavPanelStyles, getPanelWrapperStyles } from './styles';
|
||||
import { PanelNavNode } from './types';
|
||||
|
||||
const getTestSubj = (selectedNode: PanelNavNode | null): string | undefined => {
|
||||
if (!selectedNode) return;
|
||||
|
||||
const deeplinkId = selectedNode.deepLink?.id;
|
||||
return classNames(`sideNavPanel`, {
|
||||
[`sideNavPanel-id-${selectedNode.id}`]: selectedNode.id,
|
||||
[`sideNavPanel-deepLinkId-${deeplinkId}`]: !!deeplinkId,
|
||||
});
|
||||
};
|
||||
|
||||
export const NavigationPanel: FC = () => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
@ -67,7 +78,7 @@ export const NavigationPanel: FC = () => {
|
|||
hasShadow
|
||||
borderRadius="none"
|
||||
paddingSize="m"
|
||||
data-test-subj="sideNavPanel"
|
||||
data-test-subj={getTestSubj(selectedNode)}
|
||||
>
|
||||
{getContent()}
|
||||
</EuiPanel>
|
||||
|
|
|
@ -27,7 +27,7 @@ export type ContentProvider = (nodeId: string) => PanelContent | void;
|
|||
|
||||
export type PanelNavNode = Pick<
|
||||
ChromeProjectNavigationNode,
|
||||
'id' | 'children' | 'path' | 'sideNavStatus'
|
||||
'id' | 'children' | 'path' | 'sideNavStatus' | 'deepLink'
|
||||
> & {
|
||||
title: string | ReactNode;
|
||||
};
|
||||
|
|
49
test/functional/page_objects/embedded_console.ts
Normal file
49
test/functional/page_objects/embedded_console.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 type { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export function EmbeddedConsoleProvider(ctx: FtrProviderContext) {
|
||||
const testSubjects = ctx.getService('testSubjects');
|
||||
|
||||
return {
|
||||
async expectEmbeddedConsoleControlBarExists() {
|
||||
await testSubjects.existOrFail('consoleEmbeddedSection');
|
||||
},
|
||||
async expectEmbeddedConsoleToBeOpen() {
|
||||
await testSubjects.existOrFail('consoleEmbeddedBody');
|
||||
},
|
||||
async expectEmbeddedConsoleToBeClosed() {
|
||||
await testSubjects.missingOrFail('consoleEmbeddedBody');
|
||||
},
|
||||
async clickEmbeddedConsoleControlBar() {
|
||||
await testSubjects.click('consoleEmbeddedControlBar');
|
||||
},
|
||||
async expectEmbeddedConsoleNotebooksButtonExists() {
|
||||
await testSubjects.existOrFail('consoleEmbeddedNotebooksButton');
|
||||
},
|
||||
async clickEmbeddedConsoleNotebooksButton() {
|
||||
await testSubjects.click('consoleEmbeddedNotebooksButton');
|
||||
},
|
||||
async expectEmbeddedConsoleNotebooksToBeOpen() {
|
||||
await testSubjects.existOrFail('consoleEmbeddedNotebooksContainer');
|
||||
},
|
||||
async expectEmbeddedConsoleNotebooksToBeClosed() {
|
||||
await testSubjects.missingOrFail('consoleEmbeddedNotebooksContainer');
|
||||
},
|
||||
async expectEmbeddedConsoleNotebookListItemToBeAvailable(id: string) {
|
||||
await testSubjects.existOrFail(`console-embedded-notebook-select-btn-${id}`);
|
||||
},
|
||||
async clickEmbeddedConsoleNotebook(id: string) {
|
||||
await testSubjects.click(`console-embedded-notebook-select-btn-${id}`);
|
||||
},
|
||||
async expectEmbeddedConsoleNotebookToBeAvailable(id: string) {
|
||||
await testSubjects.click(`console-embedded-notebook-select-btn-${id}`);
|
||||
},
|
||||
};
|
||||
}
|
|
@ -36,6 +36,8 @@ import { UnifiedSearchPageObject } from './unified_search_page';
|
|||
import { UnifiedFieldListPageObject } from './unified_field_list';
|
||||
import { FilesManagementPageObject } from './files_management';
|
||||
import { AnnotationEditorPageObject } from './annotation_library_editor_page';
|
||||
import { SolutionNavigationProvider } from './solution_navigation';
|
||||
import { EmbeddedConsoleProvider } from './embedded_console';
|
||||
|
||||
export const pageObjects = {
|
||||
annotationEditor: AnnotationEditorPageObject,
|
||||
|
@ -46,12 +48,14 @@ export const pageObjects = {
|
|||
dashboardControls: DashboardPageControls,
|
||||
dashboardLinks: DashboardPageLinks,
|
||||
discover: DiscoverPageObject,
|
||||
embeddedConsole: EmbeddedConsoleProvider,
|
||||
error: ErrorPageObject,
|
||||
header: HeaderPageObject,
|
||||
home: HomePageObject,
|
||||
newsfeed: NewsfeedPageObject,
|
||||
settings: SettingsPageObject,
|
||||
share: SharePageObject,
|
||||
solutionNavigation: SolutionNavigationProvider,
|
||||
legacyDataTableVis: LegacyDataTableVisPageObject,
|
||||
login: LoginPageObject,
|
||||
timelion: TimelionPageObject,
|
||||
|
@ -69,3 +73,5 @@ export const pageObjects = {
|
|||
unifiedFieldList: UnifiedFieldListPageObject,
|
||||
filesManagement: FilesManagementPageObject,
|
||||
};
|
||||
|
||||
export { SolutionNavigationProvider } from './solution_navigation';
|
||||
|
|
338
test/functional/page_objects/solution_navigation.ts
Normal file
338
test/functional/page_objects/solution_navigation.ts
Normal file
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* 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 type { AppDeepLinkId } from '@kbn/core-chrome-browser';
|
||||
|
||||
import type { NavigationID as MlNavId } from '@kbn/default-nav-ml';
|
||||
import type { NavigationID as AlNavId } from '@kbn/default-nav-analytics';
|
||||
import type { NavigationID as MgmtNavId } from '@kbn/default-nav-management';
|
||||
import type { NavigationID as DevNavId } from '@kbn/default-nav-devtools';
|
||||
|
||||
// use this for nicer type suggestions, but allow any string anyway
|
||||
type NavigationId = MlNavId | AlNavId | MgmtNavId | DevNavId | string;
|
||||
|
||||
import type { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services';
|
||||
import type { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
const getSectionIdTestSubj = (sectionId: NavigationId) => `~nav-item-${sectionId}`;
|
||||
|
||||
const TIMEOUT_CHECK = 3000;
|
||||
|
||||
export function SolutionNavigationProvider(ctx: Pick<FtrProviderContext, 'getService'>) {
|
||||
const testSubjects = ctx.getService('testSubjects');
|
||||
const browser = ctx.getService('browser');
|
||||
const retry = ctx.getService('retry');
|
||||
const log = ctx.getService('log');
|
||||
|
||||
async function getByVisibleText(
|
||||
selector: string | (() => Promise<WebElementWrapper[]>),
|
||||
text: string
|
||||
) {
|
||||
const subjects =
|
||||
typeof selector === 'string' ? await testSubjects.findAll(selector) : await selector();
|
||||
let found: WebElementWrapper | null = null;
|
||||
for (const subject of subjects) {
|
||||
const visibleText = await subject.getVisibleText();
|
||||
if (visibleText === text) {
|
||||
found = subject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
return {
|
||||
// check that chrome ui is in project/solution mode
|
||||
async expectExists() {
|
||||
await testSubjects.existOrFail('kibanaProjectHeader');
|
||||
},
|
||||
async clickLogo() {
|
||||
await testSubjects.click('nav-header-logo');
|
||||
},
|
||||
// side nav related actions
|
||||
sidenav: {
|
||||
async expectLinkExists(
|
||||
by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }
|
||||
) {
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.existOrFail(`~nav-item-deepLinkId-${by.deepLinkId}`, {
|
||||
timeout: TIMEOUT_CHECK,
|
||||
});
|
||||
} else if ('navId' in by) {
|
||||
await testSubjects.existOrFail(`~nav-item-id-${by.navId}`, { timeout: TIMEOUT_CHECK });
|
||||
} else {
|
||||
expect(await getByVisibleText('~nav-item', by.text)).not.be(null);
|
||||
}
|
||||
},
|
||||
async expectLinkMissing(
|
||||
by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }
|
||||
) {
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.missingOrFail(`~nav-item-deepLinkId-${by.deepLinkId}`, {
|
||||
timeout: TIMEOUT_CHECK,
|
||||
});
|
||||
} else if ('navId' in by) {
|
||||
await testSubjects.missingOrFail(`~nav-item-id-${by.navId}`, { timeout: TIMEOUT_CHECK });
|
||||
} else {
|
||||
expect(await getByVisibleText('~nav-item', by.text)).be(null);
|
||||
}
|
||||
},
|
||||
async expectLinkActive(
|
||||
by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }
|
||||
) {
|
||||
await this.expectLinkExists(by);
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.existOrFail(
|
||||
`~nav-item-deepLinkId-${by.deepLinkId} & ~nav-item-isActive`,
|
||||
{ timeout: TIMEOUT_CHECK }
|
||||
);
|
||||
} else if ('navId' in by) {
|
||||
await testSubjects.existOrFail(`~nav-item-id-${by.navId} & ~nav-item-isActive`, {
|
||||
timeout: TIMEOUT_CHECK,
|
||||
});
|
||||
} else {
|
||||
await retry.try(async () => {
|
||||
const link = await getByVisibleText('~nav-item', by.text);
|
||||
expect(await link!.elementHasClass(`nav-item-isActive`)).to.be(true);
|
||||
});
|
||||
}
|
||||
},
|
||||
async clickLink(by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }) {
|
||||
await this.expectLinkExists(by);
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.click(`~nav-item-deepLinkId-${by.deepLinkId}`);
|
||||
} else if ('navId' in by) {
|
||||
await testSubjects.click(`~nav-item-id-${by.navId}`);
|
||||
} else {
|
||||
await retry.try(async () => {
|
||||
const link = await getByVisibleText('~nav-item', by.text);
|
||||
await link!.click();
|
||||
});
|
||||
}
|
||||
},
|
||||
async findLink(by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }) {
|
||||
await this.expectLinkExists(by);
|
||||
if ('deepLinkId' in by) {
|
||||
return testSubjects.find(`~nav-item-deepLinkId-${by.deepLinkId}`);
|
||||
} else if ('navId' in by) {
|
||||
return testSubjects.find(`~nav-item-id-${by.navId}`);
|
||||
} else {
|
||||
return retry.try(async () => {
|
||||
const link = await getByVisibleText('~nav-item', by.text);
|
||||
return link;
|
||||
});
|
||||
}
|
||||
},
|
||||
async expectSectionExists(sectionId: NavigationId) {
|
||||
log.debug('SolutionNavigation.sidenav.expectSectionExists', sectionId);
|
||||
await testSubjects.existOrFail(getSectionIdTestSubj(sectionId), { timeout: TIMEOUT_CHECK });
|
||||
},
|
||||
async isSectionOpen(sectionId: NavigationId) {
|
||||
await this.expectSectionExists(sectionId);
|
||||
const collapseBtn = await testSubjects.find(`~accordionArrow-${sectionId}`);
|
||||
const isExpanded = await collapseBtn.getAttribute('aria-expanded');
|
||||
return isExpanded === 'true';
|
||||
},
|
||||
async expectSectionOpen(sectionId: NavigationId) {
|
||||
log.debug('SolutionNavigation.sidenav.expectSectionOpen', sectionId);
|
||||
await this.expectSectionExists(sectionId);
|
||||
await retry.waitFor(`section ${sectionId} to be open`, async () => {
|
||||
const isOpen = await this.isSectionOpen(sectionId);
|
||||
return isOpen;
|
||||
});
|
||||
},
|
||||
async expectSectionClosed(sectionId: NavigationId) {
|
||||
await this.expectSectionExists(sectionId);
|
||||
await retry.waitFor(`section ${sectionId} to be closed`, async () => {
|
||||
const isOpen = await this.isSectionOpen(sectionId);
|
||||
return !isOpen;
|
||||
});
|
||||
},
|
||||
async openSection(sectionId: NavigationId) {
|
||||
log.debug('SolutionNavigation.sidenav.openSection', sectionId);
|
||||
await this.expectSectionExists(sectionId);
|
||||
const isOpen = await this.isSectionOpen(sectionId);
|
||||
if (isOpen) return;
|
||||
const collapseBtn = await testSubjects.find(`~accordionArrow-${sectionId}`, TIMEOUT_CHECK);
|
||||
await collapseBtn.click();
|
||||
await this.expectSectionOpen(sectionId);
|
||||
},
|
||||
async closeSection(sectionId: NavigationId) {
|
||||
await this.expectSectionExists(sectionId);
|
||||
const isOpen = await this.isSectionOpen(sectionId);
|
||||
if (!isOpen) return;
|
||||
const collapseBtn = await testSubjects.find(`~accordionArrow-${sectionId}`, TIMEOUT_CHECK);
|
||||
await collapseBtn.click();
|
||||
await this.expectSectionClosed(sectionId);
|
||||
},
|
||||
async expectPanelExists(sectionId: NavigationId) {
|
||||
log.debug('SolutionNavigation.sidenav.expectPanelExists', sectionId);
|
||||
await testSubjects.existOrFail(`~sideNavPanel-id-${sectionId}`, {
|
||||
timeout: TIMEOUT_CHECK,
|
||||
});
|
||||
},
|
||||
async isPanelOpen(sectionId: NavigationId) {
|
||||
try {
|
||||
const panel = await testSubjects.find(`~sideNavPanel-id-${sectionId}`, TIMEOUT_CHECK);
|
||||
return !!panel;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
async openPanel(sectionId: NavigationId) {
|
||||
log.debug('SolutionNavigation.sidenav.openPanel', sectionId);
|
||||
|
||||
const isOpen = await this.isPanelOpen(sectionId);
|
||||
if (isOpen) return;
|
||||
|
||||
const panelOpenerBtn = await testSubjects.find(
|
||||
`~panelOpener-id-${sectionId}`,
|
||||
TIMEOUT_CHECK
|
||||
);
|
||||
|
||||
await panelOpenerBtn.click();
|
||||
},
|
||||
async isCollapsed() {
|
||||
const collapseNavBtn = await testSubjects.find('euiCollapsibleNavButton', TIMEOUT_CHECK);
|
||||
return (await collapseNavBtn.getAttribute('aria-expanded')) === 'false';
|
||||
},
|
||||
async isExpanded() {
|
||||
return !(await this.isCollapsed());
|
||||
},
|
||||
/**
|
||||
* Toggles collapsed state of sidenav
|
||||
*/
|
||||
async toggle(collapsed?: boolean) {
|
||||
const currentlyCollapsed = await this.isCollapsed();
|
||||
const shouldBeCollapsed = collapsed ?? !currentlyCollapsed;
|
||||
|
||||
if (currentlyCollapsed !== shouldBeCollapsed) {
|
||||
log.debug(
|
||||
'SolutionNavigation.sidenav.toggle',
|
||||
shouldBeCollapsed ? 'Collapsing' : 'Expanding'
|
||||
);
|
||||
|
||||
const collapseNavBtn = await testSubjects.find('euiCollapsibleNavButton', TIMEOUT_CHECK);
|
||||
await collapseNavBtn.click();
|
||||
}
|
||||
},
|
||||
},
|
||||
breadcrumbs: {
|
||||
async expectExists() {
|
||||
await testSubjects.existOrFail('breadcrumbs', { timeout: TIMEOUT_CHECK });
|
||||
},
|
||||
async clickBreadcrumb(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.click(`~breadcrumb-deepLinkId-${by.deepLinkId}`);
|
||||
} else {
|
||||
await (await getByVisibleText('~breadcrumb', by.text))?.click();
|
||||
}
|
||||
},
|
||||
getBreadcrumb(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
|
||||
if ('deepLinkId' in by) {
|
||||
return testSubjects.find(`~breadcrumb-deepLinkId-${by.deepLinkId}`, TIMEOUT_CHECK);
|
||||
} else {
|
||||
return getByVisibleText('~breadcrumb', by.text);
|
||||
}
|
||||
},
|
||||
async expectBreadcrumbExists(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
|
||||
log.debug('SolutionNavigation.breadcrumbs.expectBreadcrumbExists', JSON.stringify(by));
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.existOrFail(`~breadcrumb-deepLinkId-${by.deepLinkId}`, {
|
||||
timeout: TIMEOUT_CHECK,
|
||||
});
|
||||
} else {
|
||||
await retry.try(async () => {
|
||||
expect(await getByVisibleText('~breadcrumb', by.text)).not.be(null);
|
||||
});
|
||||
}
|
||||
},
|
||||
async expectBreadcrumbMissing(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.missingOrFail(`~breadcrumb-deepLinkId-${by.deepLinkId}`, {
|
||||
timeout: TIMEOUT_CHECK,
|
||||
});
|
||||
} else {
|
||||
await retry.try(async () => {
|
||||
expect(await getByVisibleText('~breadcrumb', by.text)).be(null);
|
||||
});
|
||||
}
|
||||
},
|
||||
async expectBreadcrumbTexts(expectedBreadcrumbTexts: string[]) {
|
||||
log.debug(
|
||||
'SolutionNavigation.breadcrumbs.expectBreadcrumbTexts',
|
||||
JSON.stringify(expectedBreadcrumbTexts)
|
||||
);
|
||||
await retry.try(async () => {
|
||||
const breadcrumbsContainer = await testSubjects.find('breadcrumbs', TIMEOUT_CHECK);
|
||||
const breadcrumbs = await breadcrumbsContainer.findAllByTestSubject('~breadcrumb');
|
||||
breadcrumbs.shift(); // remove home
|
||||
expect(expectedBreadcrumbTexts.length).to.eql(breadcrumbs.length);
|
||||
const texts = await Promise.all(breadcrumbs.map((b) => b.getVisibleText()));
|
||||
expect(expectedBreadcrumbTexts).to.eql(texts);
|
||||
});
|
||||
},
|
||||
},
|
||||
recent: {
|
||||
async expectExists() {
|
||||
await testSubjects.existOrFail('nav-item-recentlyAccessed', { timeout: TIMEOUT_CHECK });
|
||||
},
|
||||
async expectHidden() {
|
||||
await testSubjects.missingOrFail('nav-item-recentlyAccessed', { timeout: TIMEOUT_CHECK });
|
||||
},
|
||||
async expectLinkExists(text: string) {
|
||||
await this.expectExists();
|
||||
let foundLink: WebElementWrapper | null = null;
|
||||
await retry.try(async () => {
|
||||
foundLink = await getByVisibleText(
|
||||
async () =>
|
||||
(
|
||||
await testSubjects.find('nav-item-recentlyAccessed', TIMEOUT_CHECK)
|
||||
).findAllByTagName('a'),
|
||||
text
|
||||
);
|
||||
expect(!!foundLink).to.be(true);
|
||||
});
|
||||
|
||||
return foundLink!;
|
||||
},
|
||||
async clickLink(text: string) {
|
||||
const link = await this.expectLinkExists(text);
|
||||
await link!.click();
|
||||
},
|
||||
},
|
||||
|
||||
// helper to assert that the page did not reload
|
||||
async createNoPageReloadCheck() {
|
||||
const trackReloadTs = Date.now();
|
||||
await browser.execute(
|
||||
({ ts }) => {
|
||||
// @ts-ignore
|
||||
window.__testTrackReload__ = ts;
|
||||
},
|
||||
{
|
||||
ts: trackReloadTs,
|
||||
}
|
||||
);
|
||||
|
||||
return async () => {
|
||||
const noReload = await browser.execute(
|
||||
({ ts }) => {
|
||||
// @ts-ignore
|
||||
return window.__testTrackReload__ && window.__testTrackReload__ === ts;
|
||||
},
|
||||
{
|
||||
ts: trackReloadTs,
|
||||
}
|
||||
);
|
||||
expect(noReload).to.be(true);
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
|
@ -73,5 +73,10 @@
|
|||
"@kbn/monaco",
|
||||
"@kbn/search-types",
|
||||
"@kbn/console-plugin",
|
||||
"@kbn/core-chrome-browser",
|
||||
"@kbn/default-nav-ml",
|
||||
"@kbn/default-nav-analytics",
|
||||
"@kbn/default-nav-management",
|
||||
"@kbn/default-nav-devtools",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -10,8 +10,22 @@ import Axios from 'axios';
|
|||
import Https from 'https';
|
||||
import { format as formatUrl } from 'url';
|
||||
import util from 'util';
|
||||
import Chance from 'chance';
|
||||
import Url from 'url';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
const chance = new Chance();
|
||||
|
||||
interface SpaceCreate {
|
||||
name?: string;
|
||||
id?: string;
|
||||
description?: string;
|
||||
color?: string;
|
||||
initials?: string;
|
||||
solution?: 'es' | 'oblt' | 'security' | 'classic';
|
||||
disabledFeatures?: string[];
|
||||
}
|
||||
|
||||
export function SpacesServiceProvider({ getService }: FtrProviderContext) {
|
||||
const log = getService('log');
|
||||
const config = getService('config');
|
||||
|
@ -35,7 +49,9 @@ export function SpacesServiceProvider({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
return new (class SpacesService {
|
||||
public async create(space: any) {
|
||||
public async create(_space?: SpaceCreate) {
|
||||
const space = { id: chance.guid(), name: 'foo', ..._space };
|
||||
|
||||
log.debug(`creating space ${space.id}`);
|
||||
const { data, status, statusText } = await axios.post('/api/spaces/space', space);
|
||||
|
||||
|
@ -45,6 +61,15 @@ export function SpacesServiceProvider({ getService }: FtrProviderContext) {
|
|||
);
|
||||
}
|
||||
log.debug(`created space ${space.id}`);
|
||||
|
||||
const cleanUp = async () => {
|
||||
return this.delete(space.id);
|
||||
};
|
||||
|
||||
return {
|
||||
cleanUp,
|
||||
space,
|
||||
};
|
||||
}
|
||||
|
||||
public async delete(spaceId: string) {
|
||||
|
@ -72,5 +97,17 @@ export function SpacesServiceProvider({ getService }: FtrProviderContext) {
|
|||
|
||||
return data;
|
||||
}
|
||||
|
||||
/** Return the full URL that points to the root of the space */
|
||||
public getRootUrl(spaceId: string) {
|
||||
const { protocol, hostname, port } = config.get('servers.kibana');
|
||||
|
||||
return Url.format({
|
||||
protocol,
|
||||
hostname,
|
||||
port,
|
||||
pathname: `/s/${spaceId}`,
|
||||
});
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
|
31
x-pack/test/functional_solution_sidenav/config.ts
Normal file
31
x-pack/test/functional_solution_sidenav/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',
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
|
@ -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 };
|
17
x-pack/test/functional_solution_sidenav/index.ts
Normal file
17
x-pack/test/functional_solution_sidenav/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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('Solution navigation smoke tests', function () {
|
||||
loadTestFile(require.resolve('./tests/observability_sidenav'));
|
||||
loadTestFile(require.resolve('./tests/search_sidenav'));
|
||||
loadTestFile(require.resolve('./tests/security_sidenav'));
|
||||
});
|
||||
};
|
10
x-pack/test/functional_solution_sidenav/services.ts
Normal file
10
x-pack/test/functional_solution_sidenav/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;
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const { common, solutionNavigation } = getPageObjects(['common', 'solutionNavigation']);
|
||||
const spaces = getService('spaces');
|
||||
const browser = getService('browser');
|
||||
|
||||
describe('observability solution', () => {
|
||||
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 observability solution and navigate to its home page
|
||||
({ cleanUp, space: spaceCreated } = await spaces.create({ solution: 'oblt' }));
|
||||
await browser.navigateTo(spaces.getRootUrl(spaceCreated.id));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
// Clean up space created
|
||||
await cleanUp();
|
||||
});
|
||||
|
||||
describe('sidenav & breadcrumbs', () => {
|
||||
it('renders the correct nav and navigate to links', async () => {
|
||||
const expectNoPageReload = await solutionNavigation.createNoPageReloadCheck();
|
||||
|
||||
await solutionNavigation.expectExists();
|
||||
await solutionNavigation.breadcrumbs.expectExists();
|
||||
|
||||
// check side nav links
|
||||
await solutionNavigation.sidenav.expectSectionExists('observability_project_nav');
|
||||
await solutionNavigation.sidenav.expectLinkActive({
|
||||
deepLinkId: 'observabilityOnboarding',
|
||||
});
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'observabilityOnboarding',
|
||||
});
|
||||
|
||||
// check the AI & ML subsection
|
||||
await solutionNavigation.sidenav.openSection('observability_project_nav.aiMl'); // open AI & ML subsection
|
||||
await solutionNavigation.sidenav.clickLink({ deepLinkId: 'ml:anomalyDetection' });
|
||||
await solutionNavigation.sidenav.expectLinkActive({ deepLinkId: 'ml:anomalyDetection' });
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'AI & ML' });
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'ml:anomalyDetection',
|
||||
});
|
||||
|
||||
// navigate to a different section
|
||||
await solutionNavigation.sidenav.openSection('project_settings_project_nav');
|
||||
await solutionNavigation.sidenav.clickLink({ deepLinkId: 'management' });
|
||||
await solutionNavigation.sidenav.expectLinkActive({ deepLinkId: 'management' });
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ deepLinkId: 'management' });
|
||||
|
||||
// navigate back to the home page using header logo
|
||||
await solutionNavigation.clickLogo();
|
||||
await solutionNavigation.sidenav.expectLinkActive({
|
||||
deepLinkId: 'observabilityOnboarding',
|
||||
});
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'observabilityOnboarding',
|
||||
});
|
||||
|
||||
await expectNoPageReload();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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 ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const { common, solutionNavigation } = getPageObjects(['common', 'solutionNavigation']);
|
||||
const spaces = getService('spaces');
|
||||
const browser = getService('browser');
|
||||
|
||||
describe('search solution', () => {
|
||||
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();
|
||||
});
|
||||
|
||||
describe('sidenav & breadcrumbs', () => {
|
||||
it('renders the correct nav and navigate to links', async () => {
|
||||
const expectNoPageReload = await solutionNavigation.createNoPageReloadCheck();
|
||||
|
||||
await solutionNavigation.expectExists();
|
||||
await solutionNavigation.breadcrumbs.expectExists();
|
||||
|
||||
// check side nav links
|
||||
await solutionNavigation.sidenav.expectSectionExists('search_project_nav');
|
||||
await solutionNavigation.sidenav.expectLinkActive({
|
||||
deepLinkId: 'enterpriseSearch',
|
||||
});
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'enterpriseSearch',
|
||||
});
|
||||
|
||||
// check the Content > Indices section
|
||||
await solutionNavigation.sidenav.clickLink({
|
||||
deepLinkId: 'enterpriseSearchContent:searchIndices',
|
||||
});
|
||||
await solutionNavigation.sidenav.expectLinkActive({
|
||||
deepLinkId: 'enterpriseSearchContent:searchIndices',
|
||||
});
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Indices' });
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'enterpriseSearchContent:searchIndices',
|
||||
});
|
||||
|
||||
// navigate to a different section
|
||||
await solutionNavigation.sidenav.openSection('project_settings_project_nav');
|
||||
await solutionNavigation.sidenav.clickLink({ deepLinkId: 'management' });
|
||||
await solutionNavigation.sidenav.expectLinkActive({ deepLinkId: 'management' });
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ deepLinkId: 'management' });
|
||||
|
||||
// navigate back to the home page using header logo
|
||||
await solutionNavigation.clickLogo();
|
||||
await solutionNavigation.sidenav.expectLinkActive({
|
||||
deepLinkId: 'enterpriseSearch',
|
||||
});
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'enterpriseSearch',
|
||||
});
|
||||
|
||||
await expectNoPageReload();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const { common, solutionNavigation } = getPageObjects(['common', 'solutionNavigation']);
|
||||
const spaces = getService('spaces');
|
||||
const browser = getService('browser');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
describe('security solution', () => {
|
||||
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 security solution and navigate to its home page
|
||||
({ cleanUp, space: spaceCreated } = await spaces.create({ solution: 'security' }));
|
||||
await browser.navigateTo(spaces.getRootUrl(spaceCreated.id));
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
// Clean up space created
|
||||
await cleanUp();
|
||||
});
|
||||
|
||||
describe('sidenav & breadcrumbs', () => {
|
||||
it('renders the correct nav and navigate to links', async () => {
|
||||
const expectNoPageReload = await solutionNavigation.createNoPageReloadCheck();
|
||||
|
||||
await solutionNavigation.expectExists();
|
||||
await solutionNavigation.breadcrumbs.expectExists();
|
||||
|
||||
// check side nav links
|
||||
await solutionNavigation.sidenav.expectSectionExists('security_solution_nav');
|
||||
await solutionNavigation.sidenav.expectLinkActive({
|
||||
deepLinkId: 'securitySolutionUI:get_started',
|
||||
});
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'securitySolutionUI:get_started',
|
||||
});
|
||||
|
||||
// check the Investigations subsection
|
||||
await solutionNavigation.sidenav.openPanel('investigations'); // open Investigations panel
|
||||
await testSubjects.click(`~solutionSideNavPanelLink-timelines`);
|
||||
await solutionNavigation.sidenav.expectLinkActive({ navId: 'investigations' });
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Timelines' });
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'securitySolutionUI:timelines',
|
||||
});
|
||||
|
||||
// navigate back to the home page using header logo
|
||||
await solutionNavigation.clickLogo();
|
||||
await solutionNavigation.sidenav.expectLinkActive({
|
||||
deepLinkId: 'securitySolutionUI:get_started',
|
||||
});
|
||||
await solutionNavigation.breadcrumbs.expectBreadcrumbExists({
|
||||
deepLinkId: 'securitySolutionUI:get_started',
|
||||
});
|
||||
|
||||
await expectNoPageReload();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -5,334 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import type { AppDeepLinkId } from '@kbn/core-chrome-browser';
|
||||
import { SolutionNavigationProvider } from '@kbn/test-suites-src/functional/page_objects';
|
||||
|
||||
import type { NavigationID as MlNavId } from '@kbn/default-nav-ml';
|
||||
import type { NavigationID as AlNavId } from '@kbn/default-nav-analytics';
|
||||
import type { NavigationID as MgmtNavId } from '@kbn/default-nav-management';
|
||||
import type { NavigationID as DevNavId } from '@kbn/default-nav-devtools';
|
||||
|
||||
// use this for nicer type suggestions, but allow any string anyway
|
||||
type NavigationId = MlNavId | AlNavId | MgmtNavId | DevNavId | string;
|
||||
|
||||
import type { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services';
|
||||
import { NavigationalSearchPageObject } from '@kbn/test-suites-xpack/functional/page_objects/navigational_search';
|
||||
import type { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
const getSectionIdTestSubj = (sectionId: NavigationId) => `~nav-item-${sectionId}`;
|
||||
|
||||
export function SvlCommonNavigationProvider(ctx: FtrProviderContext) {
|
||||
const testSubjects = ctx.getService('testSubjects');
|
||||
const browser = ctx.getService('browser');
|
||||
const retry = ctx.getService('retry');
|
||||
const log = ctx.getService('log');
|
||||
|
||||
async function getByVisibleText(
|
||||
selector: string | (() => Promise<WebElementWrapper[]>),
|
||||
text: string
|
||||
) {
|
||||
const subjects =
|
||||
typeof selector === 'string' ? await testSubjects.findAll(selector) : await selector();
|
||||
let found: WebElementWrapper | null = null;
|
||||
for (const subject of subjects) {
|
||||
const visibleText = await subject.getVisibleText();
|
||||
if (visibleText === text) {
|
||||
found = subject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
const solutionNavigation = SolutionNavigationProvider(ctx);
|
||||
|
||||
return {
|
||||
// check that chrome ui is in the serverless (project) mode
|
||||
async expectExists() {
|
||||
await testSubjects.existOrFail('kibanaProjectHeader');
|
||||
},
|
||||
async clickLogo() {
|
||||
await testSubjects.click('nav-header-logo');
|
||||
},
|
||||
// side nav related actions
|
||||
sidenav: {
|
||||
async expectLinkExists(
|
||||
by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }
|
||||
) {
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.existOrFail(`~nav-item-deepLinkId-${by.deepLinkId}`);
|
||||
} else if ('navId' in by) {
|
||||
await testSubjects.existOrFail(`~nav-item-id-${by.navId}`);
|
||||
} else {
|
||||
expect(await getByVisibleText('~nav-item', by.text)).not.be(null);
|
||||
}
|
||||
},
|
||||
async expectLinkMissing(
|
||||
by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }
|
||||
) {
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.missingOrFail(`~nav-item-deepLinkId-${by.deepLinkId}`);
|
||||
} else if ('navId' in by) {
|
||||
await testSubjects.missingOrFail(`~nav-item-id-${by.navId}`);
|
||||
} else {
|
||||
expect(await getByVisibleText('~nav-item', by.text)).be(null);
|
||||
}
|
||||
},
|
||||
async expectLinkActive(
|
||||
by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }
|
||||
) {
|
||||
await this.expectLinkExists(by);
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.existOrFail(
|
||||
`~nav-item-deepLinkId-${by.deepLinkId} & ~nav-item-isActive`
|
||||
);
|
||||
} else if ('navId' in by) {
|
||||
await testSubjects.existOrFail(`~nav-item-id-${by.navId} & ~nav-item-isActive`);
|
||||
} else {
|
||||
await retry.try(async () => {
|
||||
const link = await getByVisibleText('~nav-item', by.text);
|
||||
expect(await link!.elementHasClass(`nav-item-isActive`)).to.be(true);
|
||||
});
|
||||
}
|
||||
},
|
||||
async clickLink(by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }) {
|
||||
await this.expectLinkExists(by);
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.click(`~nav-item-deepLinkId-${by.deepLinkId}`);
|
||||
} else if ('navId' in by) {
|
||||
await testSubjects.click(`~nav-item-id-${by.navId}`);
|
||||
} else {
|
||||
await retry.try(async () => {
|
||||
const link = await getByVisibleText('~nav-item', by.text);
|
||||
await link!.click();
|
||||
});
|
||||
}
|
||||
},
|
||||
async findLink(by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string }) {
|
||||
await this.expectLinkExists(by);
|
||||
if ('deepLinkId' in by) {
|
||||
return testSubjects.find(`~nav-item-deepLinkId-${by.deepLinkId}`);
|
||||
} else if ('navId' in by) {
|
||||
return testSubjects.find(`~nav-item-id-${by.navId}`);
|
||||
} else {
|
||||
return retry.try(async () => {
|
||||
const link = await getByVisibleText('~nav-item', by.text);
|
||||
return link;
|
||||
});
|
||||
}
|
||||
},
|
||||
async expectSectionExists(sectionId: NavigationId) {
|
||||
log.debug('ServerlessCommonNavigation.sidenav.expectSectionExists', sectionId);
|
||||
await testSubjects.existOrFail(getSectionIdTestSubj(sectionId));
|
||||
},
|
||||
async isSectionOpen(sectionId: NavigationId) {
|
||||
await this.expectSectionExists(sectionId);
|
||||
const collapseBtn = await testSubjects.find(`~accordionArrow-${sectionId}`);
|
||||
const isExpanded = await collapseBtn.getAttribute('aria-expanded');
|
||||
return isExpanded === 'true';
|
||||
},
|
||||
async expectSectionOpen(sectionId: NavigationId) {
|
||||
log.debug('ServerlessCommonNavigation.sidenav.expectSectionOpen', sectionId);
|
||||
await this.expectSectionExists(sectionId);
|
||||
await retry.waitFor(`section ${sectionId} to be open`, async () => {
|
||||
const isOpen = await this.isSectionOpen(sectionId);
|
||||
return isOpen;
|
||||
});
|
||||
},
|
||||
async expectSectionClosed(sectionId: NavigationId) {
|
||||
await this.expectSectionExists(sectionId);
|
||||
await retry.waitFor(`section ${sectionId} to be closed`, async () => {
|
||||
const isOpen = await this.isSectionOpen(sectionId);
|
||||
return !isOpen;
|
||||
});
|
||||
},
|
||||
async openSection(sectionId: NavigationId) {
|
||||
log.debug('ServerlessCommonNavigation.sidenav.openSection', sectionId);
|
||||
await this.expectSectionExists(sectionId);
|
||||
const isOpen = await this.isSectionOpen(sectionId);
|
||||
if (isOpen) return;
|
||||
const collapseBtn = await testSubjects.find(`~accordionArrow-${sectionId}`);
|
||||
await collapseBtn.click();
|
||||
await this.expectSectionOpen(sectionId);
|
||||
},
|
||||
async closeSection(sectionId: NavigationId) {
|
||||
await this.expectSectionExists(sectionId);
|
||||
const isOpen = await this.isSectionOpen(sectionId);
|
||||
if (!isOpen) return;
|
||||
const collapseBtn = await testSubjects.find(`~accordionArrow-${sectionId}`);
|
||||
await collapseBtn.click();
|
||||
await this.expectSectionClosed(sectionId);
|
||||
},
|
||||
async isCollapsed() {
|
||||
const collapseNavBtn = await testSubjects.find('euiCollapsibleNavButton');
|
||||
return (await collapseNavBtn.getAttribute('aria-expanded')) === 'false';
|
||||
},
|
||||
async isExpanded() {
|
||||
return !(await this.isCollapsed());
|
||||
},
|
||||
/**
|
||||
* Toggles collapsed state of sidenav
|
||||
*/
|
||||
async toggle(collapsed?: boolean) {
|
||||
const currentlyCollapsed = await this.isCollapsed();
|
||||
const shouldBeCollapsed = collapsed ?? !currentlyCollapsed;
|
||||
|
||||
if (currentlyCollapsed !== shouldBeCollapsed) {
|
||||
log.debug(
|
||||
'ServerlessCommonNavigation.sidenav.toggle',
|
||||
shouldBeCollapsed ? 'Collapsing' : 'Expanding'
|
||||
);
|
||||
|
||||
const collapseNavBtn = await testSubjects.find('euiCollapsibleNavButton');
|
||||
await collapseNavBtn.click();
|
||||
}
|
||||
},
|
||||
},
|
||||
breadcrumbs: {
|
||||
async expectExists() {
|
||||
await testSubjects.existOrFail('breadcrumbs');
|
||||
},
|
||||
async clickBreadcrumb(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.click(`~breadcrumb-deepLinkId-${by.deepLinkId}`);
|
||||
} else {
|
||||
await (await getByVisibleText('~breadcrumb', by.text))?.click();
|
||||
}
|
||||
},
|
||||
getBreadcrumb(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
|
||||
if ('deepLinkId' in by) {
|
||||
return testSubjects.find(`~breadcrumb-deepLinkId-${by.deepLinkId}`);
|
||||
} else {
|
||||
return getByVisibleText('~breadcrumb', by.text);
|
||||
}
|
||||
},
|
||||
async expectBreadcrumbExists(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
|
||||
log.debug(
|
||||
'ServerlessCommonNavigation.breadcrumbs.expectBreadcrumbExists',
|
||||
JSON.stringify(by)
|
||||
);
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.existOrFail(`~breadcrumb-deepLinkId-${by.deepLinkId}`);
|
||||
} else {
|
||||
await retry.try(async () => {
|
||||
expect(await getByVisibleText('~breadcrumb', by.text)).not.be(null);
|
||||
});
|
||||
}
|
||||
},
|
||||
async expectBreadcrumbMissing(by: { deepLinkId: AppDeepLinkId } | { text: string }) {
|
||||
if ('deepLinkId' in by) {
|
||||
await testSubjects.missingOrFail(`~breadcrumb-deepLinkId-${by.deepLinkId}`);
|
||||
} else {
|
||||
await retry.try(async () => {
|
||||
expect(await getByVisibleText('~breadcrumb', by.text)).be(null);
|
||||
});
|
||||
}
|
||||
},
|
||||
async expectBreadcrumbTexts(expectedBreadcrumbTexts: string[]) {
|
||||
log.debug(
|
||||
'ServerlessCommonNavigation.breadcrumbs.expectBreadcrumbTexts',
|
||||
JSON.stringify(expectedBreadcrumbTexts)
|
||||
);
|
||||
await retry.try(async () => {
|
||||
const breadcrumbsContainer = await testSubjects.find('breadcrumbs');
|
||||
const breadcrumbs = await breadcrumbsContainer.findAllByTestSubject('~breadcrumb');
|
||||
breadcrumbs.shift(); // remove home
|
||||
expect(expectedBreadcrumbTexts.length).to.eql(breadcrumbs.length);
|
||||
const texts = await Promise.all(breadcrumbs.map((b) => b.getVisibleText()));
|
||||
expect(expectedBreadcrumbTexts).to.eql(texts);
|
||||
});
|
||||
},
|
||||
},
|
||||
...solutionNavigation,
|
||||
search: new SvlNavigationSearchPageObject(ctx),
|
||||
recent: {
|
||||
async expectExists() {
|
||||
await testSubjects.existOrFail('nav-item-recentlyAccessed');
|
||||
},
|
||||
async expectHidden() {
|
||||
await testSubjects.missingOrFail('nav-item-recentlyAccessed', { timeout: 1000 });
|
||||
},
|
||||
async expectLinkExists(text: string) {
|
||||
await this.expectExists();
|
||||
let foundLink: WebElementWrapper | null = null;
|
||||
await retry.try(async () => {
|
||||
foundLink = await getByVisibleText(
|
||||
async () =>
|
||||
(await testSubjects.find('nav-item-recentlyAccessed')).findAllByTagName('a'),
|
||||
text
|
||||
);
|
||||
expect(!!foundLink).to.be(true);
|
||||
});
|
||||
|
||||
return foundLink!;
|
||||
},
|
||||
async clickLink(text: string) {
|
||||
const link = await this.expectLinkExists(text);
|
||||
await link!.click();
|
||||
},
|
||||
},
|
||||
|
||||
// helper to assert that the page did not reload
|
||||
async createNoPageReloadCheck() {
|
||||
const trackReloadTs = Date.now();
|
||||
await browser.execute(
|
||||
({ ts }) => {
|
||||
// @ts-ignore
|
||||
window.__testTrackReload__ = ts;
|
||||
},
|
||||
{
|
||||
ts: trackReloadTs,
|
||||
}
|
||||
);
|
||||
|
||||
return async () => {
|
||||
const noReload = await browser.execute(
|
||||
({ ts }) => {
|
||||
// @ts-ignore
|
||||
return window.__testTrackReload__ && window.__testTrackReload__ === ts;
|
||||
},
|
||||
{
|
||||
ts: trackReloadTs,
|
||||
}
|
||||
);
|
||||
expect(noReload).to.be(true);
|
||||
};
|
||||
},
|
||||
|
||||
// embedded dev console
|
||||
devConsole: {
|
||||
async expectEmbeddedConsoleControlBarExists() {
|
||||
await testSubjects.existOrFail('consoleEmbeddedSection');
|
||||
},
|
||||
async expectEmbeddedConsoleToBeOpen() {
|
||||
await testSubjects.existOrFail('consoleEmbeddedBody');
|
||||
},
|
||||
async expectEmbeddedConsoleToBeClosed() {
|
||||
await testSubjects.missingOrFail('consoleEmbeddedBody');
|
||||
},
|
||||
async clickEmbeddedConsoleControlBar() {
|
||||
await testSubjects.click('consoleEmbeddedControlBar');
|
||||
},
|
||||
async expectEmbeddedConsoleNotebooksButtonExists() {
|
||||
await testSubjects.existOrFail('consoleEmbeddedNotebooksButton');
|
||||
},
|
||||
async clickEmbeddedConsoleNotebooksButton() {
|
||||
await testSubjects.click('consoleEmbeddedNotebooksButton');
|
||||
},
|
||||
async expectEmbeddedConsoleNotebooksToBeOpen() {
|
||||
await testSubjects.existOrFail('consoleEmbeddedNotebooksContainer');
|
||||
},
|
||||
async expectEmbeddedConsoleNotebooksToBeClosed() {
|
||||
await testSubjects.missingOrFail('consoleEmbeddedNotebooksContainer');
|
||||
},
|
||||
async expectEmbeddedConsoleNotebookListItemToBeAvailable(id: string) {
|
||||
await testSubjects.existOrFail(`console-embedded-notebook-select-btn-${id}`);
|
||||
},
|
||||
async clickEmbeddedConsoleNotebook(id: string) {
|
||||
await testSubjects.click(`console-embedded-notebook-select-btn-${id}`);
|
||||
},
|
||||
async expectEmbeddedConsoleNotebookToBeAvailable(id: string) {
|
||||
await testSubjects.click(`console-embedded-notebook-select-btn-${id}`);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
'svlCommonNavigation',
|
||||
'common',
|
||||
'svlSearchConnectorsPage',
|
||||
'embeddedConsole',
|
||||
]);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects(['svlCommonPage', 'svlCommonNavigation']);
|
||||
export default function ({ getPageObjects }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects(['svlCommonPage', 'embeddedConsole']);
|
||||
|
||||
describe('Console Notebooks', function () {
|
||||
before(async () => {
|
||||
|
@ -17,39 +17,39 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('has notebooks view available', async () => {
|
||||
// Expect Console Bar & Notebooks button to exist
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleControlBarExists();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebooksButtonExists();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleControlBarExists();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebooksButtonExists();
|
||||
|
||||
// Click the Notebooks button to open console to See Notebooks
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleNotebooksButton();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebooksToBeOpen();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleNotebooksButton();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebooksToBeOpen();
|
||||
|
||||
// Click the Notebooks button again to switch to the dev console
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleNotebooksButton();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeOpen();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebooksToBeClosed();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleNotebooksButton();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebooksToBeClosed();
|
||||
|
||||
// Clicking control bar should close the console
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebooksToBeClosed();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeClosed();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebooksToBeClosed();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeClosed();
|
||||
|
||||
// Re-open console and then open Notebooks
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeOpen();
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleNotebooksButton();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebooksToBeOpen();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleNotebooksButton();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebooksToBeOpen();
|
||||
|
||||
// Close the console
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebooksToBeClosed();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeClosed();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebooksToBeClosed();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeClosed();
|
||||
});
|
||||
|
||||
it('can open notebooks', async () => {
|
||||
// Click the Notebooks button to open console to See Notebooks
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleNotebooksButton();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebooksToBeOpen();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleNotebooksButton();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebooksToBeOpen();
|
||||
|
||||
const defaultNotebooks = [
|
||||
'00_quick_start',
|
||||
|
@ -59,13 +59,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
'04_multilingual',
|
||||
];
|
||||
for (const notebookId of defaultNotebooks) {
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebookListItemToBeAvailable(
|
||||
notebookId
|
||||
);
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleNotebook(notebookId);
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleNotebookToBeAvailable(
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebookListItemToBeAvailable(
|
||||
notebookId
|
||||
);
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleNotebook(notebookId);
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleNotebookToBeAvailable(notebookId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
type PageObjects = Pick<ReturnType<FtrProviderContext['getPageObjects']>, 'svlCommonNavigation'>;
|
||||
type PageObjects = Pick<ReturnType<FtrProviderContext['getPageObjects']>, 'embeddedConsole'>;
|
||||
|
||||
export async function testHasEmbeddedConsole(pageObjects: PageObjects) {
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleControlBarExists();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeClosed();
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeOpen();
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeClosed();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleControlBarExists();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeClosed();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeClosed();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { testHasEmbeddedConsole } from './embedded_console';
|
|||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects([
|
||||
'svlCommonPage',
|
||||
'svlCommonNavigation',
|
||||
'embeddedConsole',
|
||||
'common',
|
||||
'header',
|
||||
'indexManagement',
|
||||
|
|
|
@ -10,11 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
|||
import { testHasEmbeddedConsole } from './embedded_console';
|
||||
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects([
|
||||
'svlSearchLandingPage',
|
||||
'svlCommonPage',
|
||||
'svlCommonNavigation',
|
||||
]);
|
||||
const pageObjects = getPageObjects(['svlSearchLandingPage', 'svlCommonPage', 'embeddedConsole']);
|
||||
const svlSearchNavigation = getService('svlSearchNavigation');
|
||||
|
||||
describe('landing page', function () {
|
||||
|
|
|
@ -14,6 +14,7 @@ export default function ({ getPageObjects }: FtrProviderContext) {
|
|||
'common',
|
||||
'svlIngestPipelines',
|
||||
'svlManagementPage',
|
||||
'embeddedConsole',
|
||||
]);
|
||||
describe('ingest pipelines', function () {
|
||||
before(async () => {
|
||||
|
|
|
@ -11,7 +11,12 @@ import { RoleCredentials } from '../../../shared/services';
|
|||
import { testHasEmbeddedConsole } from './embedded_console';
|
||||
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects(['svlCommonPage', 'svlCommonNavigation', 'svlSearchHomePage']);
|
||||
const pageObjects = getPageObjects([
|
||||
'svlCommonPage',
|
||||
'svlCommonNavigation',
|
||||
'svlSearchHomePage',
|
||||
'embeddedConsole',
|
||||
]);
|
||||
const svlUserManager = getService('svlUserManager');
|
||||
const uiSettings = getService('uiSettings');
|
||||
let roleAuthc: RoleCredentials;
|
||||
|
@ -56,11 +61,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
it('has console quickstart link on page', async () => {
|
||||
await pageObjects.svlSearchHomePage.expectConsoleLinkExists();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeClosed();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeClosed();
|
||||
await pageObjects.svlSearchHomePage.clickConsoleLink();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeOpen();
|
||||
await pageObjects.svlCommonNavigation.devConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.svlCommonNavigation.devConsole.expectEmbeddedConsoleToBeClosed();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen();
|
||||
await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar();
|
||||
await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeClosed();
|
||||
});
|
||||
|
||||
it('has endpoints link and flyout', async () => {
|
||||
|
|
|
@ -15,7 +15,12 @@ import { createLlmProxy, LlmProxy } from './utils/create_llm_proxy';
|
|||
const esArchiveIndex = 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index';
|
||||
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects(['svlCommonPage', 'svlCommonNavigation', 'searchPlayground']);
|
||||
const pageObjects = getPageObjects([
|
||||
'svlCommonPage',
|
||||
'svlCommonNavigation',
|
||||
'searchPlayground',
|
||||
'embeddedConsole',
|
||||
]);
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const svlUserManager = getService('svlUserManager');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
|
|
|
@ -39,10 +39,6 @@
|
|||
"@kbn/apm-plugin",
|
||||
"@kbn/server-route-repository",
|
||||
"@kbn/core-chrome-browser",
|
||||
"@kbn/default-nav-ml",
|
||||
"@kbn/default-nav-analytics",
|
||||
"@kbn/default-nav-management",
|
||||
"@kbn/default-nav-devtools",
|
||||
"@kbn/security-plugin",
|
||||
"@kbn/security-solution-plugin",
|
||||
"@kbn/security-solution-plugin/public/management/cypress",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue