-
-
-
-
+
+
-
+
General
-
-
-
-
-
-
-
- ,
- "settingsCount": -1,
- }
+
+
+
+
+
+
+
+
+ ,
+ "settingsCount": -1,
}
- />
-
-
-
-
-
+ }
+ />
+
+
+
+
+ <_EuiSplitPanelInner>
-
-
+
+
-
-
-
-
+
+
-
+
Dashboard
-
-
-
-
+
+
+
+
+ <_EuiSplitPanelInner>
-
-
+
+
-
-
-
-
+
+
-
+
X-pack
-
-
-
-
-
-
-
- ,
- "settingsCount": 9,
- }
+
+
+
+
+
+
+
+
+ ,
+ "settingsCount": 9,
}
- />
-
-
-
-
-
+ }
+ />
+
+
+
+
+ <_EuiSplitPanelInner>
-
-
+
+
diff --git a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx
index d953bb8f6a6f6..0b08a317e87c9 100644
--- a/src/plugins/advanced_settings/public/management_app/components/form/form.tsx
+++ b/src/plugins/advanced_settings/public/management_app/components/form/form.tsx
@@ -11,16 +11,16 @@ import React, { PureComponent, Fragment } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
- EuiForm,
+ EuiSplitPanel,
EuiLink,
- EuiPanel,
+ EuiCallOut,
EuiSpacer,
- EuiText,
EuiTextColor,
EuiBottomBar,
EuiButton,
EuiToolTip,
EuiButtonEmpty,
+ EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { isEmpty } from 'lodash';
@@ -47,6 +47,7 @@ interface FormProps {
dockLinks: DocLinksStart['links'];
toasts: ToastsStart;
trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void;
+ queryText?: string;
}
interface FormState {
@@ -241,17 +242,18 @@ export class Form extends PureComponent
{
renderCategory(category: Category, settings: FieldSetting[], totalSettings: number) {
return (
-
-
-
-
-
+
+
+
+
+
{getCategoryName(category)}
-
- {this.renderClearQueryLink(totalSettings, settings.length)}
-
-
-
+
+
+ {this.renderClearQueryLink(totalSettings, settings.length)}
+
+
+
{settings.map((setting) => {
return (
{
/>
);
})}
-
-
+
+
);
@@ -276,22 +278,28 @@ export class Form extends PureComponent {
maybeRenderNoSettings(clearQuery: FormProps['clearQuery']) {
if (this.props.showNoResultsMessage) {
return (
-
-
-
-
- ),
- }}
- />
-
+
+
+
+
+ ),
+ queryText: {this.props.queryText},
+ }}
+ />
+ >
+ }
+ />
);
}
return null;
diff --git a/src/plugins/management/public/components/landing/landing.tsx b/src/plugins/management/public/components/landing/landing.tsx
index 5e361cfaff87d..4683185a5a783 100644
--- a/src/plugins/management/public/components/landing/landing.tsx
+++ b/src/plugins/management/public/components/landing/landing.tsx
@@ -8,15 +8,7 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
-
-import {
- EuiHorizontalRule,
- EuiIcon,
- EuiPageContent,
- EuiSpacer,
- EuiText,
- EuiTitle,
-} from '@elastic/eui';
+import { EuiEmptyPrompt, EuiHorizontalRule, EuiPageContent } from '@elastic/eui';
interface ManagementLandingPageProps {
version: string;
@@ -27,39 +19,37 @@ export const ManagementLandingPage = ({ version, setBreadcrumbs }: ManagementLan
setBreadcrumbs();
return (
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+ }
+ body={
+ <>
+
+
+
+
+
+
+
+ >
+ }
+ />
);
};
diff --git a/src/plugins/management/public/components/management_app/management_app.tsx b/src/plugins/management/public/components/management_app/management_app.tsx
index 350e2f3de0cdf..23d0a29083747 100644
--- a/src/plugins/management/public/components/management_app/management_app.tsx
+++ b/src/plugins/management/public/components/management_app/management_app.tsx
@@ -5,20 +5,23 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
+import './management_app.scss';
import React, { useState, useEffect, useCallback } from 'react';
import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from 'kibana/public';
import { I18nProvider } from '@kbn/i18n/react';
-import { EuiPage } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
import { ManagementSection, MANAGEMENT_BREADCRUMB } from '../../utils';
import { ManagementRouter } from './management_router';
-import { ManagementSidebarNav } from '../management_sidebar_nav';
-import { reactRouterNavigate } from '../../../../kibana_react/public';
+import { managementSidebarNav } from '../management_sidebar_nav/management_sidebar_nav';
+import {
+ KibanaPageTemplate,
+ KibanaPageTemplateProps,
+ reactRouterNavigate,
+} from '../../../../kibana_react/public';
import { SectionsServiceStart } from '../../types';
-import './management_app.scss';
-
interface ManagementAppProps {
appBasePath: string;
history: AppMountParameters['history'];
@@ -64,10 +67,30 @@ export const ManagementApp = ({ dependencies, history }: ManagementAppProps) =>
return null;
}
+ const solution: KibanaPageTemplateProps['solutionNav'] = {
+ name: i18n.translate('management.nav.label', {
+ defaultMessage: 'Management',
+ }),
+ icon: 'managementApp',
+ 'data-test-subj': 'mgtSideBarNav',
+ items: managementSidebarNav({
+ selectedId,
+ sections,
+ history,
+ }),
+ };
+
return (
-
-
+
sections={sections}
dependencies={dependencies}
/>
-
+
);
};
diff --git a/src/plugins/management/public/components/management_app/management_router.tsx b/src/plugins/management/public/components/management_app/management_router.tsx
index b3f978d5c6cd2..5ec1253ba77ef 100644
--- a/src/plugins/management/public/components/management_app/management_router.tsx
+++ b/src/plugins/management/public/components/management_app/management_router.tsx
@@ -8,7 +8,6 @@
import React, { memo } from 'react';
import { Route, Router, Switch } from 'react-router-dom';
-import { EuiPageBody } from '@elastic/eui';
import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from 'kibana/public';
import { ManagementAppWrapper } from '../management_app_wrapper';
import { ManagementLandingPage } from '../landing';
@@ -26,36 +25,34 @@ interface ManagementRouterProps {
export const ManagementRouter = memo(
({ dependencies, history, setBreadcrumbs, onAppMounted, sections }: ManagementRouterProps) => (
-
-
- {sections.map((section) =>
- section
- .getAppsEnabled()
- .map((app) => (
- (
-
- )}
- />
- ))
- )}
- (
-
+ {sections.map((section) =>
+ section
+ .getAppsEnabled()
+ .map((app) => (
+ (
+
+ )}
/>
- )}
- />
-
-
+ ))
+ )}
+ (
+
+ )}
+ />
+
)
);
diff --git a/src/plugins/management/public/components/management_app_wrapper/management_app_wrapper.tsx b/src/plugins/management/public/components/management_app_wrapper/management_app_wrapper.tsx
index 8e0043937303f..72bfe609c141a 100644
--- a/src/plugins/management/public/components/management_app_wrapper/management_app_wrapper.tsx
+++ b/src/plugins/management/public/components/management_app_wrapper/management_app_wrapper.tsx
@@ -9,8 +9,10 @@
import React, { createRef, Component } from 'react';
import { ChromeBreadcrumb, AppMountParameters, ScopedHistory } from 'kibana/public';
+import classNames from 'classnames';
import { ManagementApp } from '../../utils';
import { Unmount } from '../../types';
+import { APP_WRAPPER_CLASS } from '../../../../../../src/core/public';
interface ManagementSectionWrapperProps {
app: ManagementApp;
@@ -53,6 +55,12 @@ export class ManagementAppWrapper extends Component;
+ return (
+
+ );
}
}
diff --git a/src/plugins/management/public/components/management_sidebar_nav/index.ts b/src/plugins/management/public/components/management_sidebar_nav/index.ts
index 5bb80adf3e8ba..9cee992a17948 100644
--- a/src/plugins/management/public/components/management_sidebar_nav/index.ts
+++ b/src/plugins/management/public/components/management_sidebar_nav/index.ts
@@ -6,4 +6,4 @@
* Side Public License, v 1.
*/
-export { ManagementSidebarNav } from './management_sidebar_nav';
+export { managementSidebarNav } from './management_sidebar_nav';
diff --git a/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx b/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx
index 1b40bfcca3c63..bf1b81061f6a9 100644
--- a/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx
+++ b/src/plugins/management/public/components/management_sidebar_nav/management_sidebar_nav.tsx
@@ -6,24 +6,13 @@
* Side Public License, v 1.
*/
-import React, { useState } from 'react';
-import { i18n } from '@kbn/i18n';
+import React from 'react';
import { sortBy } from 'lodash';
-import {
- EuiIcon,
- EuiSideNav,
- EuiScreenReaderOnly,
- EuiSideNavItemType,
- EuiFlexGroup,
- EuiFlexItem,
- EuiToolTip,
-} from '@elastic/eui';
+import { EuiIcon, EuiSideNavItemType, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { AppMountParameters } from 'kibana/public';
import { ManagementApp, ManagementSection } from '../../utils';
-import './management_sidebar_nav.scss';
-
import { ManagementItem } from '../../utils/management_item';
import { reactRouterNavigate } from '../../../../kibana_react/public';
@@ -33,24 +22,12 @@ interface ManagementSidebarNavProps {
selectedId: string;
}
-const headerLabel = i18n.translate('management.nav.label', {
- defaultMessage: 'Management',
-});
-
-const navMenuLabel = i18n.translate('management.nav.menu', {
- defaultMessage: 'Management menu',
-});
-
/** @internal **/
-export const ManagementSidebarNav = ({
+export const managementSidebarNav = ({
selectedId,
sections,
history,
}: ManagementSidebarNavProps) => {
- const HEADER_ID = 'stack-management-nav-header';
- const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false);
- const toggleOpenOnMobile = () => setIsSideNavOpenOnMobile(!isSideNavOpenOnMobile);
-
const sectionsToNavItems = (managementSections: ManagementSection[]) => {
const sortedManagementSections = sortBy(managementSections, 'order');
@@ -83,11 +60,11 @@ export const ManagementSidebarNav = ({
const TooltipWrapper = ({ text, tip }: TooltipWrapperProps) => (
-
+
{text}
-
+
@@ -109,19 +86,5 @@ export const ManagementSidebarNav = ({
};
};
- return (
- <>
-
-
-
-
- >
- );
+ return sectionsToNavItems(sections);
};
diff --git a/test/functional/services/management/management_menu.ts b/test/functional/services/management/management_menu.ts
index 738a8d55439ec..2b93fce4daa51 100644
--- a/test/functional/services/management/management_menu.ts
+++ b/test/functional/services/management/management_menu.ts
@@ -13,7 +13,7 @@ export class ManagementMenuService extends FtrService {
public async getSections() {
const sectionsElements = await this.find.allByCssSelector(
- '.mgtSideBarNav > .euiSideNav__content > .euiSideNavItem'
+ '.kbnPageTemplateSolutionNav .euiSideNavItem--root'
);
const sections = [];
diff --git a/x-pack/plugins/index_management/public/application/sections/home/home.tsx b/x-pack/plugins/index_management/public/application/sections/home/home.tsx
index 12ccf089b85fb..003aa045f9591 100644
--- a/x-pack/plugins/index_management/public/application/sections/home/home.tsx
+++ b/x-pack/plugins/index_management/public/application/sections/home/home.tsx
@@ -8,17 +8,7 @@
import React, { useEffect } from 'react';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n/react';
-import {
- EuiButtonEmpty,
- EuiFlexGroup,
- EuiFlexItem,
- EuiPageBody,
- EuiPageContent,
- EuiSpacer,
- EuiTab,
- EuiTabs,
- EuiTitle,
-} from '@elastic/eui';
+import { EuiButtonEmpty, EuiPageHeader, EuiSpacer } from '@elastic/eui';
import { documentationService } from '../../services/documentation';
import { DataStreamList } from './data_stream_list';
import { IndexList } from './index_list';
@@ -93,73 +83,59 @@ export const IndexManagementHome: React.FunctionComponent
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ <>
+
+
+
+ }
+ bottomBorder
+ rightSideItems={[
+
+
+ ,
+ ]}
+ tabs={tabs.map((tab) => ({
+ onClick: () => onSectionChange(tab.id),
+ isSelected: tab.id === section,
+ key: tab.id,
+ 'data-test-subj': `${tab.id}Tab`,
+ label: tab.name,
+ }))}
+ />
-
+
-
- {tabs.map((tab) => (
- onSectionChange(tab.id)}
- isSelected={tab.id === section}
- key={tab.id}
- data-test-subj={`${tab.id}Tab`}
- >
- {tab.name}
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ >
);
};
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
index 442c1d910f814..452b119e83921 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
@@ -34,6 +34,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import type { PublicMethodsOf } from '@kbn/utility-types';
import type { NotificationsStart } from 'src/core/public';
+import { APP_WRAPPER_CLASS } from '../../../../../../../src/core/public';
import { SectionLoading } from '../../../../../../../src/plugins/es_ui_shared/public';
import { reactRouterNavigate } from '../../../../../../../src/plugins/kibana_react/public';
import type { ApiKey, ApiKeyToInvalidate } from '../../../../common/model';
@@ -88,7 +89,7 @@ export class APIKeysGridPage extends Component {
public render() {
return (
-
+
{
if (!areApiKeysEnabled) {
return (
-
+
);
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx
index cd74c0de4dfcf..70199189ca7bf 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/not_enabled/not_enabled.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { EuiCallOut, EuiLink } from '@elastic/eui';
+import { EuiEmptyPrompt, EuiLink } from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -15,30 +15,35 @@ import { useKibana } from '../../../../../../../../src/plugins/kibana_react/publ
export const NotEnabled: React.FunctionComponent = () => {
const docLinks = useKibana().services.docLinks!;
return (
-
+
+
+
}
color="danger"
iconType="alert"
- >
-
-
-
- ),
- }}
- />
-
+ body={
+
+
+
+
+ ),
+ }}
+ />
+
+ }
+ />
);
};
diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx
index f5f6d2daf306d..0e8b1c18fdc1c 100644
--- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx
+++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx
@@ -4,7 +4,6 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-
import {
EuiButton,
EuiButtonIcon,
@@ -14,15 +13,11 @@ import {
EuiInMemoryTable,
EuiLink,
EuiPageContent,
- EuiPageContentBody,
- EuiPageContentHeader,
- EuiPageContentHeaderSection,
+ EuiPageHeader,
EuiSpacer,
- EuiText,
- EuiTitle,
EuiToolTip,
} from '@elastic/eui';
-import React, { Component, Fragment } from 'react';
+import React, { Component } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -129,49 +124,42 @@ export class RoleMappingsGridPage extends Component {
if (loadState === 'finished' && roleMappings && roleMappings.length === 0) {
return (
-
+
);
}
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- ),
- }}
- />
-
-
-
-
+ <>
+
+ }
+ description={
+
+
+
+ ),
+ }}
+ />
+ }
+ rightSideItems={[
@@ -179,21 +167,20 @@ export class RoleMappingsGridPage extends Component {
id="xpack.security.management.roleMappings.createRoleMappingButtonLabel"
defaultMessage="Create role mapping"
/>
-
-
-
-
-
- {!this.state.hasCompatibleRealms && (
- <>
-
-
- >
- )}
- {this.renderTable()}
-
-
-
+ ,
+ ]}
+ />
+
+
+
+ {!this.state.hasCompatibleRealms && (
+ <>
+
+
+ >
+ )}
+ {this.renderTable()}
+ >
);
}
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index c571e2da6f76f..26521b420ae25 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -114,7 +114,6 @@
"advancedSettings.form.cancelButtonLabel": "変更をキャンセル",
"advancedSettings.form.clearNoSearchResultText": " (検索結果を消去) ",
"advancedSettings.form.clearSearchResultText": " (検索結果を消去) ",
- "advancedSettings.form.noSearchResultText": "設定が見つかりませんでした {clearSearch}",
"advancedSettings.form.requiresPageReloadToastButtonLabel": "ページを再読み込み",
"advancedSettings.form.requiresPageReloadToastDescription": "設定を有効にするためにページの再読み込みが必要です。",
"advancedSettings.form.saveButtonLabel": "変更を保存",
@@ -3248,7 +3247,6 @@
"management.landing.subhead": "インデックス、インデックスパターン、保存されたオブジェクト、Kibanaの設定、その他を管理します。",
"management.landing.text": "アプリの一覧は左側のメニューにあります。",
"management.nav.label": "管理",
- "management.nav.menu": "管理メニュー",
"management.sections.dataTip": "クラスターデータとバックアップを管理します",
"management.sections.dataTitle": "データ",
"management.sections.ingestTip": "データを変換し、クラスターに読み込む方法を管理します",
@@ -4915,17 +4913,6 @@
"visTypeVislib.heatmap.metricTitle": "値",
"visTypeVislib.heatmap.segmentTitle": "X 軸",
"visTypeVislib.heatmap.splitTitle": "チャートを分割",
- "visTypePie.pie.metricTitle": "スライスサイズ",
- "visTypePie.pie.pieDescription": "全体に対する比率でデータを比較します。",
- "visTypePie.pie.pieTitle": "円",
- "visTypePie.pie.segmentTitle": "スライスの分割",
- "visTypePie.pie.splitTitle": "チャートを分割",
- "visTypePie.editors.pie.donutLabel": "ドーナッツ",
- "visTypePie.editors.pie.labelsSettingsTitle": "ラベル設定",
- "visTypePie.editors.pie.pieSettingsTitle": "パイ設定",
- "visTypePie.editors.pie.showLabelsLabel": "ラベルを表示",
- "visTypePie.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示",
- "visTypePie.editors.pie.showValuesLabel": "値を表示",
"visTypeVislib.vislib.errors.noResultsFoundTitle": "結果が見つかりませんでした",
"visTypeVislib.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr}) 。構成されている最大値は {max} です。",
"visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "値 {legendDataLabel} でフィルタリング",
@@ -4937,8 +4924,56 @@
"visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション",
"visTypeVislib.vislib.tooltip.fieldLabel": "フィールド",
"visTypeVislib.vislib.tooltip.valueLabel": "値",
+ "visTypePie.pie.metricTitle": "スライスサイズ",
+ "visTypePie.pie.pieDescription": "全体に対する比率でデータを比較します。",
+ "visTypePie.pie.pieTitle": "円",
+ "visTypePie.pie.segmentTitle": "スライスの分割",
+ "visTypePie.pie.splitTitle": "チャートを分割",
+ "visTypePie.editors.pie.donutLabel": "ドーナッツ",
+ "visTypePie.editors.pie.labelsSettingsTitle": "ラベル設定",
+ "visTypePie.editors.pie.pieSettingsTitle": "パイ設定",
+ "visTypePie.editors.pie.showLabelsLabel": "ラベルを表示",
+ "visTypePie.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示",
+ "visTypePie.editors.pie.showValuesLabel": "値を表示",
"visualizations.advancedSettings.visualization.legacyChartsLibrary.description": "Visualizeでエリア、折れ線、棒グラフのレガシーグラフライブラリを有効にします。",
"visualizations.advancedSettings.visualization.legacyChartsLibrary.name": "レガシーグラフライブラリ",
+ "visualizations.advancedSettings.visualizeEnableLabsText": "ユーザーが実験的なビジュアライゼーションを作成、表示、編集できるようになります。無効の場合、\n ユーザーは本番準備が整ったビジュアライゼーションのみを利用できます。",
+ "visualizations.advancedSettings.visualizeEnableLabsTitle": "実験的なビジュアライゼーションを有効にする",
+ "visualizations.disabledLabVisualizationLink": "ドキュメンテーションを表示",
+ "visualizations.disabledLabVisualizationMessage": "ラボビジュアライゼーションを表示するには、高度な設定でラボモードをオンにしてください。",
+ "visualizations.disabledLabVisualizationTitle": "{title} はラボビジュアライゼーションです。",
+ "visualizations.displayName": "ビジュアライゼーション",
+ "visualizations.embeddable.placeholderTitle": "プレースホルダータイトル",
+ "visualizations.function.range.from.help": "範囲の開始",
+ "visualizations.function.range.help": "範囲オブジェクトを生成します",
+ "visualizations.function.range.to.help": "範囲の終了",
+ "visualizations.function.visDimension.accessor.help": "使用するデータセット内の列 (列インデックスまたは列名) ",
+ "visualizations.function.visDimension.error.accessor": "入力された列名は無効です。",
+ "visualizations.function.visDimension.format.help": "フォーマット",
+ "visualizations.function.visDimension.formatParams.help": "フォーマットパラメーター",
+ "visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します",
+ "visualizations.initializeWithoutIndexPatternErrorMessage": "インデックスパターンなしで集約を初期化しようとしています",
+ "visualizations.newVisWizard.aggBasedGroupDescription": "クラシック Visualize ライブラリを使用して、アグリゲーションに基づいてグラフを作成します。",
+ "visualizations.newVisWizard.aggBasedGroupTitle": "アグリゲーションに基づく",
+ "visualizations.newVisWizard.chooseSourceTitle": "ソースの選択",
+ "visualizations.newVisWizard.experimentalTitle": "実験的",
+ "visualizations.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。",
+ "visualizations.newVisWizard.exploreOptionLinkText": "探索オプション",
+ "visualizations.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング",
+ "visualizations.newVisWizard.goBackLink": "別のビジュアライゼーションを選択",
+ "visualizations.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモーダルを閉じます。Tab キーを押して次に進みます。",
+ "visualizations.newVisWizard.learnMoreText": "詳細について",
+ "visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}",
+ "visualizations.newVisWizard.readDocumentationLink": "ドキュメンテーションを表示",
+ "visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。",
+ "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン",
+ "visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索",
+ "visualizations.newVisWizard.title": "新規ビジュアライゼーション",
+ "visualizations.newVisWizard.toolsGroupTitle": "ツール",
+ "visualizations.noResultsFoundTitle": "結果が見つかりませんでした",
+ "visualizations.savedObjectName": "ビジュアライゼーション",
+ "visualizations.savingVisualizationFailed.errorMsg": "ビジュアライゼーションの保存が失敗しました",
+ "visualizations.visualizationTypeInvalidMessage": "無効なビジュアライゼーションタイプ \"{visType}\"",
"visTypeXy.aggResponse.allDocsTitle": "すべてのドキュメント",
"visTypeXy.area.areaDescription": "軸と線の間のデータを強調します。",
"visTypeXy.area.areaTitle": "エリア",
@@ -5060,43 +5095,6 @@
"visTypeXy.thresholdLine.style.dashedText": "鎖線",
"visTypeXy.thresholdLine.style.dotdashedText": "点線",
"visTypeXy.thresholdLine.style.fullText": "完全",
- "visualizations.advancedSettings.visualizeEnableLabsText": "ユーザーが実験的なビジュアライゼーションを作成、表示、編集できるようになります。無効の場合、\n ユーザーは本番準備が整ったビジュアライゼーションのみを利用できます。",
- "visualizations.advancedSettings.visualizeEnableLabsTitle": "実験的なビジュアライゼーションを有効にする",
- "visualizations.disabledLabVisualizationLink": "ドキュメンテーションを表示",
- "visualizations.disabledLabVisualizationMessage": "ラボビジュアライゼーションを表示するには、高度な設定でラボモードをオンにしてください。",
- "visualizations.disabledLabVisualizationTitle": "{title} はラボビジュアライゼーションです。",
- "visualizations.displayName": "ビジュアライゼーション",
- "visualizations.embeddable.placeholderTitle": "プレースホルダータイトル",
- "visualizations.function.range.from.help": "範囲の開始",
- "visualizations.function.range.help": "範囲オブジェクトを生成します",
- "visualizations.function.range.to.help": "範囲の終了",
- "visualizations.function.visDimension.accessor.help": "使用するデータセット内の列 (列インデックスまたは列名) ",
- "visualizations.function.visDimension.error.accessor": "入力された列名は無効です。",
- "visualizations.function.visDimension.format.help": "フォーマット",
- "visualizations.function.visDimension.formatParams.help": "フォーマットパラメーター",
- "visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します",
- "visualizations.initializeWithoutIndexPatternErrorMessage": "インデックスパターンなしで集約を初期化しようとしています",
- "visualizations.newVisWizard.aggBasedGroupDescription": "クラシック Visualize ライブラリを使用して、アグリゲーションに基づいてグラフを作成します。",
- "visualizations.newVisWizard.aggBasedGroupTitle": "アグリゲーションに基づく",
- "visualizations.newVisWizard.chooseSourceTitle": "ソースの選択",
- "visualizations.newVisWizard.experimentalTitle": "実験的",
- "visualizations.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。",
- "visualizations.newVisWizard.exploreOptionLinkText": "探索オプション",
- "visualizations.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング",
- "visualizations.newVisWizard.goBackLink": "別のビジュアライゼーションを選択",
- "visualizations.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモーダルを閉じます。Tab キーを押して次に進みます。",
- "visualizations.newVisWizard.learnMoreText": "詳細について",
- "visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}",
- "visualizations.newVisWizard.readDocumentationLink": "ドキュメンテーションを表示",
- "visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。",
- "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン",
- "visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索",
- "visualizations.newVisWizard.title": "新規ビジュアライゼーション",
- "visualizations.newVisWizard.toolsGroupTitle": "ツール",
- "visualizations.noResultsFoundTitle": "結果が見つかりませんでした",
- "visualizations.savedObjectName": "ビジュアライゼーション",
- "visualizations.savingVisualizationFailed.errorMsg": "ビジュアライゼーションの保存が失敗しました",
- "visualizations.visualizationTypeInvalidMessage": "無効なビジュアライゼーションタイプ \"{visType}\"",
"visualize.badge.readOnly.text": "読み取り専用",
"visualize.badge.readOnly.tooltip": "ビジュアライゼーションをライブラリに保存できません",
"visualize.byValue_pageHeading": "{originatingApp}アプリに埋め込まれた{chartType}タイプのビジュアライゼーション",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index bb0b740609594..bbc5554bbb6a2 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -115,7 +115,6 @@
"advancedSettings.form.clearNoSearchResultText": " (清除搜索) ",
"advancedSettings.form.clearSearchResultText": " (清除搜索) ",
"advancedSettings.form.countOfSettingsChanged": "{unsavedCount} 个未保存{unsavedCount, plural, other {设置} }{hiddenCount, plural, =0 {} other {,# 个已隐藏} }",
- "advancedSettings.form.noSearchResultText": "未找到设置{clearSearch}",
"advancedSettings.form.requiresPageReloadToastButtonLabel": "重新加载页面",
"advancedSettings.form.requiresPageReloadToastDescription": "一个或多个设置需要您重新加载页面才能生效。",
"advancedSettings.form.saveButtonLabel": "保存更改",
@@ -3270,7 +3269,6 @@
"management.landing.subhead": "管理您的索引、索引模式、已保存对象、Kibana 设置等等。",
"management.landing.text": "应用的完整列表位于左侧菜单中。",
"management.nav.label": "管理",
- "management.nav.menu": "管理菜单",
"management.sections.dataTip": "管理您的集群数据和备份",
"management.sections.dataTitle": "数据",
"management.sections.ingestTip": "管理如何转换数据并将其加载到集群中",
@@ -4942,17 +4940,6 @@
"visTypeVislib.heatmap.metricTitle": "值",
"visTypeVislib.heatmap.segmentTitle": "X 轴",
"visTypeVislib.heatmap.splitTitle": "拆分图表",
- "visTypePie.pie.metricTitle": "切片大小",
- "visTypePie.pie.pieDescription": "以整体的比例比较数据。",
- "visTypePie.pie.pieTitle": "饼图",
- "visTypePie.pie.segmentTitle": "拆分切片",
- "visTypePie.pie.splitTitle": "拆分图表",
- "visTypePie.editors.pie.donutLabel": "圆环图",
- "visTypePie.editors.pie.labelsSettingsTitle": "标签设置",
- "visTypePie.editors.pie.pieSettingsTitle": "饼图设置",
- "visTypePie.editors.pie.showLabelsLabel": "显示标签",
- "visTypePie.editors.pie.showTopLevelOnlyLabel": "仅显示顶级",
- "visTypePie.editors.pie.showValuesLabel": "显示值",
"visTypeVislib.vislib.errors.noResultsFoundTitle": "找不到结果",
"visTypeVislib.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。",
"visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "筛留值 {legendDataLabel}",
@@ -4964,8 +4951,57 @@
"visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}, 切换选项",
"visTypeVislib.vislib.tooltip.fieldLabel": "字段",
"visTypeVislib.vislib.tooltip.valueLabel": "值",
+ "visTypePie.pie.metricTitle": "切片大小",
+ "visTypePie.pie.pieDescription": "以整体的比例比较数据。",
+ "visTypePie.pie.pieTitle": "饼图",
+ "visTypePie.pie.segmentTitle": "拆分切片",
+ "visTypePie.pie.splitTitle": "拆分图表",
+ "visTypePie.editors.pie.donutLabel": "圆环图",
+ "visTypePie.editors.pie.labelsSettingsTitle": "标签设置",
+ "visTypePie.editors.pie.pieSettingsTitle": "饼图设置",
+ "visTypePie.editors.pie.showLabelsLabel": "显示标签",
+ "visTypePie.editors.pie.showTopLevelOnlyLabel": "仅显示顶级",
+ "visTypePie.editors.pie.showValuesLabel": "显示值",
"visualizations.advancedSettings.visualization.legacyChartsLibrary.description": "在 Visualize 中启用面积图、折线图和条形图的旧版图表库。",
"visualizations.advancedSettings.visualization.legacyChartsLibrary.name": "旧版图表库",
+ "visualizations.advancedSettings.visualizeEnableLabsText": "允许用户创建、查看和编辑实验性可视化。如果禁用,\n 仅被视为生产就绪的可视化可供用户使用。",
+ "visualizations.advancedSettings.visualizeEnableLabsTitle": "启用实验性可视化",
+ "visualizations.disabledLabVisualizationLink": "阅读文档",
+ "visualizations.disabledLabVisualizationMessage": "请在高级设置中打开实验模式,以查看实验性可视化。",
+ "visualizations.disabledLabVisualizationTitle": "{title} 为实验室可视化。",
+ "visualizations.displayName": "可视化",
+ "visualizations.embeddable.placeholderTitle": "占位符标题",
+ "visualizations.function.range.from.help": "范围起始",
+ "visualizations.function.range.help": "生成范围对象",
+ "visualizations.function.range.to.help": "范围结束",
+ "visualizations.function.visDimension.accessor.help": "要使用的数据集列 (列索引或列名称) ",
+ "visualizations.function.visDimension.error.accessor": "提供的列名称无效",
+ "visualizations.function.visDimension.format.help": "格式",
+ "visualizations.function.visDimension.formatParams.help": "格式参数",
+ "visualizations.function.visDimension.help": "生成 visConfig 维度对象",
+ "visualizations.initializeWithoutIndexPatternErrorMessage": "正在尝试在不使用索引模式的情况下初始化聚合",
+ "visualizations.newVisWizard.aggBasedGroupDescription": "使用我们的经典可视化库,基于聚合创建图表。",
+ "visualizations.newVisWizard.aggBasedGroupTitle": "基于聚合",
+ "visualizations.newVisWizard.chooseSourceTitle": "选择源",
+ "visualizations.newVisWizard.experimentalTitle": "实验性",
+ "visualizations.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。",
+ "visualizations.newVisWizard.exploreOptionLinkText": "浏览选项",
+ "visualizations.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型",
+ "visualizations.newVisWizard.goBackLink": "选择不同的可视化",
+ "visualizations.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。",
+ "visualizations.newVisWizard.learnMoreText": "希望了解详情?",
+ "visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}",
+ "visualizations.newVisWizard.readDocumentationLink": "阅读文档",
+ "visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {类型}}已找到",
+ "visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。",
+ "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式",
+ "visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索",
+ "visualizations.newVisWizard.title": "新建可视化",
+ "visualizations.newVisWizard.toolsGroupTitle": "工具",
+ "visualizations.noResultsFoundTitle": "找不到结果",
+ "visualizations.savedObjectName": "可视化",
+ "visualizations.savingVisualizationFailed.errorMsg": "保存可视化失败",
+ "visualizations.visualizationTypeInvalidMessage": "无效的可视化类型“{visType}”",
"visTypeXy.aggResponse.allDocsTitle": "所有文档",
"visTypeXy.area.areaDescription": "突出轴与线之间的数据。",
"visTypeXy.area.areaTitle": "面积图",
@@ -5087,44 +5123,6 @@
"visTypeXy.thresholdLine.style.dashedText": "虚线",
"visTypeXy.thresholdLine.style.dotdashedText": "点虚线",
"visTypeXy.thresholdLine.style.fullText": "实线",
- "visualizations.advancedSettings.visualizeEnableLabsText": "允许用户创建、查看和编辑实验性可视化。如果禁用,\n 仅被视为生产就绪的可视化可供用户使用。",
- "visualizations.advancedSettings.visualizeEnableLabsTitle": "启用实验性可视化",
- "visualizations.disabledLabVisualizationLink": "阅读文档",
- "visualizations.disabledLabVisualizationMessage": "请在高级设置中打开实验模式,以查看实验性可视化。",
- "visualizations.disabledLabVisualizationTitle": "{title} 为实验室可视化。",
- "visualizations.displayName": "可视化",
- "visualizations.embeddable.placeholderTitle": "占位符标题",
- "visualizations.function.range.from.help": "范围起始",
- "visualizations.function.range.help": "生成范围对象",
- "visualizations.function.range.to.help": "范围结束",
- "visualizations.function.visDimension.accessor.help": "要使用的数据集列 (列索引或列名称) ",
- "visualizations.function.visDimension.error.accessor": "提供的列名称无效",
- "visualizations.function.visDimension.format.help": "格式",
- "visualizations.function.visDimension.formatParams.help": "格式参数",
- "visualizations.function.visDimension.help": "生成 visConfig 维度对象",
- "visualizations.initializeWithoutIndexPatternErrorMessage": "正在尝试在不使用索引模式的情况下初始化聚合",
- "visualizations.newVisWizard.aggBasedGroupDescription": "使用我们的经典可视化库,基于聚合创建图表。",
- "visualizations.newVisWizard.aggBasedGroupTitle": "基于聚合",
- "visualizations.newVisWizard.chooseSourceTitle": "选择源",
- "visualizations.newVisWizard.experimentalTitle": "实验性",
- "visualizations.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。",
- "visualizations.newVisWizard.exploreOptionLinkText": "浏览选项",
- "visualizations.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型",
- "visualizations.newVisWizard.goBackLink": "选择不同的可视化",
- "visualizations.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。",
- "visualizations.newVisWizard.learnMoreText": "希望了解详情?",
- "visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}",
- "visualizations.newVisWizard.readDocumentationLink": "阅读文档",
- "visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {类型}}已找到",
- "visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。",
- "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式",
- "visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索",
- "visualizations.newVisWizard.title": "新建可视化",
- "visualizations.newVisWizard.toolsGroupTitle": "工具",
- "visualizations.noResultsFoundTitle": "找不到结果",
- "visualizations.savedObjectName": "可视化",
- "visualizations.savingVisualizationFailed.errorMsg": "保存可视化失败",
- "visualizations.visualizationTypeInvalidMessage": "无效的可视化类型“{visType}”",
"visualize.badge.readOnly.text": "只读",
"visualize.badge.readOnly.tooltip": "无法将可视化保存到库",
"visualize.byValue_pageHeading": "已嵌入到 {originatingApp} 应用中的 {chartType} 类型可视化",
From ebdda9e1104c0b3ab3b6b5cbea7cf2356a1ffeba Mon Sep 17 00:00:00 2001
From: Pierre Gayvallet
Date: Tue, 8 Jun 2021 17:27:27 +0200
Subject: [PATCH 02/13] Propagate request cancelation though basePath proxy
(#101561)
* bump `@hapi/h2o2` to `9.1.0`
* add unit test
---
package.json | 2 +-
.../src/base_path_proxy_server.test.ts | 39 +++++++++++++++++++
yarn.lock | 8 ++--
3 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index 7d4d96e840752..ea2c294b5d8e0 100644
--- a/package.json
+++ b/package.json
@@ -118,7 +118,7 @@
"@hapi/boom": "^9.1.1",
"@hapi/cookie": "^11.0.2",
"@hapi/good-squeeze": "6.0.0",
- "@hapi/h2o2": "^9.0.2",
+ "@hapi/h2o2": "^9.1.0",
"@hapi/hapi": "^20.0.3",
"@hapi/hoek": "^9.1.1",
"@hapi/inert": "^6.0.3",
diff --git a/packages/kbn-cli-dev-mode/src/base_path_proxy_server.test.ts b/packages/kbn-cli-dev-mode/src/base_path_proxy_server.test.ts
index a0afbe3a9b8c9..34c6be02847a7 100644
--- a/packages/kbn-cli-dev-mode/src/base_path_proxy_server.test.ts
+++ b/packages/kbn-cli-dev-mode/src/base_path_proxy_server.test.ts
@@ -185,6 +185,45 @@ describe('BasePathProxyServer', () => {
});
});
+ test('forwards request cancellation', async () => {
+ let propagated = false;
+
+ let notifyRequestReceived: () => void;
+ const requestReceived = new Promise((resolve) => {
+ notifyRequestReceived = resolve;
+ });
+
+ let notifyRequestAborted: () => void;
+ const requestAborted = new Promise((resolve) => {
+ notifyRequestAborted = resolve;
+ });
+
+ server.route({
+ method: 'GET',
+ path: `${basePath}/foo/{test}`,
+ handler: async (request, h) => {
+ notifyRequestReceived();
+
+ request.raw.req.once('aborted', () => {
+ notifyRequestAborted();
+ propagated = true;
+ });
+ return await new Promise((resolve) => undefined);
+ },
+ });
+ await server.start();
+
+ const request = proxySupertest.get(`${basePath}/foo/some-string`).end();
+
+ await requestReceived;
+
+ request.abort();
+
+ await requestAborted;
+
+ expect(propagated).toEqual(true);
+ });
+
test('handles putting', async () => {
server.route({
method: 'PUT',
diff --git a/yarn.lock b/yarn.lock
index 5a9b30fbd9dd2..17f61f0b408d2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1871,10 +1871,10 @@
"@hapi/hoek" "9.x.x"
fast-safe-stringify "2.x.x"
-"@hapi/h2o2@^9.0.2":
- version "9.0.2"
- resolved "https://registry.yarnpkg.com/@hapi/h2o2/-/h2o2-9.0.2.tgz#e9f1dfe789257c80d6ee37ec9fe358f8c69f855a"
- integrity sha512-V7RsmVyl7uyWeuEko4uaSZbFpBHKcSFSui6PXNRaRLJHFX+iPbqWmeH6m1pW/WJ8DuaCVJFKhluDCDI9l4+1cw==
+"@hapi/h2o2@^9.1.0":
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/@hapi/h2o2/-/h2o2-9.1.0.tgz#b223f4978b6f2b0d7d9db10a84a567606c4c3551"
+ integrity sha512-B7E58bMhxmpiDI22clxTexoAaVShNBk1Ez6S8SQjQZu5FxxD6Tqa44sXeZQBtWrdJF7ZRbsY60/C8AHLRxagNA==
dependencies:
"@hapi/boom" "9.x.x"
"@hapi/hoek" "9.x.x"
From 9acbce963bc34c3fdc4ccab23225d915fe1c73ce Mon Sep 17 00:00:00 2001
From: Thomas Watson
Date: Tue, 8 Jun 2021 17:40:25 +0200
Subject: [PATCH 03/13] Upgrade cheerio from v0.22.0 to v1.0.0-rc.9 (#101394)
---
package.json | 3 +-
packages/kbn-pm/dist/index.js | 33 ++--
.../web_element_wrapper.ts | 1 -
yarn.lock | 181 +++++++++++-------
4 files changed, 131 insertions(+), 87 deletions(-)
diff --git a/package.json b/package.json
index ea2c294b5d8e0..6f5994a8679d6 100644
--- a/package.json
+++ b/package.json
@@ -198,7 +198,7 @@
"broadcast-channel": "^3.0.3",
"chalk": "^4.1.0",
"check-disk-space": "^2.1.0",
- "cheerio": "0.22.0",
+ "cheerio": "^1.0.0-rc.9",
"chokidar": "^3.4.3",
"chroma-js": "^1.4.1",
"classnames": "2.2.6",
@@ -501,7 +501,6 @@
"@types/base64-js": "^1.2.5",
"@types/bluebird": "^3.1.1",
"@types/chance": "^1.0.0",
- "@types/cheerio": "^0.22.28",
"@types/chroma-js": "^1.4.2",
"@types/chromedriver": "^81.0.0",
"@types/classnames": "^2.2.9",
diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js
index 5b064e492beb5..e455f487d1384 100644
--- a/packages/kbn-pm/dist/index.js
+++ b/packages/kbn-pm/dist/index.js
@@ -650,6 +650,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__read", function() { return __read; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__spread", function() { return __spread; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__spreadArrays", function() { return __spreadArrays; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__spreadArray", function() { return __spreadArray; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__await", function() { return __await; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__asyncGenerator", function() { return __asyncGenerator; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "__asyncDelegator", function() { return __asyncDelegator; });
@@ -683,6 +684,8 @@ var extendStatics = function(d, b) {
};
function __extends(d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@@ -805,19 +808,27 @@ function __read(o, n) {
return ar;
}
+/** @deprecated */
function __spread() {
for (var ar = [], i = 0; i < arguments.length; i++)
ar = ar.concat(__read(arguments[i]));
return ar;
}
+/** @deprecated */
function __spreadArrays() {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
-};
+}
+
+function __spreadArray(to, from) {
+ for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
+ to[j] = from[i];
+ return to;
+}
function __await(v) {
return this instanceof __await ? (this.v = v, this) : new __await(v);
@@ -872,19 +883,17 @@ function __importDefault(mod) {
return (mod && mod.__esModule) ? mod : { default: mod };
}
-function __classPrivateFieldGet(receiver, privateMap) {
- if (!privateMap.has(receiver)) {
- throw new TypeError("attempted to get private field on non-instance");
- }
- return privateMap.get(receiver);
+function __classPrivateFieldGet(receiver, state, kind, f) {
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
}
-function __classPrivateFieldSet(receiver, privateMap, value) {
- if (!privateMap.has(receiver)) {
- throw new TypeError("attempted to set private field on non-instance");
- }
- privateMap.set(receiver, value);
- return value;
+function __classPrivateFieldSet(receiver, state, value, kind, f) {
+ if (kind === "m") throw new TypeError("Private method is not writable");
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
}
diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
index b1561b29342da..148c21ffac191 100644
--- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
+++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts
@@ -9,7 +9,6 @@
import { delay } from 'bluebird';
import { WebElement, WebDriver, By, Key } from 'selenium-webdriver';
import { PNG } from 'pngjs';
-// @ts-ignore not supported yet
import cheerio from 'cheerio';
import testSubjSelector from '@kbn/test-subj-selector';
import { ToolingLog } from '@kbn/dev-utils';
diff --git a/yarn.lock b/yarn.lock
index 17f61f0b408d2..3afbffbac6eb1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4732,7 +4732,7 @@
resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.0.1.tgz#c10703020369602c40dd9428cc6e1437027116df"
integrity sha512-jtV6Bv/j+xk4gcXeLlESwNc/m/I/dIZA0xrt29g0uKcjyPob8iisj/5z0ARE+Ldfx4MxjNFNECG0z++J7zJgqg==
-"@types/cheerio@*", "@types/cheerio@^0.22.22", "@types/cheerio@^0.22.28":
+"@types/cheerio@*", "@types/cheerio@^0.22.22":
version "0.22.28"
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.28.tgz#90808aabb44fec40fa2950f4c72351e3e4eb065b"
integrity sha512-ehUMGSW5IeDxJjbru4awKYMlKGmo1wSSGUVqXtYwlgmUM8X1a0PZttEIm6yEY7vHsY/hh6iPnklF213G0UColw==
@@ -9220,27 +9220,16 @@ check-more-types@2.24.0, check-more-types@^2.24.0:
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=
-cheerio@0.22.0:
- version "0.22.0"
- resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e"
- integrity sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=
+cheerio-select@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.4.0.tgz#3a16f21e37a2ef0f211d6d1aa4eff054bb22cdc9"
+ integrity sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew==
dependencies:
- css-select "~1.2.0"
- dom-serializer "~0.1.0"
- entities "~1.1.1"
- htmlparser2 "^3.9.1"
- lodash.assignin "^4.0.9"
- lodash.bind "^4.1.4"
- lodash.defaults "^4.0.1"
- lodash.filter "^4.4.0"
- lodash.flatten "^4.2.0"
- lodash.foreach "^4.3.0"
- lodash.map "^4.4.0"
- lodash.merge "^4.4.0"
- lodash.pick "^4.2.1"
- lodash.reduce "^4.4.0"
- lodash.reject "^4.4.0"
- lodash.some "^4.4.0"
+ css-select "^4.1.2"
+ css-what "^5.0.0"
+ domelementtype "^2.2.0"
+ domhandler "^4.2.0"
+ domutils "^2.6.0"
cheerio@^1.0.0-rc.3:
version "1.0.0-rc.3"
@@ -9254,6 +9243,19 @@ cheerio@^1.0.0-rc.3:
lodash "^4.15.0"
parse5 "^3.0.1"
+cheerio@^1.0.0-rc.9:
+ version "1.0.0-rc.9"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.9.tgz#a3ae6b7ce7af80675302ff836f628e7cb786a67f"
+ integrity sha512-QF6XVdrLONO6DXRF5iaolY+odmhj2CLj+xzNod7INPWMi/x9X4SOylH0S/vaPpX+AUU6t04s34SQNh7DbkuCng==
+ dependencies:
+ cheerio-select "^1.4.0"
+ dom-serializer "^1.3.1"
+ domhandler "^4.2.0"
+ htmlparser2 "^6.1.0"
+ parse5 "^6.0.1"
+ parse5-htmlparser2-tree-adapter "^6.0.1"
+ tslib "^2.2.0"
+
chokidar@3.4.3, chokidar@^2.0.0, chokidar@^2.0.4, chokidar@^2.1.1, chokidar@^2.1.2, chokidar@^2.1.8, chokidar@^3.2.2, chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
@@ -10491,6 +10493,17 @@ css-select@^2.0.0:
domutils "^1.7.0"
nth-check "^1.0.2"
+css-select@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.2.tgz#8b52b6714ed3a80d8221ec971c543f3b12653286"
+ integrity sha512-nu5ye2Hg/4ISq4XqdLY2bEatAcLIdt3OYGFc9Tm9n7VSlFBcfRv0gBNksHRgSdUDQGtN3XrZ94ztW+NfzkFSUw==
+ dependencies:
+ boolbase "^1.0.0"
+ css-what "^5.0.0"
+ domhandler "^4.2.0"
+ domutils "^2.6.0"
+ nth-check "^2.0.0"
+
css-to-react-native@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756"
@@ -10526,6 +10539,11 @@ css-what@^3.2.1:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
+css-what@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
+ integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==
+
css.escape@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
@@ -11837,7 +11855,7 @@ dom-helpers@^5.0.0, dom-helpers@^5.0.1:
"@babel/runtime" "^7.8.7"
csstype "^2.6.7"
-dom-serializer@0, dom-serializer@~0.1.0, dom-serializer@~0.1.1:
+dom-serializer@0, dom-serializer@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
@@ -11845,6 +11863,15 @@ dom-serializer@0, dom-serializer@~0.1.0, dom-serializer@~0.1.1:
domelementtype "^1.3.0"
entities "^1.1.1"
+dom-serializer@^1.0.1, dom-serializer@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91"
+ integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==
+ dependencies:
+ domelementtype "^2.0.1"
+ domhandler "^4.2.0"
+ entities "^2.0.0"
+
dom-walk@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
@@ -11860,6 +11887,11 @@ domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
+ integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
+
domexception@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
@@ -11888,6 +11920,13 @@ domhandler@^2.3.0, domhandler@^2.4.2:
dependencies:
domelementtype "1"
+domhandler@^4.0.0, domhandler@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
+ integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
+ dependencies:
+ domelementtype "^2.2.0"
+
domutils@1.1:
version "1.1.6"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485"
@@ -11911,6 +11950,15 @@ domutils@^1.5.1, domutils@^1.7.0:
dom-serializer "0"
domelementtype "1"
+domutils@^2.5.2, domutils@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7"
+ integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==
+ dependencies:
+ dom-serializer "^1.0.1"
+ domelementtype "^2.2.0"
+ domhandler "^4.2.0"
+
dot-case@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa"
@@ -12309,6 +12357,11 @@ entities@^1.1.1, entities@^1.1.2, entities@~1.1.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+entities@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+ integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
entities@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
@@ -15500,6 +15553,16 @@ htmlparser2@^3.10.0, htmlparser2@^3.9.1:
inherits "^2.0.1"
readable-stream "^3.1.1"
+htmlparser2@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
+ integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
+ dependencies:
+ domelementtype "^2.0.1"
+ domhandler "^4.0.0"
+ domutils "^2.5.2"
+ entities "^2.0.0"
+
htmlparser2@~3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe"
@@ -18342,16 +18405,6 @@ lodash.assign@^4.2.0:
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
-lodash.assignin@^4.0.9:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2"
- integrity sha1-uo31+4QesKPoBEIysOJjqNxqKKI=
-
-lodash.bind@^4.1.4:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35"
- integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=
-
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
@@ -18372,7 +18425,7 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
-lodash.defaults@^4.0.1, lodash.defaults@^4.2.0:
+lodash.defaults@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
@@ -18392,17 +18445,12 @@ lodash.fill@^3.4.0:
resolved "https://registry.yarnpkg.com/lodash.fill/-/lodash.fill-3.4.0.tgz#a3c74ae640d053adf0dc2079f8720788e8bfef85"
integrity sha1-o8dK5kDQU63w3CB5+HIHiOi/74U=
-lodash.filter@^4.4.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace"
- integrity sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=
-
lodash.flatmap@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e"
integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=
-lodash.flatten@^4.2.0, lodash.flatten@^4.4.0:
+lodash.flatten@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
@@ -18412,11 +18460,6 @@ lodash.flattendeep@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
-lodash.foreach@^4.3.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
- integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=
-
lodash.get@^4.0.0, lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@@ -18477,11 +18520,6 @@ lodash.isstring@^4.0.1:
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
-lodash.map@^4.4.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3"
- integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=
-
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -18492,7 +18530,7 @@ lodash.memoize@~3.0.3:
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=
-lodash.merge@^4.4.0, lodash.merge@^4.6.1:
+lodash.merge@^4.6.1:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
@@ -18512,27 +18550,17 @@ lodash.partialright@^4.2.1:
resolved "https://registry.yarnpkg.com/lodash.partialright/-/lodash.partialright-4.2.1.tgz#0130d80e83363264d40074f329b8a3e7a8a1cc4b"
integrity sha1-ATDYDoM2MmTUAHTzKbij56ihzEs=
-lodash.pick@^4.2.1, lodash.pick@^4.4.0:
+lodash.pick@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=
-lodash.reduce@^4.4.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b"
- integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=
-
-lodash.reject@^4.4.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415"
- integrity sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=
-
lodash.set@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=
-lodash.some@^4.4.0, lodash.some@^4.6.0:
+lodash.some@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=
@@ -20327,6 +20355,13 @@ nth-check@^1.0.2, nth-check@~1.0.1:
dependencies:
boolbase "~1.0.0"
+nth-check@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
+ integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
+ dependencies:
+ boolbase "^1.0.0"
+
null-loader@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/null-loader/-/null-loader-3.0.0.tgz#3e2b6c663c5bda8c73a54357d8fa0708dc61b245"
@@ -21121,6 +21156,13 @@ parse-passwd@^1.0.0:
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
+parse5-htmlparser2-tree-adapter@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
+ integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
+ dependencies:
+ parse5 "^6.0.1"
+
parse5@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2"
@@ -21138,7 +21180,7 @@ parse5@^3.0.1:
dependencies:
"@types/node" "*"
-parse5@^6.0.0:
+parse5@^6.0.0, parse5@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
@@ -27135,15 +27177,10 @@ tslib@^1, tslib@^1.0.0, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
-tslib@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
- integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==
-
-tslib@^2.0.1:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
- integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
+tslib@^2.0.0, tslib@^2.0.1, tslib@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
+ integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
tslib@~2.1.0:
version "2.1.0"
From 356b7d6c9202269d00b70886f95bfe0eb97555c5 Mon Sep 17 00:00:00 2001
From: Stacey Gammon
Date: Tue, 8 Jun 2021 11:43:16 -0400
Subject: [PATCH 04/13] Add ssl instructions to example alerting plugin
(#101529)
* Update documentation.tsx
* Update documentation.tsx
* Update documentation.tsx
* fix type issue
* Noticed a lack of a space in the screenshot
---
.../alerting_example/public/components/documentation.tsx | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/x-pack/examples/alerting_example/public/components/documentation.tsx b/x-pack/examples/alerting_example/public/components/documentation.tsx
index 2cef174675533..7dd5af38288cc 100644
--- a/x-pack/examples/alerting_example/public/components/documentation.tsx
+++ b/x-pack/examples/alerting_example/public/components/documentation.tsx
@@ -17,6 +17,7 @@ import {
EuiPageHeader,
EuiPageHeaderSection,
EuiTitle,
+ EuiCallOut,
EuiSpacer,
} from '@elastic/eui';
import { CreateAlert } from './create_alert';
@@ -49,6 +50,13 @@ export const DocumentationPage = (
registration of example the RuleTypes, while the `public` handles creation of, and
navigation for, these rule types.
+
+ If you see a message about needing to enable the Transport Layer Security, start ES with{' '}
+ yarn es snapshot --ssl --license trial
and Kibana with{' '}
+ yarn start --run-examples --ssl
. If you running chrome on a mac, you may
+ need to type in thisisunsafe
if you see the Certificate invalid screen with
+ no way to ‘proceed anyway’.
+
From ec212c061857cf19346a4c5082d2a4971f3fce04 Mon Sep 17 00:00:00 2001
From: Tyler Smalley
Date: Tue, 8 Jun 2021 08:48:43 -0700
Subject: [PATCH 05/13] [es-archiver] Use alias for Kibana mappings (#101537)
In 7.x, when saved objects point to an index and not an alias it will
assume it's pre-6.8 and perform a legacy migration. This causes issues
with the removal of oss builds. This update prevents that from happening
by using an alias.
Signed-off-by: Tyler Smalley
---
.../management/saved_objects/scroll_count/mappings.json | 5 ++++-
.../management/saved_objects/search/mappings.json | 5 ++++-
.../es_archiver/saved_objects/ui_counters/mappings.json | 5 ++++-
.../es_archiver/saved_objects/usage_counters/mappings.json | 5 ++++-
.../fixtures/es_archiver/search/count/mappings.json | 5 ++++-
.../fixtures/es_archiver/dashboard/legacy/mappings.json | 5 ++++-
.../fixtures/es_archiver/deprecations_service/mappings.json | 5 ++++-
test/functional/fixtures/es_archiver/discover/mappings.json | 5 ++++-
.../fixtures/es_archiver/empty_kibana/mappings.json | 5 ++++-
.../es_archiver/invalid_scripted_field/mappings.json | 5 ++++-
.../fixtures/es_archiver/management/mappings.json | 5 ++++-
test/functional/fixtures/es_archiver/mgmt/mappings.json | 3 ++-
.../es_archiver/saved_objects_imports/mappings.json | 5 ++++-
.../edit_saved_object/mappings.json | 5 ++++-
.../saved_objects_management/export_transform/mappings.json | 5 ++++-
.../hidden_saved_objects/mappings.json | 5 ++++-
.../saved_objects_management/hidden_types/mappings.json | 5 ++++-
.../nested_export_transform/mappings.json | 5 ++++-
.../show_relationships/mappings.json | 5 ++++-
test/functional/fixtures/es_archiver/timelion/mappings.json | 5 ++++-
.../functional/fixtures/es_archiver/visualize/mappings.json | 5 ++++-
.../fixtures/es_archiver/visualize_embedding/mappings.json | 5 ++++-
.../es_archiver/visualize_source-filters/mappings.json | 5 ++++-
.../es_archiver/visualize_source_filters/mappings.json | 5 ++++-
.../functional/es_archives/banners/multispace/mappings.json | 5 ++++-
.../es_archives/dashboard/async_search/mappings.json | 5 ++++-
.../es_archives/dashboard/drilldowns/mappings.json | 5 ++++-
.../dashboard/feature_controls/security/mappings.json | 5 ++++-
.../dashboard/feature_controls/spaces/mappings.json | 5 ++++-
.../es_archives/dashboard/session_in_space/mappings.json | 5 ++++-
.../es_archives/dashboard_view_mode/mappings.json | 5 ++++-
.../es_archives/data/search_sessions/mappings.json | 5 ++++-
.../functional/es_archives/discover/default/mappings.json | 5 ++++-
.../discover/feature_controls/security/mappings.json | 5 ++++-
.../discover/feature_controls/spaces/mappings.json | 5 ++++-
.../test/functional/es_archives/empty_kibana/mappings.json | 5 ++++-
.../es_archives/invalid_scripted_field/mappings.json | 5 ++++-
.../functional/es_archives/logstash/empty/mappings.json | 5 ++++-
.../es_archives/logstash/example_pipelines/mappings.json | 5 ++++-
.../test/functional/es_archives/maps/kibana/mappings.json | 5 ++++-
.../functional/es_archives/reporting/logs/mappings.json | 5 ++++-
.../functional/es_archives/reporting/sales/mappings.json | 5 ++++-
.../feature_controls/security/mappings.json | 5 ++++-
.../functional/es_archives/security/discover/mappings.json | 5 ++++-
.../es_archives/security/flstest/kibana/mappings.json | 5 ++++-
.../es_archives/spaces/copy_saved_objects/mappings.json | 5 ++++-
.../es_archives/spaces/disabled_features/mappings.json | 5 ++++-
.../functional/es_archives/spaces/enter_space/mappings.json | 5 ++++-
.../functional/es_archives/spaces/multi_space/mappings.json | 5 ++++-
.../functional/es_archives/spaces/selector/mappings.json | 5 ++++-
.../es_archives/timelion/feature_controls/mappings.json | 5 ++++-
.../es_archives/global_search/basic/mappings.json | 5 ++++-
.../es_archives/global_search/search_syntax/mappings.json | 6 ++++--
.../common/fixtures/es_archiver/bulk_assign/mappings.json | 6 ++++--
.../common/fixtures/es_archiver/dashboard/mappings.json | 6 ++++--
.../es_archiver/delete_with_references/mappings.json | 6 ++++--
.../fixtures/es_archiver/functional_base/mappings.json | 6 ++++--
.../common/fixtures/es_archiver/rbac_tags/mappings.json | 6 ++++--
.../common/fixtures/es_archiver/so_management/mappings.json | 6 ++++--
.../fixtures/es_archiver/usage_collection/mappings.json | 6 ++++--
.../common/fixtures/es_archiver/visualize/mappings.json | 6 ++++--
.../es_archives/empty_kibana/mappings.json | 5 ++++-
62 files changed, 246 insertions(+), 71 deletions(-)
diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json b/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json
index 8270c573e4c1e..d2177481130a2 100644
--- a/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json
+++ b/test/api_integration/fixtures/es_archiver/management/saved_objects/scroll_count/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json
index c670508247b1a..7a6d752eafb08 100644
--- a/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json
+++ b/test/api_integration/fixtures/es_archiver/management/saved_objects/search/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/api_integration/fixtures/es_archiver/saved_objects/ui_counters/mappings.json b/test/api_integration/fixtures/es_archiver/saved_objects/ui_counters/mappings.json
index 39902f8a9211a..99f2f999db988 100644
--- a/test/api_integration/fixtures/es_archiver/saved_objects/ui_counters/mappings.json
+++ b/test/api_integration/fixtures/es_archiver/saved_objects/ui_counters/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/api_integration/fixtures/es_archiver/saved_objects/usage_counters/mappings.json b/test/api_integration/fixtures/es_archiver/saved_objects/usage_counters/mappings.json
index 14ed147b2da8e..c2ec5c8881087 100644
--- a/test/api_integration/fixtures/es_archiver/saved_objects/usage_counters/mappings.json
+++ b/test/api_integration/fixtures/es_archiver/saved_objects/usage_counters/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/api_integration/fixtures/es_archiver/search/count/mappings.json b/test/api_integration/fixtures/es_archiver/search/count/mappings.json
index b62c5da05c2e6..41d5c07e93239 100644
--- a/test/api_integration/fixtures/es_archiver/search/count/mappings.json
+++ b/test/api_integration/fixtures/es_archiver/search/count/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/functional/fixtures/es_archiver/dashboard/legacy/mappings.json b/test/functional/fixtures/es_archiver/dashboard/legacy/mappings.json
index a89fe1dfacfc8..45b2508d38033 100644
--- a/test/functional/fixtures/es_archiver/dashboard/legacy/mappings.json
+++ b/test/functional/fixtures/es_archiver/dashboard/legacy/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/deprecations_service/mappings.json b/test/functional/fixtures/es_archiver/deprecations_service/mappings.json
index 5f7c7e0e7b7dc..41cddecef0c41 100644
--- a/test/functional/fixtures/es_archiver/deprecations_service/mappings.json
+++ b/test/functional/fixtures/es_archiver/deprecations_service/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/discover/mappings.json b/test/functional/fixtures/es_archiver/discover/mappings.json
index 53bbe8a5baa5b..519af2dd75b9e 100644
--- a/test/functional/fixtures/es_archiver/discover/mappings.json
+++ b/test/functional/fixtures/es_archiver/discover/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/empty_kibana/mappings.json b/test/functional/fixtures/es_archiver/empty_kibana/mappings.json
index 403a891ba1175..264096beb11ee 100644
--- a/test/functional/fixtures/es_archiver/empty_kibana/mappings.json
+++ b/test/functional/fixtures/es_archiver/empty_kibana/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/invalid_scripted_field/mappings.json b/test/functional/fixtures/es_archiver/invalid_scripted_field/mappings.json
index 0024c6943ed1c..63cc283f96d32 100644
--- a/test/functional/fixtures/es_archiver/invalid_scripted_field/mappings.json
+++ b/test/functional/fixtures/es_archiver/invalid_scripted_field/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/test/functional/fixtures/es_archiver/management/mappings.json b/test/functional/fixtures/es_archiver/management/mappings.json
index a89fe1dfacfc8..45b2508d38033 100644
--- a/test/functional/fixtures/es_archiver/management/mappings.json
+++ b/test/functional/fixtures/es_archiver/management/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/mgmt/mappings.json b/test/functional/fixtures/es_archiver/mgmt/mappings.json
index 28198102d3d68..aefbd9d0ccc8a 100644
--- a/test/functional/fixtures/es_archiver/mgmt/mappings.json
+++ b/test/functional/fixtures/es_archiver/mgmt/mappings.json
@@ -2,8 +2,9 @@
"type": "index",
"value": {
"aliases": {
+ ".kibana": {}
},
- "index": ".kibana",
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/test/functional/fixtures/es_archiver/saved_objects_imports/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_imports/mappings.json
index a89fe1dfacfc8..45b2508d38033 100644
--- a/test/functional/fixtures/es_archiver/saved_objects_imports/mappings.json
+++ b/test/functional/fixtures/es_archiver/saved_objects_imports/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json
index 6a416126d7f26..05ca4d8e8307e 100644
--- a/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json
+++ b/test/functional/fixtures/es_archiver/saved_objects_management/edit_saved_object/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json
index 43b851e817fa8..653e639954813 100644
--- a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json
+++ b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json
index 1de768d290d35..a158deb527cc8 100644
--- a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json
+++ b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json
index a9abae009cb59..61763f55c1b6a 100644
--- a/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json
+++ b/test/functional/fixtures/es_archiver/saved_objects_management/hidden_types/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json
index 43b851e817fa8..653e639954813 100644
--- a/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json
+++ b/test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json
index a5478a5805d50..aba581867bb8a 100644
--- a/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json
+++ b/test/functional/fixtures/es_archiver/saved_objects_management/show_relationships/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/test/functional/fixtures/es_archiver/timelion/mappings.json b/test/functional/fixtures/es_archiver/timelion/mappings.json
index a89fe1dfacfc8..45b2508d38033 100644
--- a/test/functional/fixtures/es_archiver/timelion/mappings.json
+++ b/test/functional/fixtures/es_archiver/timelion/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/visualize/mappings.json b/test/functional/fixtures/es_archiver/visualize/mappings.json
index 464f6751eac5c..59ec24853e227 100644
--- a/test/functional/fixtures/es_archiver/visualize/mappings.json
+++ b/test/functional/fixtures/es_archiver/visualize/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/visualize_embedding/mappings.json b/test/functional/fixtures/es_archiver/visualize_embedding/mappings.json
index a89fe1dfacfc8..45b2508d38033 100644
--- a/test/functional/fixtures/es_archiver/visualize_embedding/mappings.json
+++ b/test/functional/fixtures/es_archiver/visualize_embedding/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/visualize_source-filters/mappings.json b/test/functional/fixtures/es_archiver/visualize_source-filters/mappings.json
index a89fe1dfacfc8..45b2508d38033 100644
--- a/test/functional/fixtures/es_archiver/visualize_source-filters/mappings.json
+++ b/test/functional/fixtures/es_archiver/visualize_source-filters/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/test/functional/fixtures/es_archiver/visualize_source_filters/mappings.json b/test/functional/fixtures/es_archiver/visualize_source_filters/mappings.json
index 0f17621dbf529..5ac113e7e4b74 100644
--- a/test/functional/fixtures/es_archiver/visualize_source_filters/mappings.json
+++ b/test/functional/fixtures/es_archiver/visualize_source_filters/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/banners/multispace/mappings.json b/x-pack/test/functional/es_archives/banners/multispace/mappings.json
index f3793c7ca6780..9f3201d73abc1 100644
--- a/x-pack/test/functional/es_archives/banners/multispace/mappings.json
+++ b/x-pack/test/functional/es_archives/banners/multispace/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json b/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json
index ee860fe973f60..5001bb053e01b 100644
--- a/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json
+++ b/x-pack/test/functional/es_archives/dashboard/async_search/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/dashboard/drilldowns/mappings.json b/x-pack/test/functional/es_archives/dashboard/drilldowns/mappings.json
index 210fade40c648..a842c20b6965e 100644
--- a/x-pack/test/functional/es_archives/dashboard/drilldowns/mappings.json
+++ b/x-pack/test/functional/es_archives/dashboard/drilldowns/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json
index c66a45084441f..29c56a751c038 100644
--- a/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json
+++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/security/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json b/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json
index dafef0fa9c2c7..0cd1a29f92241 100644
--- a/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json
+++ b/x-pack/test/functional/es_archives/dashboard/feature_controls/spaces/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/x-pack/test/functional/es_archives/dashboard/session_in_space/mappings.json b/x-pack/test/functional/es_archives/dashboard/session_in_space/mappings.json
index 210fade40c648..a842c20b6965e 100644
--- a/x-pack/test/functional/es_archives/dashboard/session_in_space/mappings.json
+++ b/x-pack/test/functional/es_archives/dashboard/session_in_space/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/dashboard_view_mode/mappings.json b/x-pack/test/functional/es_archives/dashboard_view_mode/mappings.json
index 3558e89558a56..0f58add932b0c 100644
--- a/x-pack/test/functional/es_archives/dashboard_view_mode/mappings.json
+++ b/x-pack/test/functional/es_archives/dashboard_view_mode/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/data/search_sessions/mappings.json b/x-pack/test/functional/es_archives/data/search_sessions/mappings.json
index 61305d640fe3e..9c27bc1458c7f 100644
--- a/x-pack/test/functional/es_archives/data/search_sessions/mappings.json
+++ b/x-pack/test/functional/es_archives/data/search_sessions/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/functional/es_archives/discover/default/mappings.json b/x-pack/test/functional/es_archives/discover/default/mappings.json
index 53bbe8a5baa5b..519af2dd75b9e 100644
--- a/x-pack/test/functional/es_archives/discover/default/mappings.json
+++ b/x-pack/test/functional/es_archives/discover/default/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json
index 5e01af673ef2f..69fceb7c9d209 100644
--- a/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json
+++ b/x-pack/test/functional/es_archives/discover/feature_controls/security/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/x-pack/test/functional/es_archives/discover/feature_controls/spaces/mappings.json b/x-pack/test/functional/es_archives/discover/feature_controls/spaces/mappings.json
index dafef0fa9c2c7..0cd1a29f92241 100644
--- a/x-pack/test/functional/es_archives/discover/feature_controls/spaces/mappings.json
+++ b/x-pack/test/functional/es_archives/discover/feature_controls/spaces/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/x-pack/test/functional/es_archives/empty_kibana/mappings.json b/x-pack/test/functional/es_archives/empty_kibana/mappings.json
index 77eac534850a5..499c994780285 100644
--- a/x-pack/test/functional/es_archives/empty_kibana/mappings.json
+++ b/x-pack/test/functional/es_archives/empty_kibana/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json b/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json
index 0024c6943ed1c..63cc283f96d32 100644
--- a/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json
+++ b/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/functional/es_archives/logstash/empty/mappings.json b/x-pack/test/functional/es_archives/logstash/empty/mappings.json
index 096c68aefcc3c..98fb761389a24 100644
--- a/x-pack/test/functional/es_archives/logstash/empty/mappings.json
+++ b/x-pack/test/functional/es_archives/logstash/empty/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/logstash/example_pipelines/mappings.json b/x-pack/test/functional/es_archives/logstash/example_pipelines/mappings.json
index 9e56ff8a7a43a..56a2e144ff89c 100644
--- a/x-pack/test/functional/es_archives/logstash/example_pipelines/mappings.json
+++ b/x-pack/test/functional/es_archives/logstash/example_pipelines/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/maps/kibana/mappings.json b/x-pack/test/functional/es_archives/maps/kibana/mappings.json
index 4786fb1f30f43..d9a4ed16b51bd 100644
--- a/x-pack/test/functional/es_archives/maps/kibana/mappings.json
+++ b/x-pack/test/functional/es_archives/maps/kibana/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/functional/es_archives/reporting/logs/mappings.json b/x-pack/test/functional/es_archives/reporting/logs/mappings.json
index adf4050bb88c4..ffb8c85e8fd8b 100644
--- a/x-pack/test/functional/es_archives/reporting/logs/mappings.json
+++ b/x-pack/test/functional/es_archives/reporting/logs/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/reporting/sales/mappings.json b/x-pack/test/functional/es_archives/reporting/sales/mappings.json
index 3249708537b73..dbb8d396eb496 100644
--- a/x-pack/test/functional/es_archives/reporting/sales/mappings.json
+++ b/x-pack/test/functional/es_archives/reporting/sales/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/saved_objects_management/feature_controls/security/mappings.json b/x-pack/test/functional/es_archives/saved_objects_management/feature_controls/security/mappings.json
index 6a416126d7f26..05ca4d8e8307e 100644
--- a/x-pack/test/functional/es_archives/saved_objects_management/feature_controls/security/mappings.json
+++ b/x-pack/test/functional/es_archives/saved_objects_management/feature_controls/security/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/x-pack/test/functional/es_archives/security/discover/mappings.json b/x-pack/test/functional/es_archives/security/discover/mappings.json
index 3558e89558a56..0f58add932b0c 100644
--- a/x-pack/test/functional/es_archives/security/discover/mappings.json
+++ b/x-pack/test/functional/es_archives/security/discover/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/security/flstest/kibana/mappings.json b/x-pack/test/functional/es_archives/security/flstest/kibana/mappings.json
index fb2ed91f3ce14..ca97d0505f5b1 100644
--- a/x-pack/test/functional/es_archives/security/flstest/kibana/mappings.json
+++ b/x-pack/test/functional/es_archives/security/flstest/kibana/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/functional/es_archives/spaces/copy_saved_objects/mappings.json b/x-pack/test/functional/es_archives/spaces/copy_saved_objects/mappings.json
index 0d54500e83eb7..d860c19ceb64c 100644
--- a/x-pack/test/functional/es_archives/spaces/copy_saved_objects/mappings.json
+++ b/x-pack/test/functional/es_archives/spaces/copy_saved_objects/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/functional/es_archives/spaces/disabled_features/mappings.json b/x-pack/test/functional/es_archives/spaces/disabled_features/mappings.json
index d0ad5570373c4..23a63e372a855 100644
--- a/x-pack/test/functional/es_archives/spaces/disabled_features/mappings.json
+++ b/x-pack/test/functional/es_archives/spaces/disabled_features/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json b/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json
index f3793c7ca6780..9f3201d73abc1 100644
--- a/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json
+++ b/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json b/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json
index 77eac534850a5..499c994780285 100644
--- a/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json
+++ b/x-pack/test/functional/es_archives/spaces/multi_space/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/spaces/selector/mappings.json b/x-pack/test/functional/es_archives/spaces/selector/mappings.json
index 77eac534850a5..499c994780285 100644
--- a/x-pack/test/functional/es_archives/spaces/selector/mappings.json
+++ b/x-pack/test/functional/es_archives/spaces/selector/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
diff --git a/x-pack/test/functional/es_archives/timelion/feature_controls/mappings.json b/x-pack/test/functional/es_archives/timelion/feature_controls/mappings.json
index d0ad5570373c4..23a63e372a855 100644
--- a/x-pack/test/functional/es_archives/timelion/feature_controls/mappings.json
+++ b/x-pack/test/functional/es_archives/timelion/feature_controls/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json b/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json
index c66a45084441f..29c56a751c038 100644
--- a/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json
+++ b/x-pack/test/plugin_functional/es_archives/global_search/basic/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"settings": {
"index": {
"number_of_shards": "1",
diff --git a/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/mappings.json b/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/mappings.json
index ec28b51de1d10..35537a9755510 100644
--- a/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/mappings.json
+++ b/x-pack/test/plugin_functional/es_archives/global_search/search_syntax/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/bulk_assign/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/bulk_assign/mappings.json
index 9cf628bef4767..2e465869f9ae5 100644
--- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/bulk_assign/mappings.json
+++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/bulk_assign/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/dashboard/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/dashboard/mappings.json
index 4156cc9e2373d..47f23dc57e361 100644
--- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/dashboard/mappings.json
+++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/dashboard/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"_meta": {
"migrationMappingPropertyHashes": {
diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/delete_with_references/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/delete_with_references/mappings.json
index 4ea82eb30e06a..5ccad08bc21fa 100644
--- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/delete_with_references/mappings.json
+++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/delete_with_references/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/functional_base/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/functional_base/mappings.json
index 4ea82eb30e06a..5ccad08bc21fa 100644
--- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/functional_base/mappings.json
+++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/functional_base/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/rbac_tags/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/rbac_tags/mappings.json
index cd0d076258721..cd57c26dfc337 100644
--- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/rbac_tags/mappings.json
+++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/rbac_tags/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/so_management/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/so_management/mappings.json
index 4ea82eb30e06a..5ccad08bc21fa 100644
--- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/so_management/mappings.json
+++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/so_management/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/mappings.json
index 9cf628bef4767..2e465869f9ae5 100644
--- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/mappings.json
+++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/usage_collection/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/visualize/mappings.json b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/visualize/mappings.json
index 4ea82eb30e06a..5ccad08bc21fa 100644
--- a/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/visualize/mappings.json
+++ b/x-pack/test/saved_object_tagging/common/fixtures/es_archiver/visualize/mappings.json
@@ -1,8 +1,10 @@
{
"type": "index",
"value": {
- "aliases": {},
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"dynamic": "strict",
"properties": {
diff --git a/x-pack/test/security_solution_cypress/es_archives/empty_kibana/mappings.json b/x-pack/test/security_solution_cypress/es_archives/empty_kibana/mappings.json
index 77eac534850a5..499c994780285 100644
--- a/x-pack/test/security_solution_cypress/es_archives/empty_kibana/mappings.json
+++ b/x-pack/test/security_solution_cypress/es_archives/empty_kibana/mappings.json
@@ -1,7 +1,10 @@
{
"type": "index",
"value": {
- "index": ".kibana",
+ "aliases": {
+ ".kibana": {}
+ },
+ "index": ".kibana_1",
"mappings": {
"properties": {
"config": {
From 13e20e78875f44cdb6750e32068e4ff4ae411201 Mon Sep 17 00:00:00 2001
From: Christos Nasikas
Date: Tue, 8 Jun 2021 19:01:33 +0300
Subject: [PATCH 06/13] [Cases] Performance and RBAC improvements (#101465)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../cases/common/api/cases/configure.ts | 20 +--
.../plugins/cases/common/api/runtime_types.ts | 4 -
x-pack/plugins/cases/common/constants.ts | 3 +
.../cases/server/client/attachments/delete.ts | 30 +++--
.../cases/server/client/cases/delete.ts | 102 ++++++++-------
.../plugins/cases/server/client/cases/find.ts | 1 +
.../cases/server/client/cases/update.ts | 10 +-
.../cases/server/client/configure/client.ts | 30 +++--
.../cases/server/client/stats/client.ts | 1 +
.../cases/server/client/sub_cases/client.ts | 25 ++--
.../server/common/models/commentable_case.ts | 20 ++-
.../server/services/attachments/index.ts | 4 +-
.../cases/server/services/cases/index.ts | 116 +++++-------------
.../cases/server/services/configure/index.ts | 3 +-
.../server/services/user_actions/index.ts | 10 +-
15 files changed, 182 insertions(+), 197 deletions(-)
diff --git a/x-pack/plugins/cases/common/api/cases/configure.ts b/x-pack/plugins/cases/common/api/cases/configure.ts
index 2814dd44f513f..6c92702c523b4 100644
--- a/x-pack/plugins/cases/common/api/cases/configure.ts
+++ b/x-pack/plugins/cases/common/api/cases/configure.ts
@@ -9,13 +9,11 @@ import * as rt from 'io-ts';
import { UserRT } from '../user';
import { CaseConnectorRt, ConnectorMappingsRt, ESCaseConnector } from '../connectors';
-import { OmitProp } from '../runtime_types';
-import { OWNER_FIELD } from './constants';
// TODO: we will need to add this type rt.literal('close-by-third-party')
const ClosureTypeRT = rt.union([rt.literal('close-by-user'), rt.literal('close-by-pushing')]);
-const CasesConfigureBasicRt = rt.type({
+const CasesConfigureBasicWithoutOwnerRt = rt.type({
/**
* The external connector
*/
@@ -24,15 +22,17 @@ const CasesConfigureBasicRt = rt.type({
* Whether to close the case after it has been synced with the external system
*/
closure_type: ClosureTypeRT,
- /**
- * The plugin owner that manages this configuration
- */
- owner: rt.string,
});
-const CasesConfigureBasicWithoutOwnerRt = rt.type(
- OmitProp(CasesConfigureBasicRt.props, OWNER_FIELD)
-);
+const CasesConfigureBasicRt = rt.intersection([
+ CasesConfigureBasicWithoutOwnerRt,
+ rt.type({
+ /**
+ * The plugin owner that manages this configuration
+ */
+ owner: rt.string,
+ }),
+]);
export const CasesConfigureRequestRt = CasesConfigureBasicRt;
export const CasesConfigurePatchRt = rt.intersection([
diff --git a/x-pack/plugins/cases/common/api/runtime_types.ts b/x-pack/plugins/cases/common/api/runtime_types.ts
index 361786985c6de..8817764e261d9 100644
--- a/x-pack/plugins/cases/common/api/runtime_types.ts
+++ b/x-pack/plugins/cases/common/api/runtime_types.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { omit } from 'lodash';
import { either, fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
import { pipe } from 'fp-ts/lib/pipeable';
@@ -14,9 +13,6 @@ import { isObject } from 'lodash/fp';
type ErrorFactory = (message: string) => Error;
-export const OmitProp = (o: O, k: K): Omit =>
- omit(o, k);
-
/**
* @deprecated Use packages/kbn-securitysolution-io-ts-utils/src/format_errors/index.ts
* Bug fix for the TODO is in the format_errors package
diff --git a/x-pack/plugins/cases/common/constants.ts b/x-pack/plugins/cases/common/constants.ts
index 72c21aa12dcf2..f0d3e8ccbcdea 100644
--- a/x-pack/plugins/cases/common/constants.ts
+++ b/x-pack/plugins/cases/common/constants.ts
@@ -92,3 +92,6 @@ export const ENABLE_CASE_CONNECTOR = false;
if (ENABLE_CASE_CONNECTOR) {
SAVED_OBJECT_TYPES.push(SUB_CASE_SAVED_OBJECT);
}
+
+export const MAX_DOCS_PER_PAGE = 10000;
+export const MAX_CONCURRENT_SEARCHES = 10;
diff --git a/x-pack/plugins/cases/server/client/attachments/delete.ts b/x-pack/plugins/cases/server/client/attachments/delete.ts
index d935a0c8f09db..89e12d7f7ea39 100644
--- a/x-pack/plugins/cases/server/client/attachments/delete.ts
+++ b/x-pack/plugins/cases/server/client/attachments/delete.ts
@@ -6,9 +6,15 @@
*/
import Boom from '@hapi/boom';
-import { CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../../common/constants';
-
-import { AssociationType } from '../../../common/api';
+import pMap from 'p-map';
+
+import { SavedObject } from 'kibana/public';
+import {
+ CASE_SAVED_OBJECT,
+ MAX_CONCURRENT_SEARCHES,
+ SUB_CASE_SAVED_OBJECT,
+} from '../../../common/constants';
+import { AssociationType, CommentAttributes } from '../../../common/api';
import { CasesClientArgs } from '../types';
import { buildCommentUserActionItem } from '../../services/user_actions/helpers';
import { createCaseError } from '../../common/error';
@@ -88,14 +94,16 @@ export async function deleteAll(
})),
});
- await Promise.all(
- comments.saved_objects.map((comment) =>
- attachmentService.delete({
- unsecuredSavedObjectsClient,
- attachmentId: comment.id,
- })
- )
- );
+ const mapper = async (comment: SavedObject) =>
+ attachmentService.delete({
+ unsecuredSavedObjectsClient,
+ attachmentId: comment.id,
+ });
+
+ // Ensuring we don't too many concurrent deletions running.
+ await pMap(comments.saved_objects, mapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
const deleteDate = new Date().toISOString();
diff --git a/x-pack/plugins/cases/server/client/cases/delete.ts b/x-pack/plugins/cases/server/client/cases/delete.ts
index b66abc6cc7be4..8e99e39ec473b 100644
--- a/x-pack/plugins/cases/server/client/cases/delete.ts
+++ b/x-pack/plugins/cases/server/client/cases/delete.ts
@@ -5,15 +5,16 @@
* 2.0.
*/
+import pMap from 'p-map';
import { Boom } from '@hapi/boom';
-import { SavedObjectsClientContract } from 'kibana/server';
-import { ENABLE_CASE_CONNECTOR } from '../../../common/constants';
+import { SavedObject, SavedObjectsClientContract, SavedObjectsFindResponse } from 'kibana/server';
+import { ENABLE_CASE_CONNECTOR, MAX_CONCURRENT_SEARCHES } from '../../../common/constants';
import { CasesClientArgs } from '..';
import { createCaseError } from '../../common/error';
import { AttachmentService, CasesService } from '../../services';
import { buildCaseUserActionItem } from '../../services/user_actions/helpers';
import { Operations, OwnerEntity } from '../../authorization';
-import { OWNER_FIELD } from '../../../common/api';
+import { OWNER_FIELD, SubCaseAttributes, CommentAttributes } from '../../../common/api';
async function deleteSubCases({
attachmentService,
@@ -37,19 +38,24 @@ async function deleteSubCases({
id: subCaseIDs,
});
- // This shouldn't actually delete anything because all the comments should be deleted when comments are deleted
- // per case ID
- await Promise.all(
- commentsForSubCases.saved_objects.map((commentSO) =>
- attachmentService.delete({ unsecuredSavedObjectsClient, attachmentId: commentSO.id })
- )
- );
-
- await Promise.all(
- subCasesForCaseIds.saved_objects.map((subCaseSO) =>
- caseService.deleteSubCase(unsecuredSavedObjectsClient, subCaseSO.id)
- )
- );
+ const commentMapper = (commentSO: SavedObject) =>
+ attachmentService.delete({ unsecuredSavedObjectsClient, attachmentId: commentSO.id });
+
+ const subCasesMapper = (subCaseSO: SavedObject) =>
+ caseService.deleteSubCase(unsecuredSavedObjectsClient, subCaseSO.id);
+
+ /**
+ * This shouldn't actually delete anything because
+ * all the comments should be deleted when comments are deleted
+ * per case ID. We also ensure that we don't too many concurrent deletions running.
+ */
+ await pMap(commentsForSubCases.saved_objects, commentMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
+
+ await pMap(subCasesForCaseIds.saved_objects, subCasesMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
}
/**
@@ -88,38 +94,46 @@ export async function deleteCases(ids: string[], clientArgs: CasesClientArgs): P
entities: Array.from(entities.values()),
});
- await Promise.all(
- ids.map((id) =>
- caseService.deleteCase({
- unsecuredSavedObjectsClient,
- id,
- })
- )
- );
+ const deleteCasesMapper = async (id: string) =>
+ caseService.deleteCase({
+ unsecuredSavedObjectsClient,
+ id,
+ });
- const comments = await Promise.all(
- ids.map((id) =>
- caseService.getAllCaseComments({
+ // Ensuring we don't too many concurrent deletions running.
+ await pMap(ids, deleteCasesMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
+
+ const getCommentsMapper = async (id: string) =>
+ caseService.getAllCaseComments({
+ unsecuredSavedObjectsClient,
+ id,
+ });
+
+ // Ensuring we don't too many concurrent get running.
+ const comments = await pMap(ids, getCommentsMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
+
+ /**
+ * This is a nested pMap.Mapper.
+ * Each element of the comments array contains all comments of a particular case.
+ * For that reason we need first to create a map that iterate over all cases
+ * and return a pMap that deletes the comments for that case
+ */
+ const deleteCommentsMapper = async (commentRes: SavedObjectsFindResponse) =>
+ pMap(commentRes.saved_objects, (comment) =>
+ attachmentService.delete({
unsecuredSavedObjectsClient,
- id,
+ attachmentId: comment.id,
})
- )
- );
-
- if (comments.some((c) => c.saved_objects.length > 0)) {
- await Promise.all(
- comments.map((c) =>
- Promise.all(
- c.saved_objects.map(({ id }) =>
- attachmentService.delete({
- unsecuredSavedObjectsClient,
- attachmentId: id,
- })
- )
- )
- )
);
- }
+
+ // Ensuring we don't too many concurrent deletions running.
+ await pMap(comments, deleteCommentsMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
if (ENABLE_CASE_CONNECTOR) {
await deleteSubCases({
diff --git a/x-pack/plugins/cases/server/client/cases/find.ts b/x-pack/plugins/cases/server/client/cases/find.ts
index 3b4efe78f642b..73eca5e7abb93 100644
--- a/x-pack/plugins/cases/server/client/cases/find.ts
+++ b/x-pack/plugins/cases/server/client/cases/find.ts
@@ -77,6 +77,7 @@ export const find = async (
ensureSavedObjectsAreAuthorized([...cases.casesMap.values()]);
+ // casesStatuses are bounded by us. No need to limit concurrent calls.
const [openCases, inProgressCases, closedCases] = await Promise.all([
...caseStatuses.map((status) => {
const statusQuery = constructQueryOptions({ ...queryArgs, status, authorizationFilter });
diff --git a/x-pack/plugins/cases/server/client/cases/update.ts b/x-pack/plugins/cases/server/client/cases/update.ts
index db20ba8318447..608c726f18531 100644
--- a/x-pack/plugins/cases/server/client/cases/update.ts
+++ b/x-pack/plugins/cases/server/client/cases/update.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import pMap from 'p-map';
import Boom from '@hapi/boom';
import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
@@ -42,6 +43,7 @@ import { CasesService } from '../../services';
import {
CASE_COMMENT_SAVED_OBJECT,
CASE_SAVED_OBJECT,
+ MAX_CONCURRENT_SEARCHES,
SUB_CASE_SAVED_OBJECT,
} from '../../../common/constants';
import {
@@ -162,9 +164,11 @@ async function throwIfInvalidUpdateOfTypeWithAlerts({
};
const requestsUpdatingTypeField = requests.filter((req) => req.type === CaseType.collection);
- const casesAlertTotals = await Promise.all(
- requestsUpdatingTypeField.map((caseToUpdate) => getAlertsForID(caseToUpdate))
- );
+ const getAlertsMapper = async (caseToUpdate: ESCasePatchRequest) => getAlertsForID(caseToUpdate);
+ // Ensuring we don't too many concurrent get running.
+ const casesAlertTotals = await pMap(requestsUpdatingTypeField, getAlertsMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
// grab the cases that have at least one alert comment attached to them
const typeUpdateWithAlerts = casesAlertTotals.filter((caseInfo) => caseInfo.alerts.total > 0);
diff --git a/x-pack/plugins/cases/server/client/configure/client.ts b/x-pack/plugins/cases/server/client/configure/client.ts
index 14348e03f99cc..d95667d5eee04 100644
--- a/x-pack/plugins/cases/server/client/configure/client.ts
+++ b/x-pack/plugins/cases/server/client/configure/client.ts
@@ -4,13 +4,19 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
+
+import pMap from 'p-map';
import Boom from '@hapi/boom';
import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';
-import { SavedObjectsFindResponse, SavedObjectsUtils } from '../../../../../../src/core/server';
-import { SUPPORTED_CONNECTORS } from '../../../common/constants';
+import {
+ SavedObject,
+ SavedObjectsFindResponse,
+ SavedObjectsUtils,
+} from '../../../../../../src/core/server';
+import { MAX_CONCURRENT_SEARCHES, SUPPORTED_CONNECTORS } from '../../../common/constants';
import {
CaseConfigureResponseRt,
CasesConfigurePatch,
@@ -26,6 +32,7 @@ import {
CaseConfigurationsResponseRt,
CasesConfigurePatchRt,
ConnectorMappings,
+ ESCasesConfigureAttributes,
} from '../../../common/api';
import { createCaseError } from '../../common/error';
import {
@@ -175,8 +182,9 @@ async function get(
}))
);
- const configurations = await Promise.all(
- myCaseConfigure.saved_objects.map(async (configuration) => {
+ const configurations = await pMap(
+ myCaseConfigure.saved_objects,
+ async (configuration: SavedObject) => {
const { connector, ...caseConfigureWithoutConnector } = configuration?.attributes ?? {
connector: null,
};
@@ -204,7 +212,7 @@ async function get(
error,
id: configuration.id,
};
- })
+ }
);
return CaseConfigurationsResponseRt.encode(configurations);
@@ -400,11 +408,13 @@ async function create(
);
if (myCaseConfigure.saved_objects.length > 0) {
- await Promise.all(
- myCaseConfigure.saved_objects.map((cc) =>
- caseConfigureService.delete({ unsecuredSavedObjectsClient, configurationId: cc.id })
- )
- );
+ const deleteConfigurationMapper = async (c: SavedObject) =>
+ caseConfigureService.delete({ unsecuredSavedObjectsClient, configurationId: c.id });
+
+ // Ensuring we don't too many concurrent deletions running.
+ await pMap(myCaseConfigure.saved_objects, deleteConfigurationMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
}
const savedObjectID = SavedObjectsUtils.generateId();
diff --git a/x-pack/plugins/cases/server/client/stats/client.ts b/x-pack/plugins/cases/server/client/stats/client.ts
index 0e222d54ab218..4fc8be4ccbfb2 100644
--- a/x-pack/plugins/cases/server/client/stats/client.ts
+++ b/x-pack/plugins/cases/server/client/stats/client.ts
@@ -63,6 +63,7 @@ async function getStatusTotalsByType(
ensureSavedObjectsAreAuthorized,
} = await authorization.getAuthorizationFilter(Operations.getCaseStatuses);
+ // casesStatuses are bounded by us. No need to limit concurrent calls.
const [openCases, inProgressCases, closedCases] = await Promise.all([
...caseStatuses.map((status) => {
const statusQuery = constructQueryOptions({
diff --git a/x-pack/plugins/cases/server/client/sub_cases/client.ts b/x-pack/plugins/cases/server/client/sub_cases/client.ts
index b35d58ce06010..9a7dad77909e9 100644
--- a/x-pack/plugins/cases/server/client/sub_cases/client.ts
+++ b/x-pack/plugins/cases/server/client/sub_cases/client.ts
@@ -5,10 +5,13 @@
* 2.0.
*/
+import pMap from 'p-map';
import Boom from '@hapi/boom';
+import { SavedObject } from 'kibana/server';
import {
caseStatuses,
+ CommentAttributes,
SubCaseResponse,
SubCaseResponseRt,
SubCasesFindRequest,
@@ -19,7 +22,7 @@ import {
import { CasesClientArgs, CasesClientInternal } from '..';
import { countAlertsForID, flattenSubCaseSavedObject, transformSubCases } from '../../common';
import { createCaseError } from '../../common/error';
-import { CASE_SAVED_OBJECT } from '../../../common/constants';
+import { CASE_SAVED_OBJECT, MAX_CONCURRENT_SEARCHES } from '../../../common/constants';
import { buildCaseUserActionItem } from '../../services/user_actions/helpers';
import { constructQueryOptions } from '../utils';
import { defaultPage, defaultPerPage } from '../../routes/api';
@@ -121,13 +124,20 @@ async function deleteSubCase(ids: string[], clientArgs: CasesClientArgs): Promis
return acc;
}, new Map());
- await Promise.all(
- comments.saved_objects.map((comment) =>
- attachmentService.delete({ unsecuredSavedObjectsClient, attachmentId: comment.id })
- )
- );
+ const deleteCommentMapper = async (comment: SavedObject) =>
+ attachmentService.delete({ unsecuredSavedObjectsClient, attachmentId: comment.id });
+
+ const deleteSubCasesMapper = async (id: string) =>
+ caseService.deleteSubCase(unsecuredSavedObjectsClient, id);
- await Promise.all(ids.map((id) => caseService.deleteSubCase(unsecuredSavedObjectsClient, id)));
+ // Ensuring we don't too many concurrent deletions running.
+ await pMap(comments.saved_objects, deleteCommentMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
+
+ await pMap(ids, deleteSubCasesMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
const deleteDate = new Date().toISOString();
@@ -181,6 +191,7 @@ async function find(
},
});
+ // casesStatuses are bounded by us. No need to limit concurrent calls.
const [open, inProgress, closed] = await Promise.all([
...caseStatuses.map((status) => {
const { subCase: statusQueryOptions } = constructQueryOptions({
diff --git a/x-pack/plugins/cases/server/common/models/commentable_case.ts b/x-pack/plugins/cases/server/common/models/commentable_case.ts
index 2d1e1e18b5098..241278c77ab48 100644
--- a/x-pack/plugins/cases/server/common/models/commentable_case.ts
+++ b/x-pack/plugins/cases/server/common/models/commentable_case.ts
@@ -34,7 +34,11 @@ import {
flattenSubCaseSavedObject,
transformNewComment,
} from '..';
-import { CASE_SAVED_OBJECT, SUB_CASE_SAVED_OBJECT } from '../../../common/constants';
+import {
+ CASE_SAVED_OBJECT,
+ MAX_DOCS_PER_PAGE,
+ SUB_CASE_SAVED_OBJECT,
+} from '../../../common/constants';
import { AttachmentService, CasesService } from '../../services';
import { createCaseError } from '../error';
import { countAlertsForID } from '../index';
@@ -309,23 +313,13 @@ export class CommentableCase {
public async encode(): Promise {
try {
- const collectionCommentStats = await this.caseService.getAllCaseComments({
- unsecuredSavedObjectsClient: this.unsecuredSavedObjectsClient,
- id: this.collection.id,
- options: {
- fields: [],
- page: 1,
- perPage: 1,
- },
- });
-
const collectionComments = await this.caseService.getAllCaseComments({
unsecuredSavedObjectsClient: this.unsecuredSavedObjectsClient,
id: this.collection.id,
options: {
fields: [],
page: 1,
- perPage: collectionCommentStats.total,
+ perPage: MAX_DOCS_PER_PAGE,
},
});
@@ -335,7 +329,7 @@ export class CommentableCase {
const caseResponse = {
comments: flattenCommentSavedObjects(collectionComments.saved_objects),
totalAlerts: collectionTotalAlerts,
- ...this.formatCollectionForEncoding(collectionCommentStats.total),
+ ...this.formatCollectionForEncoding(collectionComments.total),
};
if (this.subCase) {
diff --git a/x-pack/plugins/cases/server/services/attachments/index.ts b/x-pack/plugins/cases/server/services/attachments/index.ts
index c9b9d11a89689..077f00a571fa7 100644
--- a/x-pack/plugins/cases/server/services/attachments/index.ts
+++ b/x-pack/plugins/cases/server/services/attachments/index.ts
@@ -57,10 +57,10 @@ export class AttachmentService {
public async delete({ unsecuredSavedObjectsClient, attachmentId }: GetAttachmentArgs) {
try {
- this.log.debug(`Attempting to GET attachment ${attachmentId}`);
+ this.log.debug(`Attempting to DELETE attachment ${attachmentId}`);
return await unsecuredSavedObjectsClient.delete(CASE_COMMENT_SAVED_OBJECT, attachmentId);
} catch (error) {
- this.log.error(`Error on GET attachment ${attachmentId}: ${error}`);
+ this.log.error(`Error on DELETE attachment ${attachmentId}: ${error}`);
throw error;
}
}
diff --git a/x-pack/plugins/cases/server/services/cases/index.ts b/x-pack/plugins/cases/server/services/cases/index.ts
index 5618f6c83ff06..fd0a5e878bfb9 100644
--- a/x-pack/plugins/cases/server/services/cases/index.ts
+++ b/x-pack/plugins/cases/server/services/cases/index.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { cloneDeep } from 'lodash';
+import pMap from 'p-map';
import {
KibanaRequest,
Logger,
@@ -43,7 +43,11 @@ import {
groupTotalAlertsByID,
SavedObjectFindOptionsKueryNode,
} from '../../common';
-import { ENABLE_CASE_CONNECTOR } from '../../../common/constants';
+import {
+ ENABLE_CASE_CONNECTOR,
+ MAX_CONCURRENT_SEARCHES,
+ MAX_DOCS_PER_PAGE,
+} from '../../../common/constants';
import { defaultPage, defaultPerPage } from '../../routes/api';
import {
CASE_SAVED_OBJECT,
@@ -250,7 +254,7 @@ export class CasesService {
filter,
]);
- let response = await unsecuredSavedObjectsClient.find<
+ const response = await unsecuredSavedObjectsClient.find<
CommentAttributes,
GetCaseIdsByAlertIdAggs
>({
@@ -259,23 +263,9 @@ export class CasesService {
page: 1,
perPage: 1,
sortField: defaultSortField,
- aggs: this.buildCaseIdsAggs(),
+ aggs: this.buildCaseIdsAggs(MAX_DOCS_PER_PAGE),
filter: combinedFilter,
});
- if (response.total > 100) {
- response = await unsecuredSavedObjectsClient.find<
- CommentAttributes,
- GetCaseIdsByAlertIdAggs
- >({
- type: CASE_COMMENT_SAVED_OBJECT,
- fields: includeFieldsRequiredForAuthentication(),
- page: 1,
- perPage: 1,
- sortField: defaultSortField,
- aggs: this.buildCaseIdsAggs(response.total),
- filter: combinedFilter,
- });
- }
return response;
} catch (error) {
this.log.error(`Error on GET all cases for alert id ${alertId}: ${error}`);
@@ -393,16 +383,6 @@ export class CasesService {
ensureSavedObjectsAreAuthorized: EnsureSOAuthCallback;
subCaseOptions?: SavedObjectFindOptionsKueryNode;
}): Promise {
- const casesStats = await this.findCases({
- unsecuredSavedObjectsClient,
- options: {
- ...caseOptions,
- fields: [],
- page: 1,
- perPage: 1,
- },
- });
-
/**
* This could be made more performant. What we're doing here is retrieving all cases
* that match the API request's filters instead of just counts. This is because we need to grab
@@ -429,7 +409,7 @@ export class CasesService {
...caseOptions,
fields: includeFieldsRequiredForAuthentication([caseTypeField]),
page: 1,
- perPage: casesStats.total,
+ perPage: MAX_DOCS_PER_PAGE,
},
});
@@ -447,7 +427,7 @@ export class CasesService {
if (ENABLE_CASE_CONNECTOR && subCaseOptions) {
subCasesTotal = await this.findSubCaseStatusStats({
unsecuredSavedObjectsClient,
- options: cloneDeep(subCaseOptions),
+ options: subCaseOptions,
ids: caseIds,
});
}
@@ -505,16 +485,18 @@ export class CasesService {
const refType =
associationType === AssociationType.case ? CASE_SAVED_OBJECT : SUB_CASE_SAVED_OBJECT;
- const allComments = await Promise.all(
- ids.map((id) =>
- this.getCommentsByAssociation({
- unsecuredSavedObjectsClient,
- associationType,
- id,
- options: { page: 1, perPage: 1 },
- })
- )
- );
+ const getCommentsMapper = async (id: string) =>
+ this.getCommentsByAssociation({
+ unsecuredSavedObjectsClient,
+ associationType,
+ id,
+ options: { page: 1, perPage: 1 },
+ });
+
+ // Ensuring we don't too many concurrent get running.
+ const allComments = await pMap(ids, getCommentsMapper, {
+ concurrency: MAX_CONCURRENT_SEARCHES,
+ });
const alerts = await this.getCommentsByAssociation({
unsecuredSavedObjectsClient,
@@ -795,7 +777,7 @@ export class CasesService {
this.log.debug(`Attempting to find cases`);
return await unsecuredSavedObjectsClient.find({
sortField: defaultSortField,
- ...cloneDeep(options),
+ ...options,
type: CASE_SAVED_OBJECT,
});
} catch (error) {
@@ -815,24 +797,16 @@ export class CasesService {
if (options?.page !== undefined || options?.perPage !== undefined) {
return unsecuredSavedObjectsClient.find({
sortField: defaultSortField,
- ...cloneDeep(options),
+ ...options,
type: SUB_CASE_SAVED_OBJECT,
});
}
- const stats = await unsecuredSavedObjectsClient.find({
- fields: [],
- page: 1,
- perPage: 1,
- sortField: defaultSortField,
- ...cloneDeep(options),
- type: SUB_CASE_SAVED_OBJECT,
- });
return unsecuredSavedObjectsClient.find({
page: 1,
- perPage: stats.total,
+ perPage: MAX_DOCS_PER_PAGE,
sortField: defaultSortField,
- ...cloneDeep(options),
+ ...options,
type: SUB_CASE_SAVED_OBJECT,
});
} catch (error) {
@@ -902,26 +876,16 @@ export class CasesService {
return unsecuredSavedObjectsClient.find({
type: CASE_COMMENT_SAVED_OBJECT,
sortField: defaultSortField,
- ...cloneDeep(options),
+ ...options,
});
}
- // get the total number of comments that are in ES then we'll grab them all in one go
- const stats = await unsecuredSavedObjectsClient.find({
- type: CASE_COMMENT_SAVED_OBJECT,
- fields: [],
- page: 1,
- perPage: 1,
- sortField: defaultSortField,
- // spread the options after so the caller can override the default behavior if they want
- ...cloneDeep(options),
- });
return unsecuredSavedObjectsClient.find({
type: CASE_COMMENT_SAVED_OBJECT,
page: 1,
- perPage: stats.total,
+ perPage: MAX_DOCS_PER_PAGE,
sortField: defaultSortField,
- ...cloneDeep(options),
+ ...options,
});
} catch (error) {
this.log.error(`Error on GET all comments internal for ${JSON.stringify(id)}: ${error}`);
@@ -1022,20 +986,13 @@ export class CasesService {
}: GetReportersArgs): Promise> {
try {
this.log.debug(`Attempting to GET all reporters`);
- const firstReporters = await unsecuredSavedObjectsClient.find({
- type: CASE_SAVED_OBJECT,
- fields: ['created_by', OWNER_FIELD],
- page: 1,
- perPage: 1,
- filter: cloneDeep(filter),
- });
return await unsecuredSavedObjectsClient.find({
type: CASE_SAVED_OBJECT,
fields: ['created_by', OWNER_FIELD],
page: 1,
- perPage: firstReporters.total,
- filter: cloneDeep(filter),
+ perPage: MAX_DOCS_PER_PAGE,
+ filter,
});
} catch (error) {
this.log.error(`Error on GET all reporters: ${error}`);
@@ -1049,20 +1006,13 @@ export class CasesService {
}: GetTagsArgs): Promise> {
try {
this.log.debug(`Attempting to GET all cases`);
- const firstTags = await unsecuredSavedObjectsClient.find({
- type: CASE_SAVED_OBJECT,
- fields: ['tags', OWNER_FIELD],
- page: 1,
- perPage: 1,
- filter: cloneDeep(filter),
- });
return await unsecuredSavedObjectsClient.find({
type: CASE_SAVED_OBJECT,
fields: ['tags', OWNER_FIELD],
page: 1,
- perPage: firstTags.total,
- filter: cloneDeep(filter),
+ perPage: MAX_DOCS_PER_PAGE,
+ filter,
});
} catch (error) {
this.log.error(`Error on GET tags: ${error}`);
diff --git a/x-pack/plugins/cases/server/services/configure/index.ts b/x-pack/plugins/cases/server/services/configure/index.ts
index 8ea1c903622b7..745e64d9016f9 100644
--- a/x-pack/plugins/cases/server/services/configure/index.ts
+++ b/x-pack/plugins/cases/server/services/configure/index.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { cloneDeep } from 'lodash';
import { Logger, SavedObjectsClientContract } from 'kibana/server';
import { SavedObjectFindOptionsKueryNode } from '../../common';
@@ -63,7 +62,7 @@ export class CaseConfigureService {
try {
this.log.debug(`Attempting to find all case configuration`);
return await unsecuredSavedObjectsClient.find({
- ...cloneDeep(options),
+ ...options,
// Get the latest configuration
sortField: 'created_at',
sortOrder: 'desc',
diff --git a/x-pack/plugins/cases/server/services/user_actions/index.ts b/x-pack/plugins/cases/server/services/user_actions/index.ts
index e691b9305fb37..2c20bf808ad4e 100644
--- a/x-pack/plugins/cases/server/services/user_actions/index.ts
+++ b/x-pack/plugins/cases/server/services/user_actions/index.ts
@@ -12,6 +12,7 @@ import {
CASE_USER_ACTION_SAVED_OBJECT,
CASE_SAVED_OBJECT,
SUB_CASE_SAVED_OBJECT,
+ MAX_DOCS_PER_PAGE,
} from '../../../common/constants';
import { ClientArgs } from '..';
@@ -36,19 +37,12 @@ export class CaseUserActionService {
try {
const id = subCaseId ?? caseId;
const type = subCaseId ? SUB_CASE_SAVED_OBJECT : CASE_SAVED_OBJECT;
- const caseUserActionInfo = await unsecuredSavedObjectsClient.find({
- type: CASE_USER_ACTION_SAVED_OBJECT,
- fields: [],
- hasReference: { type, id },
- page: 1,
- perPage: 1,
- });
return await unsecuredSavedObjectsClient.find({
type: CASE_USER_ACTION_SAVED_OBJECT,
hasReference: { type, id },
page: 1,
- perPage: caseUserActionInfo.total,
+ perPage: MAX_DOCS_PER_PAGE,
sortField: 'action_at',
sortOrder: 'asc',
});
From 7b2c03f2a6714136a7c81417419b820b988c70d3 Mon Sep 17 00:00:00 2001
From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>
Date: Tue, 8 Jun 2021 12:13:16 -0400
Subject: [PATCH 07/13] [Security Solution][Endpoint] Set
`Endpoint.configuration` and `Endpoint.state` optional in endpoint Metadata
(#101592)
* make `Endpoint.configuration` and `Endpoint.state` optional in the Host Metadata
* Set types in generator to also be optional
---
.../security_solution/common/endpoint/generate_data.ts | 4 ++--
.../plugins/security_solution/common/endpoint/types/index.ts | 4 ++--
.../common/utils/validators/is_endpoint_host_isolated.ts | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
index 436f1573639c8..7e03d9b61fc10 100644
--- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
@@ -254,10 +254,10 @@ interface HostInfo {
version: number;
};
};
- configuration: {
+ configuration?: {
isolation: boolean;
};
- state: {
+ state?: {
isolation: boolean;
};
};
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts
index 4367c0d90af79..e006944eb914f 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts
@@ -473,14 +473,14 @@ export type HostMetadata = Immutable<{
version: number;
};
};
- configuration: {
+ configuration?: {
/**
* Shows whether the endpoint is set up to be isolated. (e.g. a user has isolated a host,
* and the endpoint successfully received that action and applied the setting)
*/
isolation?: boolean;
};
- state: {
+ state?: {
/**
* Shows what the current state of the host is. This could differ from `Endpoint.configuration.isolation`
* in some cases, but normally they will match
diff --git a/x-pack/plugins/security_solution/public/common/utils/validators/is_endpoint_host_isolated.ts b/x-pack/plugins/security_solution/public/common/utils/validators/is_endpoint_host_isolated.ts
index 6ca187c52475e..c22f33d524029 100644
--- a/x-pack/plugins/security_solution/public/common/utils/validators/is_endpoint_host_isolated.ts
+++ b/x-pack/plugins/security_solution/public/common/utils/validators/is_endpoint_host_isolated.ts
@@ -13,5 +13,5 @@ import { HostMetadata } from '../../../../common/endpoint/types';
* @param endpointMetadata
*/
export const isEndpointHostIsolated = (endpointMetadata: HostMetadata): boolean => {
- return Boolean(endpointMetadata.Endpoint.state.isolation);
+ return Boolean(endpointMetadata.Endpoint.state?.isolation);
};
From 1c225d71a8edc65d62511bc7775a513453acbf4d Mon Sep 17 00:00:00 2001
From: Nathan Reese
Date: Tue, 8 Jun 2021 10:18:53 -0600
Subject: [PATCH 08/13] [Maps] fix EMS Boundaries with joins not rendering
(#101604)
* [Maps] fix EMS Boundaries with joins do not rendering
* make newData required
---
x-pack/plugins/maps/public/actions/data_request_actions.ts | 4 ++--
x-pack/plugins/maps/public/reducers/map/data_request_utils.ts | 2 +-
x-pack/plugins/maps/public/reducers/map/map.ts | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts
index 6b57da132e895..a2a811a8a4223 100644
--- a/x-pack/plugins/maps/public/actions/data_request_actions.ts
+++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts
@@ -123,7 +123,7 @@ function getDataRequestContext(
dispatch(endDataLoad(layerId, dataId, requestToken, data, meta)),
onLoadError: (dataId: string, requestToken: symbol, errorMessage: string) =>
dispatch(onDataLoadError(layerId, dataId, requestToken, errorMessage)),
- updateSourceData: (newData: unknown) => {
+ updateSourceData: (newData: object) => {
dispatch(updateSourceDataRequest(layerId, newData));
},
isRequestStillActive: (dataId: string, requestToken: symbol) => {
@@ -316,7 +316,7 @@ function onDataLoadError(
};
}
-export function updateSourceDataRequest(layerId: string, newData: unknown) {
+export function updateSourceDataRequest(layerId: string, newData: object) {
return (dispatch: ThunkDispatch) => {
dispatch({
type: UPDATE_SOURCE_DATA_REQUEST,
diff --git a/x-pack/plugins/maps/public/reducers/map/data_request_utils.ts b/x-pack/plugins/maps/public/reducers/map/data_request_utils.ts
index 637f06f1d2e81..18a3fd3d6c150 100644
--- a/x-pack/plugins/maps/public/reducers/map/data_request_utils.ts
+++ b/x-pack/plugins/maps/public/reducers/map/data_request_utils.ts
@@ -38,7 +38,7 @@ export function startDataRequest(
export function updateSourceDataRequest(
state: MapState,
layerId: string,
- newData?: object
+ newData: object
): MapState {
const dataRequest = getDataRequest(state, layerId, SOURCE_DATA_REQUEST_ID);
return dataRequest ? setDataRequest(state, layerId, { ...dataRequest, data: newData }) : state;
diff --git a/x-pack/plugins/maps/public/reducers/map/map.ts b/x-pack/plugins/maps/public/reducers/map/map.ts
index ec622e01dab38..e259e97bf260f 100644
--- a/x-pack/plugins/maps/public/reducers/map/map.ts
+++ b/x-pack/plugins/maps/public/reducers/map/map.ts
@@ -84,7 +84,7 @@ export const DEFAULT_MAP_STATE: MapState = {
__rollbackSettings: null,
};
-export function map(state: MapState = DEFAULT_MAP_STATE, action: any) {
+export function map(state: MapState = DEFAULT_MAP_STATE, action: Record) {
switch (action.type) {
case UPDATE_DRAW_STATE:
return {
@@ -183,7 +183,7 @@ export function map(state: MapState = DEFAULT_MAP_STATE, action: any) {
],
};
case UPDATE_SOURCE_DATA_REQUEST:
- return updateSourceDataRequest(state, action);
+ return updateSourceDataRequest(state, action.layerId, action.newData);
case LAYER_DATA_LOAD_STARTED:
return startDataRequest(
state,
From 6e514d4f5c14df48e6d76decf481aa027f3c59e3 Mon Sep 17 00:00:00 2001
From: Angela Chuang <6295984+angorayc@users.noreply.github.com>
Date: Tue, 8 Jun 2021 17:24:04 +0100
Subject: [PATCH 09/13] disable Add to favorites button for Elastic templates
(#101471)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../timeline/properties/helpers.test.tsx | 153 +++++++++++++++++-
.../timeline/properties/helpers.tsx | 13 +-
2 files changed, 162 insertions(+), 4 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.test.tsx
index 7c9de3bb231f0..296e4c85be88a 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.test.tsx
@@ -8,10 +8,17 @@
import React from 'react';
import { mount } from 'enzyme';
-import { NewTimeline, NewTimelineProps } from './helpers';
+import { AddToFavoritesButton, NewTimeline, NewTimelineProps } from './helpers';
import { useCreateTimelineButton } from './use_create_timeline';
-
-jest.mock('../../../../common/hooks/use_selector');
+import { kibanaObservable, TestProviders } from '../../../../common/mock/test_providers';
+import { timelineActions } from '../../../../timelines/store/timeline';
+import { TimelineStatus, TimelineType } from '../../../../../common/types/timeline';
+import {
+ createSecuritySolutionStorageMock,
+ mockGlobalState,
+ SUB_PLUGINS_REDUCER,
+} from '../../../../common/mock';
+import { createStore } from '../../../../common/store';
jest.mock('./use_create_timeline');
@@ -84,3 +91,143 @@ describe('NewTimeline', () => {
});
});
});
+
+describe('Favorite Button', () => {
+ describe('Non Elastic prebuilt templates', () => {
+ test('should render favorite button', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="timeline-favorite-empty-star"]').exists()).toBeTruthy();
+ });
+
+ test('Favorite button should be enabled ', () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper.find('[data-test-subj="timeline-favorite-empty-star"]').first().prop('disabled')
+ ).toEqual(false);
+ });
+
+ test('Should update isFavorite after clicking on favorite button', async () => {
+ const spy = jest.spyOn(timelineActions, 'updateIsFavorite');
+ const wrapper = mount(
+
+
+
+ );
+
+ wrapper.simulate('click');
+
+ expect(spy).toHaveBeenCalled();
+ });
+
+ test('should disable favorite button with filled star', () => {
+ const { storage } = createSecuritySolutionStorageMock();
+
+ const store = createStore(
+ {
+ ...mockGlobalState,
+ timeline: {
+ ...mockGlobalState.timeline,
+ timelineById: {
+ test: {
+ ...mockGlobalState.timeline.timelineById.test,
+ isFavorite: true,
+ },
+ },
+ },
+ },
+ SUB_PLUGINS_REDUCER,
+ kibanaObservable,
+ storage
+ );
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper.find('[data-test-subj="timeline-favorite-filled-star"]').exists()
+ ).toBeTruthy();
+ });
+ });
+
+ describe('Elast prebuilt templates', () => {
+ test('should disable favorite button', () => {
+ const { storage } = createSecuritySolutionStorageMock();
+
+ const store = createStore(
+ {
+ ...mockGlobalState,
+ timeline: {
+ ...mockGlobalState.timeline,
+ timelineById: {
+ test: {
+ ...mockGlobalState.timeline.timelineById.test,
+ status: TimelineStatus.immutable,
+ timelineType: TimelineType.template,
+ templateTimelineId: 'mock-template-timeline-id',
+ templateTimelineVersion: 1,
+ },
+ },
+ },
+ },
+ SUB_PLUGINS_REDUCER,
+ kibanaObservable,
+ storage
+ );
+ const wrapper = mount(
+
+
+
+ );
+ expect(
+ wrapper.find('[data-test-subj="timeline-favorite-empty-star"]').first().prop('disabled')
+ ).toEqual(true);
+ });
+ });
+
+ describe('Custom templates', () => {
+ test('should enable favorite button', () => {
+ const { storage } = createSecuritySolutionStorageMock();
+
+ const store = createStore(
+ {
+ ...mockGlobalState,
+ timeline: {
+ ...mockGlobalState.timeline,
+ timelineById: {
+ test: {
+ ...mockGlobalState.timeline.timelineById.test,
+ status: TimelineStatus.active,
+ timelineType: TimelineType.template,
+ templateTimelineId: 'mock-template-timeline-id',
+ templateTimelineVersion: 1,
+ },
+ },
+ },
+ },
+ SUB_PLUGINS_REDUCER,
+ kibanaObservable,
+ storage
+ );
+ const wrapper = mount(
+
+
+
+ );
+ expect(
+ wrapper.find('[data-test-subj="timeline-favorite-empty-star"]').first().prop('disabled')
+ ).toEqual(false);
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
index 165de178768f2..5f5cab00fcbbe 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
@@ -10,7 +10,11 @@ import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { useDispatch } from 'react-redux';
-import { TimelineTypeLiteral, TimelineType } from '../../../../../common/types/timeline';
+import {
+ TimelineTypeLiteral,
+ TimelineType,
+ TimelineStatus,
+} from '../../../../../common/types/timeline';
import { timelineActions, timelineSelectors } from '../../../../timelines/store/timeline';
import { useShallowEqualSelector } from '../../../../common/hooks/use_selector';
@@ -36,6 +40,12 @@ const AddToFavoritesButtonComponent: React.FC = ({ ti
(state) => (getTimeline(state, timelineId) ?? timelineDefaults).isFavorite
);
+ const status = useShallowEqualSelector(
+ (state) => (getTimeline(state, timelineId) ?? timelineDefaults).status
+ );
+
+ const disableFavoriteButton = status === TimelineStatus.immutable;
+
const handleClick = useCallback(
() => dispatch(timelineActions.updateIsFavorite({ id: timelineId, isFavorite: !isFavorite })),
[dispatch, timelineId, isFavorite]
@@ -48,6 +58,7 @@ const AddToFavoritesButtonComponent: React.FC = ({ ti
iconType={isFavorite ? 'starFilled' : 'starEmpty'}
onClick={handleClick}
data-test-subj={`timeline-favorite-${isFavorite ? 'filled' : 'empty'}-star`}
+ disabled={disableFavoriteButton}
>
{isFavorite ? i18n.REMOVE_FROM_FAVORITES : i18n.ADD_TO_FAVORITES}
From 664cc5f1630bb49aa7def4c316880639dc6618e0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20St=C3=BCrmer?=
Date: Tue, 8 Jun 2021 18:29:50 +0200
Subject: [PATCH 10/13] [Logs UI] Fix applying a filter from the log entry
detail flyout (#101011)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
x-pack/plugins/infra/common/typed_json.ts | 1 +
.../log_entry_fields_table.tsx | 25 +++++++++++++++++--
.../log_entry_flyout/log_entry_flyout.tsx | 3 ++-
.../log_entry_rate/page_results_content.tsx | 12 ++++-----
.../pages/logs/stream/page_logs_content.tsx | 12 +++++----
5 files changed, 38 insertions(+), 15 deletions(-)
diff --git a/x-pack/plugins/infra/common/typed_json.ts b/x-pack/plugins/infra/common/typed_json.ts
index b14de38c47eac..5b7bbdcfbc07b 100644
--- a/x-pack/plugins/infra/common/typed_json.ts
+++ b/x-pack/plugins/infra/common/typed_json.ts
@@ -11,6 +11,7 @@ import { JsonArray, JsonObject, JsonValue } from '../../../../src/plugins/kibana
export { JsonArray, JsonObject, JsonValue };
export const jsonScalarRT = rt.union([rt.null, rt.boolean, rt.number, rt.string]);
+export type JsonScalar = rt.TypeOf;
export const jsonValueRT: rt.Type = rt.recursion('JsonValue', () =>
rt.union([jsonScalarRT, jsonArrayRT, jsonObjectRT])
diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx
index 3b19a3eb0902c..d061866cf24a8 100644
--- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_fields_table.tsx
@@ -7,21 +7,37 @@
import { EuiBasicTableColumn, EuiInMemoryTable } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
+import * as rt from 'io-ts';
import React, { useMemo } from 'react';
+import { Query } from '../../../../../../../src/plugins/data/public';
import { LogEntryField } from '../../../../common/log_entry';
import { LogEntry } from '../../../../common/search_strategies/log_entries/log_entry';
import { TimeKey } from '../../../../common/time';
+import { JsonScalar, jsonScalarRT } from '../../../../common/typed_json';
import { FieldValue } from '../log_text_stream/field_value';
export const LogEntryFieldsTable: React.FC<{
logEntry: LogEntry;
- onSetFieldFilter?: (filter: string, logEntryId: string, timeKey?: TimeKey) => void;
+ onSetFieldFilter?: (filter: Query, logEntryId: string, timeKey?: TimeKey) => void;
}> = ({ logEntry, onSetFieldFilter }) => {
const createSetFilterHandler = useMemo(
() =>
onSetFieldFilter
? (field: LogEntryField) => () => {
- onSetFieldFilter?.(`${field.field}:"${field.value}"`, logEntry.id, logEntry.cursor);
+ if (!rt.array(jsonScalarRT).is(field.value)) {
+ return;
+ }
+
+ onSetFieldFilter?.(
+ {
+ language: 'kuery',
+ query: `${escapeKueryLiteral(field.field)}:${field.value
+ .map(escapeKueryLiteral)
+ .join(' OR ')}`,
+ },
+ logEntry.id,
+ logEntry.cursor
+ );
}
: undefined,
[logEntry, onSetFieldFilter]
@@ -98,3 +114,8 @@ const setFilterButtonLabel = i18n.translate('xpack.infra.logFlyout.filterAriaLab
const setFilterButtonDescription = i18n.translate('xpack.infra.logFlyout.setFilterTooltip', {
defaultMessage: 'View event with filter',
});
+
+const escapeKueryLiteral = (unquotedLiteral: JsonScalar): JsonScalar =>
+ typeof unquotedLiteral === 'string'
+ ? `"${unquotedLiteral.replace(/"/g, '\\"')}"`
+ : unquotedLiteral;
diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
index dcd36d73dd0bb..a296b7063282a 100644
--- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
+++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx
@@ -18,6 +18,7 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useEffect } from 'react';
+import type { Query } from '../../../../../../../src/plugins/data/public';
import { TimeKey } from '../../../../common/time';
import { useLogEntry } from '../../../containers/logs/log_entry';
import { CenteredEuiFlyoutBody } from '../../centered_flyout_body';
@@ -29,7 +30,7 @@ import { LogEntryFieldsTable } from './log_entry_fields_table';
export interface LogEntryFlyoutProps {
logEntryId: string | null | undefined;
onCloseFlyout: () => void;
- onSetFieldFilter?: (filter: string, logEntryId: string, timeKey?: TimeKey) => void;
+ onSetFieldFilter?: (filter: Query, logEntryId: string, timeKey?: TimeKey) => void;
sourceId: string | null | undefined;
}
diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
index 54617d025652b..0741423fd3886 100644
--- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_results_content.tsx
@@ -10,9 +10,11 @@ import moment from 'moment';
import { stringify } from 'query-string';
import React, { useCallback, useMemo } from 'react';
import { encode, RisonValue } from 'rison-node';
+import type { Query } from '../../../../../../../src/plugins/data/public';
import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { useTrackPageview } from '../../../../../observability/public';
+import { isJobStatusWithResults } from '../../../../common/log_analysis';
import { TimeKey } from '../../../../common/time';
import {
CategoryJobNoticesSection,
@@ -27,10 +29,9 @@ import { useLogEntryRateModuleContext } from '../../../containers/logs/log_analy
import { useLogEntryFlyoutContext } from '../../../containers/logs/log_flyout';
import { useLogSourceContext } from '../../../containers/logs/log_source';
import { AnomaliesResults } from './sections/anomalies';
-import { useLogEntryAnomaliesResults } from './use_log_entry_anomalies_results';
import { useDatasetFiltering } from './use_dataset_filtering';
+import { useLogEntryAnomaliesResults } from './use_log_entry_anomalies_results';
import { useLogAnalysisResultsUrlState } from './use_log_entry_rate_results_url_state';
-import { isJobStatusWithResults } from '../../../../common/log_analysis';
export const SORT_DEFAULTS = {
direction: 'desc' as const,
@@ -102,7 +103,7 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => {
} = useLogEntryFlyoutContext();
const linkToLogStream = useCallback(
- (filter: string, id: string, timeKey?: TimeKey) => {
+ (filterQuery: Query, id: string, timeKey?: TimeKey) => {
const params = {
logPosition: encode({
end: moment(timeRange.value.endTime).format('YYYY-MM-DDTHH:mm:ss.SSSZ'),
@@ -113,10 +114,7 @@ export const LogEntryRateResultsContent: React.FunctionComponent = () => {
flyoutOptions: encode({
surroundingLogsId: id,
}),
- logFilter: encode({
- expression: filter,
- kind: 'kuery',
- }),
+ logFilter: encode(filterQuery),
};
navigateToApp?.('logs', { path: `/stream?${stringify(params)}` });
diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
index 664cccb552202..165a5a03bcc6e 100644
--- a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
+++ b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx
@@ -5,10 +5,12 @@
* 2.0.
*/
-import React, { useContext, useCallback, useMemo, useEffect } from 'react';
+import React, { useCallback, useContext, useEffect, useMemo } from 'react';
import usePrevious from 'react-use/lib/usePrevious';
-import { LogEntry } from '../../../../common/log_entry';
+import type { Query } from '../../../../../../../src/plugins/data/public';
import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
+import { LogEntry } from '../../../../common/log_entry';
+import { TimeKey } from '../../../../common/time';
import { AutoSizer } from '../../../components/auto_sizer';
import { LogEntryFlyout } from '../../../components/logging/log_entry_flyout';
import { LogMinimap } from '../../../components/logging/log_minimap';
@@ -23,14 +25,14 @@ import {
import { LogHighlightsState } from '../../../containers/logs/log_highlights';
import { LogPositionState } from '../../../containers/logs/log_position';
import { useLogSourceContext } from '../../../containers/logs/log_source';
+import { useLogStreamContext } from '../../../containers/logs/log_stream';
import { WithSummary } from '../../../containers/logs/log_summary';
import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration';
import { ViewLogInContext } from '../../../containers/logs/view_log_in_context';
import { WithLogTextviewUrlState } from '../../../containers/logs/with_log_textview';
+import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath';
import { LogsToolbar } from './page_toolbar';
import { PageViewLogInContext } from './page_view_log_in_context';
-import { useLogStreamContext } from '../../../containers/logs/log_stream';
-import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath';
const PAGE_THRESHOLD = 2;
@@ -190,7 +192,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
);
const setFilter = useCallback(
- (filter, flyoutItemId, timeKey) => {
+ (filter: Query, flyoutItemId: string, timeKey: TimeKey | undefined | null) => {
applyLogFilterQuery(filter);
if (timeKey) {
jumpToTargetPosition(timeKey);
From 59dae3defde6b7186be7e6d55b33c25e4e58060b Mon Sep 17 00:00:00 2001
From: Lisa Cawley
Date: Tue, 8 Jun 2021 09:37:50 -0700
Subject: [PATCH 11/13] [ML] Add popover help in Single Metric Viewer (#101446)
---
.../timeseriesexplorer/timeseriesexplorer.js | 112 ++++++++++--------
.../timeseriesexplorer_help_popover.tsx | 64 ++++++++++
2 files changed, 125 insertions(+), 51 deletions(-)
create mode 100644 x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_help_popover.tsx
diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js
index c2b806abcf286..b6b63d7713b85 100644
--- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js
+++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js
@@ -33,6 +33,7 @@ import {
EuiBadge,
} from '@elastic/eui';
import { ResizeChecker } from '../../../../../../src/plugins/kibana_utils/public';
+import { TimeSeriesExplorerHelpPopover } from './timeseriesexplorer_help_popover';
import { ANOMALIES_TABLE_DEFAULT_QUERY_SIZE } from '../../../common/constants/search';
import {
@@ -1083,58 +1084,67 @@ export class TimeSeriesExplorer extends React.Component {
hasResults === true && (
-
-
-
- {i18n.translate('xpack.ml.timeSeriesExplorer.singleTimeSeriesAnalysisTitle', {
- defaultMessage: 'Single time series analysis of {functionLabel}',
- values: { functionLabel: chartDetails.functionLabel },
- })}
-
-
- {chartDetails.entityData.count === 1 && (
-
- {chartDetails.entityData.entities.length > 0 && '('}
- {chartDetails.entityData.entities
- .map((entity) => {
- return `${entity.fieldName}: ${entity.fieldValue}`;
- })
- .join(', ')}
- {chartDetails.entityData.entities.length > 0 && ')'}
-
- )}
- {chartDetails.entityData.count !== 1 && (
-
- {chartDetails.entityData.entities.map((countData, i) => {
- return (
-
- {i18n.translate(
- 'xpack.ml.timeSeriesExplorer.countDataInChartDetailsDescription',
- {
- defaultMessage:
- '{openBrace}{cardinalityValue} distinct {fieldName} {cardinality, plural, one {} other { values}}{closeBrace}',
- values: {
- openBrace: i === 0 ? '(' : '',
- closeBrace:
- i === chartDetails.entityData.entities.length - 1 ? ')' : '',
- cardinalityValue:
- countData.cardinality === 0
- ? allValuesLabel
- : countData.cardinality,
- cardinality: countData.cardinality,
- fieldName: countData.fieldName,
- },
- }
- )}
- {i !== chartDetails.entityData.entities.length - 1 ? ', ' : ''}
-
- );
- })}
+
+
+
+
+ {i18n.translate(
+ 'xpack.ml.timeSeriesExplorer.singleTimeSeriesAnalysisTitle',
+ {
+ defaultMessage: 'Single time series analysis of {functionLabel}',
+ values: { functionLabel: chartDetails.functionLabel },
+ }
+ )}
- )}
-
-
-
+
+ {chartDetails.entityData.count === 1 && (
+
+ {chartDetails.entityData.entities.length > 0 && '('}
+ {chartDetails.entityData.entities
+ .map((entity) => {
+ return `${entity.fieldName}: ${entity.fieldValue}`;
+ })
+ .join(', ')}
+ {chartDetails.entityData.entities.length > 0 && ')'}
+
+ )}
+ {chartDetails.entityData.count !== 1 && (
+
+ {chartDetails.entityData.entities.map((countData, i) => {
+ return (
+
+ {i18n.translate(
+ 'xpack.ml.timeSeriesExplorer.countDataInChartDetailsDescription',
+ {
+ defaultMessage:
+ '{openBrace}{cardinalityValue} distinct {fieldName} {cardinality, plural, one {} other { values}}{closeBrace}',
+ values: {
+ openBrace: i === 0 ? '(' : '',
+ closeBrace:
+ i === chartDetails.entityData.entities.length - 1
+ ? ')'
+ : '',
+ cardinalityValue:
+ countData.cardinality === 0
+ ? allValuesLabel
+ : countData.cardinality,
+ cardinality: countData.cardinality,
+ fieldName: countData.fieldName,
+ },
+ }
+ )}
+ {i !== chartDetails.entityData.entities.length - 1 ? ', ' : ''}
+
+ );
+ })}
+
+ )}
+
+
+
+
+
+
{showModelBoundsCheckbox && (
diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_help_popover.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_help_popover.tsx
new file mode 100644
index 0000000000000..59249791f0078
--- /dev/null
+++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_help_popover.tsx
@@ -0,0 +1,64 @@
+/*
+ * 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 React, { useState } from 'react';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { HelpPopover, HelpPopoverButton } from '../components/help_popover/help_popover';
+
+export const TimeSeriesExplorerHelpPopover = () => {
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+
+ return (
+ {
+ setIsPopoverOpen(!isPopoverOpen);
+ }}
+ />
+ }
+ closePopover={() => setIsPopoverOpen(false)}
+ isOpen={isPopoverOpen}
+ title={i18n.translate('xpack.ml.timeSeriesExplorer.popoverTitle', {
+ defaultMessage: 'Single time series analysis',
+ })}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
From a9e64abe8c222ee190ff5f9bbbe26180ff6fec70 Mon Sep 17 00:00:00 2001
From: Jason Stoltzfus
Date: Tue, 8 Jun 2021 12:42:50 -0400
Subject: [PATCH 12/13] [App Search] Updated Search UI to new URL (#101320)
---
.../app_search/__mocks__/engine_logic.mock.ts | 1 +
.../components/engine/engine_logic.test.ts | 55 +++++++++++++
.../components/engine/engine_logic.ts | 11 +++
.../components/search_ui_form.test.tsx | 80 ++++++++++++++-----
.../search_ui/components/search_ui_form.tsx | 14 +++-
.../app_search/components/search_ui/i18n.ts | 8 ++
.../search_ui/search_ui_logic.test.ts | 14 +++-
.../components/search_ui/search_ui_logic.ts | 11 ++-
.../components/search_ui/utils.test.ts | 2 +-
.../app_search/components/search_ui/utils.ts | 2 +-
10 files changed, 169 insertions(+), 29 deletions(-)
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts
index d16391089120a..23d638d5f25f3 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts
@@ -12,6 +12,7 @@ import { generateEncodedPath } from '../utils/encode_path_params';
export const mockEngineValues = {
engineName: 'some-engine',
engine: {} as EngineDetails,
+ searchKey: 'search-abc123',
};
export const mockEngineActions = {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts
index 5435450f1bfdb..836265a037e16 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts
@@ -9,6 +9,8 @@ import { LogicMounter, mockHttpValues } from '../../../__mocks__';
import { nextTick } from '@kbn/test/jest';
+import { ApiTokenTypes } from '../credentials/constants';
+
import { EngineTypes } from './types';
import { EngineLogic } from './';
@@ -47,6 +49,7 @@ describe('EngineLogic', () => {
hasSchemaConflicts: false,
hasUnconfirmedSchemaFields: false,
engineNotFound: false,
+ searchKey: '',
};
beforeEach(() => {
@@ -263,5 +266,57 @@ describe('EngineLogic', () => {
});
});
});
+
+ describe('searchKey', () => {
+ it('should select the first available search key for this engine', () => {
+ const engine = {
+ ...mockEngineData,
+ apiTokens: [
+ {
+ key: 'private-123xyz',
+ name: 'privateKey',
+ type: ApiTokenTypes.Private,
+ },
+ {
+ key: 'search-123xyz',
+ name: 'searchKey',
+ type: ApiTokenTypes.Search,
+ },
+ {
+ key: 'search-8910abc',
+ name: 'searchKey2',
+ type: ApiTokenTypes.Search,
+ },
+ ],
+ };
+ mount({ engine });
+
+ expect(EngineLogic.values).toEqual({
+ ...DEFAULT_VALUES,
+ engine,
+ searchKey: 'search-123xyz',
+ });
+ });
+
+ it('should return an empty string if none are available', () => {
+ const engine = {
+ ...mockEngineData,
+ apiTokens: [
+ {
+ key: 'private-123xyz',
+ name: 'privateKey',
+ type: ApiTokenTypes.Private,
+ },
+ ],
+ };
+ mount({ engine });
+
+ expect(EngineLogic.values).toEqual({
+ ...DEFAULT_VALUES,
+ engine,
+ searchKey: '',
+ });
+ });
+ });
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts
index 8fbf45bc85e52..5cbe89b364859 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.ts
@@ -8,6 +8,8 @@
import { kea, MakeLogicType } from 'kea';
import { HttpLogic } from '../../../shared/http';
+import { ApiTokenTypes } from '../credentials/constants';
+import { ApiToken } from '../credentials/types';
import { EngineDetails, EngineTypes } from './types';
@@ -21,6 +23,7 @@ interface EngineValues {
hasSchemaConflicts: boolean;
hasUnconfirmedSchemaFields: boolean;
engineNotFound: boolean;
+ searchKey: string;
}
interface EngineActions {
@@ -87,6 +90,14 @@ export const EngineLogic = kea>({
() => [selectors.engine],
(engine) => engine?.unconfirmedFields?.length > 0,
],
+ searchKey: [
+ () => [selectors.engine],
+ (engine: Partial) => {
+ const isSearchKey = (token: ApiToken) => token.type === ApiTokenTypes.Search;
+ const searchKey = (engine.apiTokens || []).find(isSearchKey);
+ return searchKey?.key || '';
+ },
+ ],
}),
listeners: ({ actions, values }) => ({
initializeEngine: async () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.test.tsx
index b015c2dec6c0a..82b925f57f2df 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.test.tsx
@@ -13,7 +13,9 @@ import { setMockValues, setMockActions } from '../../../../__mocks__';
import React from 'react';
-import { shallow } from 'enzyme';
+import { shallow, ShallowWrapper } from 'enzyme';
+
+import { EuiForm } from '@elastic/eui';
import { ActiveField } from '../types';
import { generatePreviewUrl } from '../utils';
@@ -29,6 +31,7 @@ describe('SearchUIForm', () => {
urlField: 'url',
facetFields: ['category'],
sortFields: ['size'],
+ dataLoading: false,
};
const actions = {
onActiveFieldChange: jest.fn(),
@@ -43,10 +46,6 @@ describe('SearchUIForm', () => {
setMockActions(actions);
});
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
it('renders', () => {
const wrapper = shallow();
expect(wrapper.find('[data-test-subj="selectTitle"]').exists()).toBe(true);
@@ -56,6 +55,7 @@ describe('SearchUIForm', () => {
});
describe('title field', () => {
+ beforeEach(() => jest.clearAllMocks());
const subject = () => shallow().find('[data-test-subj="selectTitle"]');
it('renders with its value set from state', () => {
@@ -84,6 +84,7 @@ describe('SearchUIForm', () => {
});
describe('url field', () => {
+ beforeEach(() => jest.clearAllMocks());
const subject = () => shallow().find('[data-test-subj="selectUrl"]');
it('renders with its value set from state', () => {
@@ -112,6 +113,7 @@ describe('SearchUIForm', () => {
});
describe('filters field', () => {
+ beforeEach(() => jest.clearAllMocks());
const subject = () => shallow().find('[data-test-subj="selectFilters"]');
it('renders with its value set from state', () => {
@@ -145,6 +147,7 @@ describe('SearchUIForm', () => {
});
describe('sorts field', () => {
+ beforeEach(() => jest.clearAllMocks());
const subject = () => shallow().find('[data-test-subj="selectSort"]');
it('renders with its value set from state', () => {
@@ -177,26 +180,61 @@ describe('SearchUIForm', () => {
});
});
- it('includes a link to generate the preview', () => {
- (generatePreviewUrl as jest.Mock).mockReturnValue('http://www.example.com?foo=bar');
+ describe('generate preview button', () => {
+ let wrapper: ShallowWrapper;
- setMockValues({
- ...values,
- urlField: 'foo',
- titleField: 'bar',
- facetFields: ['baz'],
- sortFields: ['qux'],
+ beforeAll(() => {
+ jest.clearAllMocks();
+ (generatePreviewUrl as jest.Mock).mockReturnValue('http://www.example.com?foo=bar');
+ setMockValues({
+ ...values,
+ urlField: 'foo',
+ titleField: 'bar',
+ facetFields: ['baz'],
+ sortFields: ['qux'],
+ searchKey: 'search-123abc',
+ });
+ wrapper = shallow();
+ });
+
+ it('should be a submit button', () => {
+ expect(wrapper.find('[data-test-subj="generateSearchUiPreview"]').prop('type')).toBe(
+ 'submit'
+ );
+ });
+
+ it('should be wrapped in a form configured to POST to the preview screen in a new tab', () => {
+ const form = wrapper.find(EuiForm);
+ expect(generatePreviewUrl).toHaveBeenCalledWith({
+ urlField: 'foo',
+ titleField: 'bar',
+ facets: ['baz'],
+ sortFields: ['qux'],
+ });
+ expect(form.prop('action')).toBe('http://www.example.com?foo=bar');
+ expect(form.prop('target')).toBe('_blank');
+ expect(form.prop('method')).toBe('POST');
+ expect(form.prop('component')).toBe('form');
});
- const subject = () =>
- shallow().find('[data-test-subj="generateSearchUiPreview"]');
+ it('should include a searchKey in that form POST', () => {
+ const form = wrapper.find(EuiForm);
+ const hiddenInput = form.find('input[type="hidden"]');
+ expect(hiddenInput.prop('id')).toBe('searchKey');
+ expect(hiddenInput.prop('value')).toBe('search-123abc');
+ });
+ });
- expect(subject().prop('href')).toBe('http://www.example.com?foo=bar');
- expect(generatePreviewUrl).toHaveBeenCalledWith({
- urlField: 'foo',
- titleField: 'bar',
- facets: ['baz'],
- sortFields: ['qux'],
+ it('should disable everything while data is loading', () => {
+ setMockValues({
+ ...values,
+ dataLoading: true,
});
+ const wrapper = shallow();
+ expect(wrapper.find('[data-test-subj="selectTitle"]').prop('disabled')).toBe(true);
+ expect(wrapper.find('[data-test-subj="selectFilters"]').prop('isDisabled')).toBe(true);
+ expect(wrapper.find('[data-test-subj="selectSort"]').prop('isDisabled')).toBe(true);
+ expect(wrapper.find('[data-test-subj="selectUrl"]').prop('disabled')).toBe(true);
+ expect(wrapper.find('[data-test-subj="generateSearchUiPreview"]').prop('disabled')).toBe(true);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.tsx
index 15bd699be721c..b795a46268237 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/components/search_ui_form.tsx
@@ -11,6 +11,7 @@ import { useValues, useActions } from 'kea';
import { EuiForm, EuiFormRow, EuiSelect, EuiComboBox, EuiButton } from '@elastic/eui';
+import { EngineLogic } from '../../engine';
import {
TITLE_FIELD_LABEL,
TITLE_FIELD_HELP_TEXT,
@@ -27,7 +28,9 @@ import { ActiveField } from '../types';
import { generatePreviewUrl } from '../utils';
export const SearchUIForm: React.FC = () => {
+ const { searchKey } = useValues(EngineLogic);
const {
+ dataLoading,
validFields,
validSortFields,
validFacetFields,
@@ -70,9 +73,11 @@ export const SearchUIForm: React.FC = () => {
const selectedFacetOptions = formatMultiOptions(facetFields);
return (
-
+
+
onTitleFieldChange(e.target.value)}
@@ -85,6 +90,7 @@ export const SearchUIForm: React.FC = () => {
onFacetFieldsChange(newValues.map((field) => field.value!))}
@@ -96,6 +102,7 @@ export const SearchUIForm: React.FC = () => {
onSortFieldsChange(newValues.map((field) => field.value!))}
@@ -108,6 +115,7 @@ export const SearchUIForm: React.FC = () => {
onUrlFieldChange(e.target.value)}
@@ -119,8 +127,8 @@ export const SearchUIForm: React.FC = () => {
/>
+ i18n.translate('xpack.enterpriseSearch.appSearch.engine.searchUI.noSearchKeyErrorMessage', {
+ defaultMessage:
+ "It looks like you don't have any Public Search Keys with access to the '{engineName}' engine. Please visit the {credentialsTitle} page to set one up.",
+ values: { engineName, credentialsTitle: CREDENTIALS_TITLE },
+ });
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.test.ts
index 2191c633131bb..2e29ac0d398a6 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.test.ts
@@ -18,7 +18,7 @@ import { SearchUILogic } from './';
describe('SearchUILogic', () => {
const { mount } = new LogicMounter(SearchUILogic);
const { http } = mockHttpValues;
- const { flashAPIErrors } = mockFlashMessageHelpers;
+ const { flashAPIErrors, setErrorMessage } = mockFlashMessageHelpers;
const DEFAULT_VALUES = {
dataLoading: true,
@@ -35,6 +35,7 @@ describe('SearchUILogic', () => {
beforeEach(() => {
jest.clearAllMocks();
mockEngineValues.engineName = 'engine1';
+ mockEngineValues.searchKey = 'search-abc123';
});
it('has expected default values', () => {
@@ -155,6 +156,17 @@ describe('SearchUILogic', () => {
});
});
+ it('will short circuit the call if there is no searchKey available for this engine', async () => {
+ mockEngineValues.searchKey = '';
+ mount();
+
+ SearchUILogic.actions.loadFieldData();
+
+ expect(setErrorMessage).toHaveBeenCalledWith(
+ "It looks like you don't have any Public Search Keys with access to the 'engine1' engine. Please visit the Credentials page to set one up."
+ );
+ });
+
it('handles errors', async () => {
http.get.mockReturnValueOnce(Promise.reject('error'));
mount();
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.ts
index c9e2e5623d9fd..096365f57ea36 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/search_ui_logic.ts
@@ -7,10 +7,12 @@
import { kea, MakeLogicType } from 'kea';
-import { flashAPIErrors } from '../../../shared/flash_messages';
+import { flashAPIErrors, setErrorMessage } from '../../../shared/flash_messages';
import { HttpLogic } from '../../../shared/http';
import { EngineLogic } from '../engine';
+import { NO_SEARCH_KEY_ERROR } from './i18n';
+
import { ActiveField } from './types';
interface InitialFieldValues {
@@ -84,7 +86,12 @@ export const SearchUILogic = kea>
listeners: ({ actions }) => ({
loadFieldData: async () => {
const { http } = HttpLogic.values;
- const { engineName } = EngineLogic.values;
+ const { searchKey, engineName } = EngineLogic.values;
+
+ if (!searchKey) {
+ setErrorMessage(NO_SEARCH_KEY_ERROR(engineName));
+ return;
+ }
const url = `/api/app_search/engines/${engineName}/search_ui/field_config`;
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/utils.test.ts
index 8d99ffc514b3f..9a005649e7dc9 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/utils.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/utils.test.ts
@@ -33,7 +33,7 @@ describe('generatePreviewUrl', () => {
empty2: [''], // Empty fields should be stripped
})
).toEqual(
- 'http://localhost:3002/as/engines/national-parks-demo/reference_application/preview?facets[]=baz&facets[]=qux&sortFields[]=quux&sortFields[]=quuz&titleField=foo&urlField=bar'
+ 'http://localhost:3002/as/engines/national-parks-demo/search_experience/preview?facets[]=baz&facets[]=qux&sortFields[]=quux&sortFields[]=quuz&titleField=foo&urlField=bar'
);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/utils.ts
index 72d90514ea0a0..6c57df0d66a2a 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/utils.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/search_ui/utils.ts
@@ -15,7 +15,7 @@ export const generatePreviewUrl = (query: ParsedQuery) => {
return queryString.stringifyUrl(
{
query,
- url: getAppSearchUrl(`/engines/${engineName}/reference_application/preview`),
+ url: getAppSearchUrl(`/engines/${engineName}/search_experience/preview`),
},
{ arrayFormat: 'bracket', skipEmptyString: true }
);
From bdafd27e19b727427df4dd9ab81d6529663b9e3e Mon Sep 17 00:00:00 2001
From: Spencer
Date: Tue, 8 Jun 2021 09:54:05 -0700
Subject: [PATCH 13/13] [ts] migrate `x-pack/test` to composite ts project
(#101441)
Co-authored-by: spalger
---
src/plugins/telemetry/tsconfig.json | 9 +-
...d_es_archiver.js => extend_es_archiver.ts} | 21 +-
test/common/services/kibana_server/index.ts | 1 -
test/tsconfig.json | 6 +-
tsconfig.refs.json | 1 +
.../telemetry_collection_xpack/tsconfig.json | 5 +-
.../page_objects/account_settings_page.ts | 43 +-
.../functional/page_objects/banners_page.ts | 30 +-
.../test/functional/page_objects/gis_page.ts | 1187 +++++++++--------
.../functional/page_objects/graph_page.ts | 441 +++---
.../page_objects/grok_debugger_page.ts | 18 +-
x-pack/test/functional/page_objects/index.ts | 73 +-
.../functional/page_objects/logstash_page.js | 25 -
.../functional/page_objects/logstash_page.ts | 25 +
.../page_objects/monitoring_page.ts | 72 +-
.../page_objects/navigational_search.ts | 138 +-
.../functional/page_objects/reporting_page.ts | 246 ++--
.../functional/page_objects/rollup_page.ts | 233 ++--
.../functional/page_objects/security_page.ts | 812 +++++------
.../page_objects/space_selector_page.ts | 294 ++--
.../functional/page_objects/status_page.ts | 21 +-
.../page_objects/tag_management_page.ts | 785 +++++------
.../page_objects/upgrade_assistant_page.ts | 116 +-
.../functional/page_objects/uptime_page.ts | 214 +--
.../functional/page_objects/watcher_page.ts | 99 +-
x-pack/test/functional/services/index.ts | 4 +-
.../test/functional/services/ml/common_ui.ts | 2 +-
.../services/ml/job_wizard_common.ts | 8 +-
.../functional/services/search_sessions.ts | 298 ++---
.../services/usage.ts | 2 +-
.../common/lib/saved_object_test_utils.ts | 124 +-
.../page_objects/detections/index.ts | 277 ++--
x-pack/test/tsconfig.json | 19 +-
33 files changed, 2842 insertions(+), 2807 deletions(-)
rename test/common/services/kibana_server/{extend_es_archiver.js => extend_es_archiver.ts} (70%)
delete mode 100644 x-pack/test/functional/page_objects/logstash_page.js
create mode 100644 x-pack/test/functional/page_objects/logstash_page.ts
diff --git a/src/plugins/telemetry/tsconfig.json b/src/plugins/telemetry/tsconfig.json
index 3b043b8aab895..710e209537b8e 100644
--- a/src/plugins/telemetry/tsconfig.json
+++ b/src/plugins/telemetry/tsconfig.json
@@ -8,7 +8,14 @@
"declarationMap": true,
"isolatedModules": true
},
- "include": ["public/**/**/*", "server/**/**/*", "common/**/*", "../../../typings/**/*"],
+ "include": [
+ "public/**/**/*",
+ "server/**/**/*",
+ "common/**/*",
+ "../../../typings/**/*",
+ "schema/oss_plugins.json",
+ "schema/oss_root.json",
+ ],
"references": [
{ "path": "../../core/tsconfig.json" },
{ "path": "../../plugins/kibana_react/tsconfig.json" },
diff --git a/test/common/services/kibana_server/extend_es_archiver.js b/test/common/services/kibana_server/extend_es_archiver.ts
similarity index 70%
rename from test/common/services/kibana_server/extend_es_archiver.js
rename to test/common/services/kibana_server/extend_es_archiver.ts
index 9a06dd7b74969..98c28960bf523 100644
--- a/test/common/services/kibana_server/extend_es_archiver.js
+++ b/test/common/services/kibana_server/extend_es_archiver.ts
@@ -6,10 +6,23 @@
* Side Public License, v 1.
*/
-const ES_ARCHIVER_LOAD_METHODS = ['load', 'loadIfNeeded', 'unload', 'emptyKibanaIndex'];
+import type { ProvidedType } from '@kbn/test';
+
+import type { EsArchiverProvider } from '../es_archiver';
+import type { RetryService } from '../retry';
+import type { KibanaServerProvider } from './kibana_server';
+
+const ES_ARCHIVER_LOAD_METHODS = ['load', 'loadIfNeeded', 'unload', 'emptyKibanaIndex'] as const;
const KIBANA_INDEX = '.kibana';
-export function extendEsArchiver({ esArchiver, kibanaServer, retry, defaults }) {
+interface Options {
+ esArchiver: ProvidedType;
+ kibanaServer: ProvidedType;
+ retry: RetryService;
+ defaults: Record;
+}
+
+export function extendEsArchiver({ esArchiver, kibanaServer, retry, defaults }: Options) {
// only extend the esArchiver if there are default uiSettings to restore
if (!defaults) {
return;
@@ -18,9 +31,9 @@ export function extendEsArchiver({ esArchiver, kibanaServer, retry, defaults })
ES_ARCHIVER_LOAD_METHODS.forEach((method) => {
const originalMethod = esArchiver[method];
- esArchiver[method] = async (...args) => {
+ esArchiver[method] = async (...args: unknown[]) => {
// esArchiver methods return a stats object, with information about the indexes created
- const stats = await originalMethod.apply(esArchiver, args);
+ const stats = await originalMethod.apply(esArchiver, args as any);
const statsKeys = Object.keys(stats);
const kibanaKeys = statsKeys.filter(
diff --git a/test/common/services/kibana_server/index.ts b/test/common/services/kibana_server/index.ts
index 7f727179032f2..2a749de2b4ce8 100644
--- a/test/common/services/kibana_server/index.ts
+++ b/test/common/services/kibana_server/index.ts
@@ -7,5 +7,4 @@
*/
export { KibanaServerProvider } from './kibana_server';
-// @ts-ignore
export { extendEsArchiver } from './extend_es_archiver';
diff --git a/test/tsconfig.json b/test/tsconfig.json
index 2524755d3f291..3e02283946080 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -11,7 +11,11 @@
"include": [
"**/*",
"../typings/**/*",
- "../packages/kbn-test/types/ftr_globals/**/*"
+ "../packages/kbn-test/types/ftr_globals/**/*",
+ "api_integration/apis/logstash/pipeline/fixtures/*.json",
+ "api_integration/apis/logstash/pipelines/fixtures/*.json",
+ "api_integration/apis/telemetry/fixtures/*.json",
+ "api_integration/apis/telemetry/fixtures/*.json",
],
"exclude": ["target/**/*", "plugin_functional/plugins/**/*", "interpreter_functional/plugins/**/*"],
"references": [
diff --git a/tsconfig.refs.json b/tsconfig.refs.json
index a2c1ee43a92c4..3baf5c323ef81 100644
--- a/tsconfig.refs.json
+++ b/tsconfig.refs.json
@@ -119,5 +119,6 @@
{ "path": "./x-pack/plugins/index_lifecycle_management/tsconfig.json" },
{ "path": "./x-pack/plugins/uptime/tsconfig.json" },
{ "path": "./x-pack/plugins/xpack_legacy/tsconfig.json" },
+ { "path": "./x-pack/test/tsconfig.json" },
]
}
diff --git a/x-pack/plugins/telemetry_collection_xpack/tsconfig.json b/x-pack/plugins/telemetry_collection_xpack/tsconfig.json
index 1221200c7548c..f4c17c4317a9f 100644
--- a/x-pack/plugins/telemetry_collection_xpack/tsconfig.json
+++ b/x-pack/plugins/telemetry_collection_xpack/tsconfig.json
@@ -11,7 +11,10 @@
"include": [
"common/**/*",
"server/**/*",
- "../../../typings/*"
+ "../../../typings/*",
+ "schema/xpack_monitoring.json",
+ "schema/xpack_plugins.json",
+ "schema/xpack_root.json",
],
"references": [
{ "path": "../../../src/core/tsconfig.json" },
diff --git a/x-pack/test/functional/page_objects/account_settings_page.ts b/x-pack/test/functional/page_objects/account_settings_page.ts
index 2aeb255ad7eb7..5bf5be9030b75 100644
--- a/x-pack/test/functional/page_objects/account_settings_page.ts
+++ b/x-pack/test/functional/page_objects/account_settings_page.ts
@@ -6,33 +6,30 @@
*/
import expect from '@kbn/expect';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
-export function AccountSettingProvider({ getService }: FtrProviderContext) {
- const testSubjects = getService('testSubjects');
- const userMenu = getService('userMenu');
+export class AccountSettingsPageObject extends FtrService {
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly userMenu = this.ctx.getService('userMenu');
- class AccountSettingsPage {
- async verifyAccountSettings(expectedEmail: string, expectedUserName: string) {
- await userMenu.clickProvileLink();
+ async verifyAccountSettings(expectedEmail: string, expectedUserName: string) {
+ await this.userMenu.clickProvileLink();
- const usernameField = await testSubjects.find('username');
- const userName = await usernameField.getVisibleText();
- expect(userName).to.be(expectedUserName);
+ const usernameField = await this.testSubjects.find('username');
+ const userName = await usernameField.getVisibleText();
+ expect(userName).to.be(expectedUserName);
- const emailIdField = await testSubjects.find('email');
- const emailField = await emailIdField.getVisibleText();
- expect(emailField).to.be(expectedEmail);
- await userMenu.closeMenu();
- }
+ const emailIdField = await this.testSubjects.find('email');
+ const emailField = await emailIdField.getVisibleText();
+ expect(emailField).to.be(expectedEmail);
+ await this.userMenu.closeMenu();
+ }
- async changePassword(currentPassword: string, newPassword: string) {
- await testSubjects.setValue('currentPassword', currentPassword);
- await testSubjects.setValue('newPassword', newPassword);
- await testSubjects.setValue('confirmNewPassword', newPassword);
- await testSubjects.click('changePasswordButton');
- await testSubjects.existOrFail('passwordUpdateSuccess');
- }
+ async changePassword(currentPassword: string, newPassword: string) {
+ await this.testSubjects.setValue('currentPassword', currentPassword);
+ await this.testSubjects.setValue('newPassword', newPassword);
+ await this.testSubjects.setValue('confirmNewPassword', newPassword);
+ await this.testSubjects.click('changePasswordButton');
+ await this.testSubjects.existOrFail('passwordUpdateSuccess');
}
- return new AccountSettingsPage();
}
diff --git a/x-pack/test/functional/page_objects/banners_page.ts b/x-pack/test/functional/page_objects/banners_page.ts
index d2e4e43cec117..b9e9b29b4eeb2 100644
--- a/x-pack/test/functional/page_objects/banners_page.ts
+++ b/x-pack/test/functional/page_objects/banners_page.ts
@@ -5,26 +5,22 @@
* 2.0.
*/
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
-export function BannersPageProvider({ getService }: FtrProviderContext) {
- const find = getService('find');
+export class BannersPageObject extends FtrService {
+ private readonly find = this.ctx.getService('find');
- class BannersPage {
- isTopBannerVisible() {
- return find.existsByCssSelector('.header__topBanner .kbnUserBanner__container');
- }
+ isTopBannerVisible() {
+ return this.find.existsByCssSelector('.header__topBanner .kbnUserBanner__container');
+ }
- async getTopBannerText() {
- if (!(await this.isTopBannerVisible())) {
- return '';
- }
- const bannerContainer = await find.byCssSelector(
- '.header__topBanner .kbnUserBanner__container'
- );
- return bannerContainer.getVisibleText();
+ async getTopBannerText() {
+ if (!(await this.isTopBannerVisible())) {
+ return '';
}
+ const bannerContainer = await this.find.byCssSelector(
+ '.header__topBanner .kbnUserBanner__container'
+ );
+ return bannerContainer.getVisibleText();
}
-
- return new BannersPage();
}
diff --git a/x-pack/test/functional/page_objects/gis_page.ts b/x-pack/test/functional/page_objects/gis_page.ts
index 99d7172651a4d..c2c92b2370381 100644
--- a/x-pack/test/functional/page_objects/gis_page.ts
+++ b/x-pack/test/functional/page_objects/gis_page.ts
@@ -7,717 +7,718 @@
import _ from 'lodash';
import { APP_ID } from '../../../plugins/maps/common/constants';
-import { FtrProviderContext } from '../ftr_provider_context';
-
-export function GisPageProvider({ getService, getPageObjects }: FtrProviderContext) {
- const PageObjects = getPageObjects(['common', 'header', 'timePicker', 'visualize']);
-
- const log = getService('log');
- const testSubjects = getService('testSubjects');
- const retry = getService('retry');
- const inspector = getService('inspector');
- const find = getService('find');
- const queryBar = getService('queryBar');
- const comboBox = getService('comboBox');
- const renderable = getService('renderable');
- const browser = getService('browser');
- const menuToggle = getService('menuToggle');
- const listingTable = getService('listingTable');
- const monacoEditor = getService('monacoEditor');
- const dashboardPanelActions = getService('dashboardPanelActions');
-
- const setViewPopoverToggle = menuToggle.create({
+import { FtrService } from '../ftr_provider_context';
+
+function escapeLayerName(layerName: string) {
+ return layerName.split(' ').join('_');
+}
+
+export class GisPageObject extends FtrService {
+ private readonly common = this.ctx.getPageObject('common');
+ private readonly header = this.ctx.getPageObject('header');
+ private readonly timePicker = this.ctx.getPageObject('timePicker');
+ private readonly visualize = this.ctx.getPageObject('visualize');
+
+ private readonly log = this.ctx.getService('log');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly inspector = this.ctx.getService('inspector');
+ private readonly find = this.ctx.getService('find');
+ private readonly queryBar = this.ctx.getService('queryBar');
+ private readonly comboBox = this.ctx.getService('comboBox');
+ private readonly renderable = this.ctx.getService('renderable');
+ private readonly browser = this.ctx.getService('browser');
+ private readonly listingTable = this.ctx.getService('listingTable');
+ private readonly monacoEditor = this.ctx.getService('monacoEditor');
+ private readonly dashboardPanelActions = this.ctx.getService('dashboardPanelActions');
+
+ private readonly setViewPopoverToggle = this.ctx.getService('menuToggle').create({
name: 'SetView Popover',
menuTestSubject: 'mapSetViewForm',
toggleButtonTestSubject: 'toggleSetViewVisibilityButton',
});
- function escapeLayerName(layerName: string) {
- return layerName.split(' ').join('_');
- }
+ basePath = '';
- class GisPage {
- basePath;
-
- constructor() {
- this.basePath = '';
- }
+ setBasePath(basePath: string) {
+ this.basePath = basePath;
+ }
- setBasePath(basePath: string) {
- this.basePath = basePath;
- }
+ async setAbsoluteRange(start: string, end: string) {
+ await this.timePicker.setAbsoluteRange(start, end);
+ await this.waitForLayersToLoad();
+ }
- async setAbsoluteRange(start: string, end: string) {
- await PageObjects.timePicker.setAbsoluteRange(start, end);
- await this.waitForLayersToLoad();
- }
+ async setAndSubmitQuery(query: string) {
+ await this.queryBar.setQuery(query);
+ await this.queryBar.submitQuery();
+ await this.waitForLayersToLoad();
+ }
- async setAndSubmitQuery(query: string) {
- await queryBar.setQuery(query);
- await queryBar.submitQuery();
- await this.waitForLayersToLoad();
- }
+ async refreshQuery() {
+ await this.queryBar.submitQuery();
+ await this.waitForLayersToLoad();
+ }
- async refreshQuery() {
- await queryBar.submitQuery();
- await this.waitForLayersToLoad();
- }
+ async enterFullScreen() {
+ this.log.debug(`enterFullScreen`);
+ await this.testSubjects.click('mapsFullScreenMode');
+ await this.retry.try(async () => {
+ await this.testSubjects.exists('exitFullScreenModeLogo');
+ });
+ await this.waitForLayersToLoad();
+ }
- async enterFullScreen() {
- log.debug(`enterFullScreen`);
- await testSubjects.click('mapsFullScreenMode');
- await retry.try(async () => {
- await testSubjects.exists('exitFullScreenModeLogo');
- });
- await this.waitForLayersToLoad();
+ // TODO combine with dashboard full screen into a service
+ async existFullScreen() {
+ this.log.debug(`existFullScreen`);
+ const isFullScreen = await this.testSubjects.exists('exitFullScreenModeLogo');
+ if (isFullScreen) {
+ await this.testSubjects.click('exitFullScreenModeLogo');
}
+ }
- // TODO combine with dashboard full screen into a service
- async existFullScreen() {
- log.debug(`existFullScreen`);
- const isFullScreen = await testSubjects.exists('exitFullScreenModeLogo');
- if (isFullScreen) {
- await testSubjects.click('exitFullScreenModeLogo');
+ // Since there are no DOM indicators that signal when map pan and zoom actions are complete,
+ // this method waits until the map view has stabilized, signaling that the panning/zooming is complete.
+ // Pass origView parameter when the new map view determinition is async
+ // so method knows when panning/zooming has started.
+ async waitForMapPanAndZoom(origView?: { lon: number; lat: number; zoom: number }) {
+ await this.retry.try(async () => {
+ this.log.debug('Waiting for map pan and zoom to complete');
+ const prevView = await this.getView();
+ await this.common.sleep(1000);
+ const currentView = await this.getView();
+ if (origView && _.isEqual(origView, currentView)) {
+ throw new Error('Map pan and zoom has not started yet');
}
- }
+ if (!_.isEqual(prevView, currentView)) {
+ throw new Error('Map is still panning and zooming');
+ }
+ });
+ await this.waitForLayersToLoad();
+ }
- // Since there are no DOM indicators that signal when map pan and zoom actions are complete,
- // this method waits until the map view has stabilized, signaling that the panning/zooming is complete.
- // Pass origView parameter when the new map view determinition is async
- // so method knows when panning/zooming has started.
- async waitForMapPanAndZoom(origView?: { lon: number; lat: number; zoom: number }) {
- await retry.try(async () => {
- log.debug('Waiting for map pan and zoom to complete');
- const prevView = await this.getView();
- await PageObjects.common.sleep(1000);
- const currentView = await this.getView();
- if (origView && _.isEqual(origView, currentView)) {
- throw new Error('Map pan and zoom has not started yet');
- }
- if (!_.isEqual(prevView, currentView)) {
- throw new Error('Map is still panning and zooming');
- }
- });
- await this.waitForLayersToLoad();
- }
+ async waitForLayersToLoad() {
+ this.log.debug('Wait for layers to load');
+ await this.retry.try(async () => {
+ const tableOfContents = await this.testSubjects.find('mapLayerTOC');
+ await tableOfContents.waitForDeletedByCssSelector('.euiLoadingSpinner');
+ });
+ }
- async waitForLayersToLoad() {
- log.debug('Wait for layers to load');
- await retry.try(async () => {
- const tableOfContents = await testSubjects.find('mapLayerTOC');
- await tableOfContents.waitForDeletedByCssSelector('.euiLoadingSpinner');
- });
- }
+ async waitForLayerDeleted(layerName: string) {
+ this.log.debug('Wait for layer deleted');
+ await this.retry.waitFor('Layer to be deleted', async () => {
+ const doesLayerExist = await this.doesLayerExist(layerName);
+ return !doesLayerExist;
+ });
+ }
- async waitForLayerDeleted(layerName: string) {
- log.debug('Wait for layer deleted');
- await retry.waitFor('Layer to be deleted', async () => {
- const doesLayerExist = await this.doesLayerExist(layerName);
- return !doesLayerExist;
- });
- }
+ // use the search filter box to narrow the results down to a single
+ // entry, or at least to a single page of results
+ async loadSavedMap(name: string) {
+ this.log.debug(`Load Saved Map ${name}`);
- // use the search filter box to narrow the results down to a single
- // entry, or at least to a single page of results
- async loadSavedMap(name: string) {
- log.debug(`Load Saved Map ${name}`);
-
- await retry.try(async () => {
- await this.searchForMapWithName(name);
- await listingTable.clickItemLink('map', name);
- await PageObjects.header.waitUntilLoadingHasFinished();
- // check Map landing page is not present
- await testSubjects.missingOrFail('mapLandingPage', { timeout: 10000 });
- });
+ await this.retry.try(async () => {
+ await this.searchForMapWithName(name);
+ await this.listingTable.clickItemLink('map', name);
+ await this.header.waitUntilLoadingHasFinished();
+ // check Map landing page is not present
+ await this.testSubjects.missingOrFail('mapLandingPage', { timeout: 10000 });
+ });
- await this.waitForLayersToLoad();
- }
+ await this.waitForLayersToLoad();
+ }
- async deleteSavedMaps(search: string) {
- await this.searchForMapWithName(search);
- await listingTable.checkListingSelectAllCheckbox();
- await listingTable.clickDeleteSelected();
- await PageObjects.common.clickConfirmOnModal();
+ async deleteSavedMaps(search: string) {
+ await this.searchForMapWithName(search);
+ await this.listingTable.checkListingSelectAllCheckbox();
+ await this.listingTable.clickDeleteSelected();
+ await this.common.clickConfirmOnModal();
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ await this.header.waitUntilLoadingHasFinished();
+ }
- async openNewMap() {
- log.debug(`Open new Map`);
+ async openNewMap() {
+ this.log.debug(`Open new Map`);
- // Navigate directly because we don't need to go through the map listing
- // page. The listing page is skipped if there are no saved objects
- await PageObjects.common.navigateToUrlWithBrowserHistory(APP_ID, '/map');
- await renderable.waitForRender();
- }
+ // Navigate directly because we don't need to go through the map listing
+ // page. The listing page is skipped if there are no saved objects
+ await this.common.navigateToUrlWithBrowserHistory(APP_ID, '/map');
+ await this.renderable.waitForRender();
+ }
- async saveMap(name: string, redirectToOrigin = true, tags?: string[]) {
- await testSubjects.click('mapSaveButton');
- await testSubjects.setValue('savedObjectTitle', name);
- await PageObjects.visualize.setSaveModalValues(name, {
- addToDashboard: false,
- redirectToOrigin,
- saveAsNew: true,
- });
- if (tags) {
- await testSubjects.click('savedObjectTagSelector');
- for (const tagName of tags) {
- await testSubjects.click(`tagSelectorOption-${tagName.replace(' ', '_')}`);
- }
- await testSubjects.click('savedObjectTitle');
+ async saveMap(name: string, redirectToOrigin = true, tags?: string[]) {
+ await this.testSubjects.click('mapSaveButton');
+ await this.testSubjects.setValue('savedObjectTitle', name);
+ await this.visualize.setSaveModalValues(name, {
+ addToDashboard: false,
+ redirectToOrigin,
+ saveAsNew: true,
+ });
+ if (tags) {
+ await this.testSubjects.click('savedObjectTagSelector');
+ for (const tagName of tags) {
+ await this.testSubjects.click(`tagSelectorOption-${tagName.replace(' ', '_')}`);
}
- await testSubjects.clickWhenNotDisabled('confirmSaveSavedObjectButton');
- }
-
- async clickSaveAndReturnButton() {
- await testSubjects.click('mapSaveAndReturnButton');
- }
-
- async expectMissingSaveAndReturnButton() {
- await testSubjects.missingOrFail('mapSaveAndReturnButton');
+ await this.testSubjects.click('savedObjectTitle');
}
+ await this.testSubjects.clickWhenNotDisabled('confirmSaveSavedObjectButton');
+ }
- async expectMissingSaveButton() {
- await testSubjects.missingOrFail('mapSaveButton');
- }
+ async clickSaveAndReturnButton() {
+ await this.testSubjects.click('mapSaveAndReturnButton');
+ }
- async expectMissingCreateNewButton() {
- await testSubjects.missingOrFail('newItemButton');
- }
+ async expectMissingSaveAndReturnButton() {
+ await this.testSubjects.missingOrFail('mapSaveAndReturnButton');
+ }
- async expectMissingAddLayerButton() {
- await testSubjects.missingOrFail('addLayerButton');
- }
+ async expectMissingSaveButton() {
+ await this.testSubjects.missingOrFail('mapSaveButton');
+ }
- async expectExistAddLayerButton() {
- await testSubjects.existOrFail('addLayerButton');
- }
+ async expectMissingCreateNewButton() {
+ await this.testSubjects.missingOrFail('newItemButton');
+ }
- async onMapListingPage() {
- log.debug(`onMapListingPage`);
- return await listingTable.onListingPage('map');
- }
+ async expectMissingAddLayerButton() {
+ await this.testSubjects.missingOrFail('addLayerButton');
+ }
- async onMapPage() {
- log.debug(`onMapPage`);
- return await testSubjects.exists('mapLayerTOC', {
- timeout: 5000,
- });
- }
+ async expectExistAddLayerButton() {
+ await this.testSubjects.existOrFail('addLayerButton');
+ }
- async searchForMapWithName(name: string) {
- log.debug(`searchForMapWithName: ${name}`);
+ async onMapListingPage() {
+ this.log.debug(`onMapListingPage`);
+ return await this.listingTable.onListingPage('map');
+ }
- await this.gotoMapListingPage();
+ async onMapPage() {
+ this.log.debug(`onMapPage`);
+ return await this.testSubjects.exists('mapLayerTOC', {
+ timeout: 5000,
+ });
+ }
- await listingTable.searchForItemWithName(name);
+ async searchForMapWithName(name: string) {
+ this.log.debug(`searchForMapWithName: ${name}`);
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ await this.gotoMapListingPage();
- async getHits() {
- await inspector.open();
- await inspector.openInspectorRequestsView();
- const requestStats = await inspector.getTableData();
- const hits = this.getInspectorStatRowHit(requestStats, 'Hits');
- await inspector.close();
- return hits;
- }
+ await this.listingTable.searchForItemWithName(name);
- async gotoMapListingPage() {
- log.debug('gotoMapListingPage');
- const onPage = await this.onMapListingPage();
- if (!onPage) {
- await retry.try(async () => {
- await PageObjects.common.navigateToUrlWithBrowserHistory(APP_ID, '/');
- const onMapListingPage = await this.onMapListingPage();
- if (!onMapListingPage) throw new Error('Not on map listing page.');
- });
- }
- }
+ await this.header.waitUntilLoadingHasFinished();
+ }
- async searchAndExpectItemsCount(name: string, count: number) {
- await this.gotoMapListingPage();
+ async getHits() {
+ await this.inspector.open();
+ await this.inspector.openInspectorRequestsView();
+ const requestStats = await this.inspector.getTableData();
+ const hits = this.getInspectorStatRowHit(requestStats, 'Hits');
+ await this.inspector.close();
+ return hits;
+ }
- log.debug(`searchAndExpectItemsCount: ${name}`);
- await listingTable.searchAndExpectItemsCount('map', name, count);
+ async gotoMapListingPage() {
+ this.log.debug('gotoMapListingPage');
+ const onPage = await this.onMapListingPage();
+ if (!onPage) {
+ await this.retry.try(async () => {
+ await this.common.navigateToUrlWithBrowserHistory(APP_ID, '/');
+ const onMapListingPage = await this.onMapListingPage();
+ if (!onMapListingPage) throw new Error('Not on map listing page.');
+ });
}
+ }
- async setView(lat: number, lon: number, zoom: number) {
- log.debug(
- `Set view lat: ${lat.toString()}, lon: ${lon.toString()}, zoom: ${zoom.toString()}`
- );
- await setViewPopoverToggle.open();
- await testSubjects.setValue('latitudeInput', lat.toString());
- await testSubjects.setValue('longitudeInput', lon.toString());
- await testSubjects.setValue('zoomInput', zoom.toString());
- await testSubjects.click('submitViewButton');
- await this.waitForMapPanAndZoom();
- }
+ async searchAndExpectItemsCount(name: string, count: number) {
+ await this.gotoMapListingPage();
- async getView() {
- log.debug('Get view');
- await setViewPopoverToggle.open();
- // this method is regularly called within a retry, so we need to reduce the timeouts
- // of the retries done within the getAttribute method in order to ensure that they fail
- // early enough to retry getView()
- const getAttributeOptions = {
- tryTimeout: 5000,
- findTimeout: 1000,
- };
-
- const lat = await testSubjects.getAttribute('latitudeInput', 'value', getAttributeOptions);
- const lon = await testSubjects.getAttribute('longitudeInput', 'value', getAttributeOptions);
- const zoom = await testSubjects.getAttribute('zoomInput', 'value', getAttributeOptions);
-
- await setViewPopoverToggle.close();
- return {
- lat: parseFloat(lat),
- lon: parseFloat(lon),
- zoom: parseFloat(zoom),
- };
- }
+ this.log.debug(`searchAndExpectItemsCount: ${name}`);
+ await this.listingTable.searchAndExpectItemsCount('map', name, count);
+ }
- async toggleLayerVisibility(layerName: string) {
- log.debug(`Toggle layer visibility, layer: ${layerName}`);
- await this.openLayerTocActionsPanel(layerName);
- await testSubjects.click('layerVisibilityToggleButton');
- }
+ async setView(lat: number, lon: number, zoom: number) {
+ this.log.debug(
+ `Set view lat: ${lat.toString()}, lon: ${lon.toString()}, zoom: ${zoom.toString()}`
+ );
+ await this.setViewPopoverToggle.open();
+ await this.testSubjects.setValue('latitudeInput', lat.toString());
+ await this.testSubjects.setValue('longitudeInput', lon.toString());
+ await this.testSubjects.setValue('zoomInput', zoom.toString());
+ await this.testSubjects.click('submitViewButton');
+ await this.waitForMapPanAndZoom();
+ }
- async closeLegend() {
- const isOpen = await testSubjects.exists('mapLayerTOC');
- if (isOpen) {
- await testSubjects.click('mapToggleLegendButton');
- await testSubjects.waitForDeleted('mapLayerTOC');
- }
- }
+ async getView() {
+ this.log.debug('Get view');
+ await this.setViewPopoverToggle.open();
+ // this method is regularly called within a retry, so we need to reduce the timeouts
+ // of the retries done within the getAttribute method in order to ensure that they fail
+ // early enough to retry getView()
+ const getAttributeOptions = {
+ tryTimeout: 5000,
+ findTimeout: 1000,
+ };
+
+ const lat = await this.testSubjects.getAttribute('latitudeInput', 'value', getAttributeOptions);
+ const lon = await this.testSubjects.getAttribute(
+ 'longitudeInput',
+ 'value',
+ getAttributeOptions
+ );
+ const zoom = await this.testSubjects.getAttribute('zoomInput', 'value', getAttributeOptions);
+
+ await this.setViewPopoverToggle.close();
+ return {
+ lat: parseFloat(lat),
+ lon: parseFloat(lon),
+ zoom: parseFloat(zoom),
+ };
+ }
- async clickFitToBounds(layerName: string) {
- log.debug(`Fit to bounds, layer: ${layerName}`);
- const origView = await this.getView();
- await this.openLayerTocActionsPanel(layerName);
- await testSubjects.click('fitToBoundsButton');
- await this.waitForMapPanAndZoom(origView);
- }
+ async toggleLayerVisibility(layerName: string) {
+ this.log.debug(`Toggle layer visibility, layer: ${layerName}`);
+ await this.openLayerTocActionsPanel(layerName);
+ await this.testSubjects.click('layerVisibilityToggleButton');
+ }
- async openLayerTocActionsPanel(layerName: string) {
- const escapedDisplayName = escapeLayerName(layerName);
- const isOpen = await testSubjects.exists(`layerTocActionsPanel${escapedDisplayName}`);
- if (!isOpen) {
- await testSubjects.click(`layerTocActionsPanelToggleButton${escapedDisplayName}`);
- }
+ async closeLegend() {
+ const isOpen = await this.testSubjects.exists('mapLayerTOC');
+ if (isOpen) {
+ await this.testSubjects.click('mapToggleLegendButton');
+ await this.testSubjects.waitForDeleted('mapLayerTOC');
}
+ }
- async openLayerPanel(layerName: string) {
- log.debug(`Open layer panel, layer: ${layerName}`);
- await this.openLayerTocActionsPanel(layerName);
- await testSubjects.click('editLayerButton');
- }
+ async clickFitToBounds(layerName: string) {
+ this.log.debug(`Fit to bounds, layer: ${layerName}`);
+ const origView = await this.getView();
+ await this.openLayerTocActionsPanel(layerName);
+ await this.testSubjects.click('fitToBoundsButton');
+ await this.waitForMapPanAndZoom(origView);
+ }
- async closeLayerPanel() {
- await testSubjects.click('layerPanelCancelButton');
- await this.waitForLayersToLoad();
+ async openLayerTocActionsPanel(layerName: string) {
+ const escapedDisplayName = escapeLayerName(layerName);
+ const isOpen = await this.testSubjects.exists(`layerTocActionsPanel${escapedDisplayName}`);
+ if (!isOpen) {
+ await this.testSubjects.click(`layerTocActionsPanelToggleButton${escapedDisplayName}`);
}
+ }
- async getLayerTOCDetails(layerName: string) {
- return await testSubjects.getVisibleText(`mapLayerTOCDetails${escapeLayerName(layerName)}`);
- }
+ async openLayerPanel(layerName: string) {
+ this.log.debug(`Open layer panel, layer: ${layerName}`);
+ await this.openLayerTocActionsPanel(layerName);
+ await this.testSubjects.click('editLayerButton');
+ }
- async disableApplyGlobalQuery() {
- const isSelected = await testSubjects.getAttribute(
- 'mapLayerPanelApplyGlobalQueryCheckbox',
- 'aria-checked'
- );
- if (isSelected === 'true') {
- await retry.try(async () => {
- log.debug(`disabling applyGlobalQuery`);
- await testSubjects.click('mapLayerPanelApplyGlobalQueryCheckbox');
- const isStillSelected = await testSubjects.getAttribute(
- 'mapLayerPanelApplyGlobalQueryCheckbox',
- 'aria-checked'
- );
- if (isStillSelected === 'true') {
- throw new Error('applyGlobalQuery not disabled');
- }
- });
- await this.waitForLayersToLoad();
- }
- }
+ async closeLayerPanel() {
+ await this.testSubjects.click('layerPanelCancelButton');
+ await this.waitForLayersToLoad();
+ }
- async getNumberOfLayers() {
- const tocEntries = await find.allByCssSelector('.mapTocEntry');
- return tocEntries.length;
- }
+ async getLayerTOCDetails(layerName: string) {
+ return await this.testSubjects.getVisibleText(
+ `mapLayerTOCDetails${escapeLayerName(layerName)}`
+ );
+ }
- async doesLayerExist(layerName: string) {
- return await testSubjects.exists(
- `layerTocActionsPanelToggleButton${escapeLayerName(layerName)}`
- );
+ async disableApplyGlobalQuery() {
+ const isSelected = await this.testSubjects.getAttribute(
+ 'mapLayerPanelApplyGlobalQueryCheckbox',
+ 'aria-checked'
+ );
+ if (isSelected === 'true') {
+ await this.retry.try(async () => {
+ this.log.debug(`disabling applyGlobalQuery`);
+ await this.testSubjects.click('mapLayerPanelApplyGlobalQueryCheckbox');
+ const isStillSelected = await this.testSubjects.getAttribute(
+ 'mapLayerPanelApplyGlobalQueryCheckbox',
+ 'aria-checked'
+ );
+ if (isStillSelected === 'true') {
+ throw new Error('applyGlobalQuery not disabled');
+ }
+ });
+ await this.waitForLayersToLoad();
}
+ }
- async hasFilePickerLoadedFile(fileName: string) {
- log.debug(`Has file picker loaded file ${fileName}`);
- const filePickerText = await find.byCssSelector('.euiFilePicker__promptText');
- const filePickerTextContent = await filePickerText.getVisibleText();
+ async getNumberOfLayers() {
+ const tocEntries = await this.find.allByCssSelector('.mapTocEntry');
+ return tocEntries.length;
+ }
- return fileName === filePickerTextContent;
- }
+ async doesLayerExist(layerName: string) {
+ return await this.testSubjects.exists(
+ `layerTocActionsPanelToggleButton${escapeLayerName(layerName)}`
+ );
+ }
- /*
- * Layer panel utility functions
- */
- async isLayerAddPanelOpen() {
- log.debug(`Is layer add panel open`);
- return await testSubjects.exists('layerAddForm');
- }
+ async hasFilePickerLoadedFile(fileName: string) {
+ this.log.debug(`Has file picker loaded file ${fileName}`);
+ const filePickerText = await this.find.byCssSelector('.euiFilePicker__promptText');
+ const filePickerTextContent = await filePickerText.getVisibleText();
- async waitForLayerAddPanelClosed() {
- let layerAddPanelOpen = false;
- await retry.waitForWithTimeout('Layer add panel closed', 1000, async () => {
- layerAddPanelOpen = await this.isLayerAddPanelOpen();
- return !layerAddPanelOpen;
- });
- }
+ return fileName === filePickerTextContent;
+ }
- async clickAddLayer() {
- log.debug('Click add layer');
- await retry.try(async () => {
- await testSubjects.click('addLayerButton');
- const isOpen = await this.isLayerAddPanelOpen();
- if (!isOpen) {
- throw new Error('Add layer panel still not open, trying again.');
- }
- });
- }
+ /*
+ * Layer panel utility functions
+ */
+ async isLayerAddPanelOpen() {
+ this.log.debug(`Is layer add panel open`);
+ return await this.testSubjects.exists('layerAddForm');
+ }
- async cancelLayerAdd(layerName: string) {
- log.debug(`Cancel layer add`);
- const cancelExists = await testSubjects.exists('layerAddCancelButton');
- if (cancelExists) {
- await testSubjects.click('layerAddCancelButton');
- await this.waitForLayerAddPanelClosed();
- if (layerName) {
- await this.waitForLayerDeleted(layerName);
- }
- }
- }
+ async waitForLayerAddPanelClosed() {
+ let layerAddPanelOpen = false;
+ await this.retry.waitForWithTimeout('Layer add panel closed', 1000, async () => {
+ layerAddPanelOpen = await this.isLayerAddPanelOpen();
+ return !layerAddPanelOpen;
+ });
+ }
- async closeOrCancelLayer(layerName: string) {
- log.debug(`Close or cancel layer add`);
- const cancelExists = await testSubjects.exists('layerAddCancelButton');
- const closeExists = await testSubjects.exists('layerPanelCancelButton');
- if (cancelExists) {
- log.debug(`Cancel layer add.`);
- await testSubjects.click('layerAddCancelButton');
- } else if (closeExists) {
- log.debug(`Close layer add.`);
- await testSubjects.click('layerPanelCancelButton');
- } else {
- log.debug(`No need to close or cancel.`);
- return;
+ async clickAddLayer() {
+ this.log.debug('Click add layer');
+ await this.retry.try(async () => {
+ await this.testSubjects.click('addLayerButton');
+ const isOpen = await this.isLayerAddPanelOpen();
+ if (!isOpen) {
+ throw new Error('Add layer panel still not open, trying again.');
}
+ });
+ }
+ async cancelLayerAdd(layerName: string) {
+ this.log.debug(`Cancel layer add`);
+ const cancelExists = await this.testSubjects.exists('layerAddCancelButton');
+ if (cancelExists) {
+ await this.testSubjects.click('layerAddCancelButton');
await this.waitForLayerAddPanelClosed();
if (layerName) {
await this.waitForLayerDeleted(layerName);
}
}
+ }
- async importFileButtonEnabled() {
- log.debug(`Check "Import file" button enabled`);
- const importFileButton = await testSubjects.find('importFileButton');
- const isDisabled = await importFileButton.getAttribute('disabled');
- return !isDisabled;
+ async closeOrCancelLayer(layerName: string) {
+ this.log.debug(`Close or cancel layer add`);
+ const cancelExists = await this.testSubjects.exists('layerAddCancelButton');
+ const closeExists = await this.testSubjects.exists('layerPanelCancelButton');
+ if (cancelExists) {
+ this.log.debug(`Cancel layer add.`);
+ await this.testSubjects.click('layerAddCancelButton');
+ } else if (closeExists) {
+ this.log.debug(`Close layer add.`);
+ await this.testSubjects.click('layerPanelCancelButton');
+ } else {
+ this.log.debug(`No need to close or cancel.`);
+ return;
+ }
+
+ await this.waitForLayerAddPanelClosed();
+ if (layerName) {
+ await this.waitForLayerDeleted(layerName);
}
+ }
- async importLayerReadyForAdd() {
- log.debug(`Wait until import complete`);
- await testSubjects.find('indexRespCopyButton', 5000);
- let layerAddReady = false;
- await retry.waitForWithTimeout('Add layer button ready', 2000, async () => {
- layerAddReady = await this.importFileButtonEnabled();
- return layerAddReady;
- });
- return layerAddReady;
- }
+ async importFileButtonEnabled() {
+ this.log.debug(`Check "Import file" button enabled`);
+ const importFileButton = await this.testSubjects.find('importFileButton');
+ const isDisabled = await importFileButton.getAttribute('disabled');
+ return !isDisabled;
+ }
- async clickImportFileButton() {
- log.debug(`Click "Import file" button`);
- await testSubjects.click('importFileButton');
- }
+ async importLayerReadyForAdd() {
+ this.log.debug(`Wait until import complete`);
+ await this.testSubjects.find('indexRespCopyButton', 5000);
+ let layerAddReady = false;
+ await this.retry.waitForWithTimeout('Add layer button ready', 2000, async () => {
+ layerAddReady = await this.importFileButtonEnabled();
+ return layerAddReady;
+ });
+ return layerAddReady;
+ }
- async setIndexName(indexName: string) {
- log.debug(`Set index name to: ${indexName}`);
- await testSubjects.setValue('fileUploadIndexNameInput', indexName);
- }
+ async clickImportFileButton() {
+ this.log.debug(`Click "Import file" button`);
+ await this.testSubjects.click('importFileButton');
+ }
- async setIndexType(indexType: string) {
- log.debug(`Set index type to: ${indexType}`);
- await testSubjects.selectValue('fileImportIndexSelect', indexType);
- }
+ async setIndexName(indexName: string) {
+ this.log.debug(`Set index name to: ${indexName}`);
+ await this.testSubjects.setValue('fileUploadIndexNameInput', indexName);
+ }
- async indexTypeOptionExists(indexType: string) {
- log.debug(`Check index type "${indexType}" available`);
- return await find.existsByCssSelector(
- `select[data-test-subj="fileImportIndexSelect"] > option[value="${indexType}"]`
- );
- }
+ async setIndexType(indexType: string) {
+ this.log.debug(`Set index type to: ${indexType}`);
+ await this.testSubjects.selectValue('fileImportIndexSelect', indexType);
+ }
- async clickCopyButton(dataTestSubj: string): Promise {
- log.debug(`Click ${dataTestSubj} copy button`);
+ async indexTypeOptionExists(indexType: string) {
+ this.log.debug(`Check index type "${indexType}" available`);
+ return await this.find.existsByCssSelector(
+ `select[data-test-subj="fileImportIndexSelect"] > option[value="${indexType}"]`
+ );
+ }
- await testSubjects.click(dataTestSubj);
+ async clickCopyButton(dataTestSubj: string): Promise {
+ this.log.debug(`Click ${dataTestSubj} copy button`);
- return await browser.getClipboardValue();
- }
+ await this.testSubjects.click(dataTestSubj);
- async getIndexResults() {
- return JSON.parse(await this.clickCopyButton('indexRespCopyButton'));
- }
+ return await this.browser.getClipboardValue();
+ }
- async getIndexPatternResults() {
- return JSON.parse(await this.clickCopyButton('indexPatternRespCopyButton'));
- }
+ async getIndexResults() {
+ return JSON.parse(await this.clickCopyButton('indexRespCopyButton'));
+ }
- async setLayerQuery(layerName: string, query: string) {
- await this.openLayerPanel(layerName);
- await testSubjects.click('mapLayerPanelOpenFilterEditorButton');
- const filterEditorContainer = await testSubjects.find('mapFilterEditor');
- const queryBarInFilterEditor = await testSubjects.findDescendant(
- 'queryInput',
- filterEditorContainer
- );
- await queryBarInFilterEditor.click();
- const input = await find.activeElement();
- await retry.try(async () => {
- await input.clearValue();
- await input.type(query);
- const value = await input.getAttribute('value');
- if (value !== query) {
- throw new Error(`Layer query set to ${value} instead of ${query}`);
- }
- });
- await testSubjects.click('mapFilterEditorSubmitButton');
- await this.waitForLayersToLoad();
- }
+ async getIndexPatternResults() {
+ return JSON.parse(await this.clickCopyButton('indexPatternRespCopyButton'));
+ }
- async setJoinWhereQuery(layerName: string, query: string) {
- await this.openLayerPanel(layerName);
- await testSubjects.click('mapJoinWhereExpressionButton');
- const filterEditorContainer = await testSubjects.find('mapJoinWhereFilterEditor');
- const queryBarInFilterEditor = await testSubjects.findDescendant(
- 'queryInput',
- filterEditorContainer
- );
- await queryBarInFilterEditor.click();
- const input = await find.activeElement();
+ async setLayerQuery(layerName: string, query: string) {
+ await this.openLayerPanel(layerName);
+ await this.testSubjects.click('mapLayerPanelOpenFilterEditorButton');
+ const filterEditorContainer = await this.testSubjects.find('mapFilterEditor');
+ const queryBarInFilterEditor = await this.testSubjects.findDescendant(
+ 'queryInput',
+ filterEditorContainer
+ );
+ await queryBarInFilterEditor.click();
+ const input = await this.find.activeElement();
+ await this.retry.try(async () => {
await input.clearValue();
await input.type(query);
- await testSubjects.click('mapWhereFilterEditorSubmitButton');
- await this.waitForLayersToLoad();
- }
+ const value = await input.getAttribute('value');
+ if (value !== query) {
+ throw new Error(`Layer query set to ${value} instead of ${query}`);
+ }
+ });
+ await this.testSubjects.click('mapFilterEditorSubmitButton');
+ await this.waitForLayersToLoad();
+ }
- async selectEMSBoundariesSource() {
- log.debug(`Select EMS boundaries source`);
- await testSubjects.click('emsBoundaries');
- }
+ async setJoinWhereQuery(layerName: string, query: string) {
+ await this.openLayerPanel(layerName);
+ await this.testSubjects.click('mapJoinWhereExpressionButton');
+ const filterEditorContainer = await this.testSubjects.find('mapJoinWhereFilterEditor');
+ const queryBarInFilterEditor = await this.testSubjects.findDescendant(
+ 'queryInput',
+ filterEditorContainer
+ );
+ await queryBarInFilterEditor.click();
+ const input = await this.find.activeElement();
+ await input.clearValue();
+ await input.type(query);
+ await this.testSubjects.click('mapWhereFilterEditorSubmitButton');
+ await this.waitForLayersToLoad();
+ }
- async selectGeoJsonUploadSource() {
- log.debug(`Select upload geojson source`);
- await testSubjects.click('uploadGeoJson');
- }
+ async selectEMSBoundariesSource() {
+ this.log.debug(`Select EMS boundaries source`);
+ await this.testSubjects.click('emsBoundaries');
+ }
- async uploadJsonFileForIndexing(path: string) {
- await PageObjects.common.setFileInputPath(path);
- log.debug(`File selected`);
+ async selectGeoJsonUploadSource() {
+ this.log.debug(`Select upload geojson source`);
+ await this.testSubjects.click('uploadGeoJson');
+ }
- await PageObjects.header.waitUntilLoadingHasFinished();
- await this.waitForLayersToLoad();
- }
+ async uploadJsonFileForIndexing(path: string) {
+ await this.common.setFileInputPath(path);
+ this.log.debug(`File selected`);
- // Returns first layer by default
- async selectVectorLayer(vectorLayerName: string) {
- log.debug(`Select EMS vector layer ${vectorLayerName}`);
- if (!vectorLayerName) {
- throw new Error(`You did not provide the EMS layer to select`);
- }
- await comboBox.set('emsVectorComboBox', vectorLayerName);
- await this.waitForLayersToLoad();
- }
+ await this.header.waitUntilLoadingHasFinished();
+ await this.waitForLayersToLoad();
+ }
- async removeLayer(layerName: string) {
- log.debug(`Remove layer ${layerName}`);
- await this.openLayerPanel(layerName);
- await testSubjects.click(`mapRemoveLayerButton`);
- await this.waitForLayerDeleted(layerName);
+ // Returns first layer by default
+ async selectVectorLayer(vectorLayerName: string) {
+ this.log.debug(`Select EMS vector layer ${vectorLayerName}`);
+ if (!vectorLayerName) {
+ throw new Error(`You did not provide the EMS layer to select`);
}
+ await this.comboBox.set('emsVectorComboBox', vectorLayerName);
+ await this.waitForLayersToLoad();
+ }
- async getLayerErrorText(layerName: string) {
- log.debug(`Remove layer ${layerName}`);
- await this.openLayerPanel(layerName);
- return await testSubjects.getVisibleText(`layerErrorMessage`);
- }
+ async removeLayer(layerName: string) {
+ this.log.debug(`Remove layer ${layerName}`);
+ await this.openLayerPanel(layerName);
+ await this.testSubjects.click(`mapRemoveLayerButton`);
+ await this.waitForLayerDeleted(layerName);
+ }
- async fullScreenModeMenuItemExists() {
- return await testSubjects.exists('mapsFullScreenMode');
- }
+ async getLayerErrorText(layerName: string) {
+ this.log.debug(`Remove layer ${layerName}`);
+ await this.openLayerPanel(layerName);
+ return await this.testSubjects.getVisibleText(`layerErrorMessage`);
+ }
- async clickFullScreenMode() {
- log.debug(`clickFullScreenMode`);
- await testSubjects.click('mapsFullScreenMode');
- }
+ async fullScreenModeMenuItemExists() {
+ return await this.testSubjects.exists('mapsFullScreenMode');
+ }
- async exitFullScreenLogoButtonExists() {
- return await testSubjects.exists('exitFullScreenModeLogo');
- }
+ async clickFullScreenMode() {
+ this.log.debug(`clickFullScreenMode`);
+ await this.testSubjects.click('mapsFullScreenMode');
+ }
- async getExitFullScreenLogoButton() {
- return await testSubjects.find('exitFullScreenModeLogo');
- }
+ async exitFullScreenLogoButtonExists() {
+ return await this.testSubjects.exists('exitFullScreenModeLogo');
+ }
- async clickExitFullScreenTextButton() {
- await testSubjects.click('exitFullScreenModeText');
- }
+ async getExitFullScreenLogoButton() {
+ return await this.testSubjects.find('exitFullScreenModeLogo');
+ }
- async openInspectorMapView() {
- await inspector.openInspectorView('~inspectorViewChooserMap');
- }
+ async clickExitFullScreenTextButton() {
+ await this.testSubjects.click('exitFullScreenModeText');
+ }
- // Method should only be used when multiple requests are expected
- // RequestSelector will only display inspectorRequestChooser when there is more than one request
- async openInspectorRequest(requestName: string) {
- await inspector.open();
- await inspector.openInspectorRequestsView();
- log.debug(`Open Inspector request ${requestName}`);
- await testSubjects.click('inspectorRequestChooser');
- await testSubjects.click(`inspectorRequestChooser${requestName}`);
- }
+ async openInspectorMapView() {
+ await this.inspector.openInspectorView('~inspectorViewChooserMap');
+ }
- async doesInspectorHaveRequests() {
- await inspector.open();
- await inspector.openInspectorRequestsView();
- return await testSubjects.exists('inspectorNoRequestsMessage');
- }
+ // Method should only be used when multiple requests are expected
+ // RequestSelector will only display inspectorRequestChooser when there is more than one request
+ async openInspectorRequest(requestName: string) {
+ await this.inspector.open();
+ await this.inspector.openInspectorRequestsView();
+ this.log.debug(`Open Inspector request ${requestName}`);
+ await this.testSubjects.click('inspectorRequestChooser');
+ await this.testSubjects.click(`inspectorRequestChooser${requestName}`);
+ }
- async getMapboxStyle() {
- log.debug('getMapboxStyle');
- await inspector.open();
- await this.openInspectorMapView();
- await testSubjects.click('mapboxStyleTab');
- const mapboxStyleContainer = await testSubjects.find('mapboxStyleContainer');
- const mapboxStyleJson = await mapboxStyleContainer.getVisibleText();
- await inspector.close();
- let mapboxStyle;
- try {
- mapboxStyle = JSON.parse(mapboxStyleJson);
- } catch (err) {
- throw new Error(`Unable to parse mapbox style, error: ${err.message}`);
- }
- return mapboxStyle;
- }
+ async doesInspectorHaveRequests() {
+ await this.inspector.open();
+ await this.inspector.openInspectorRequestsView();
+ return await this.testSubjects.exists('inspectorNoRequestsMessage');
+ }
- async getResponse(requestName: string) {
- await inspector.open();
- const response = await this._getResponse(requestName);
- await inspector.close();
- return response;
- }
+ async getMapboxStyle() {
+ this.log.debug('getMapboxStyle');
+ await this.inspector.open();
+ await this.openInspectorMapView();
+ await this.testSubjects.click('mapboxStyleTab');
+ const mapboxStyleContainer = await this.testSubjects.find('mapboxStyleContainer');
+ const mapboxStyleJson = await mapboxStyleContainer.getVisibleText();
+ await this.inspector.close();
+ let mapboxStyle;
+ try {
+ mapboxStyle = JSON.parse(mapboxStyleJson);
+ } catch (err) {
+ throw new Error(`Unable to parse mapbox style, error: ${err.message}`);
+ }
+ return mapboxStyle;
+ }
- async _getResponse(requestName: string) {
- if (requestName) {
- await testSubjects.click('inspectorRequestChooser');
- await testSubjects.click(`inspectorRequestChooser${requestName}`);
- }
- await inspector.openInspectorRequestsView();
- await testSubjects.click('inspectorRequestDetailResponse');
- await find.byCssSelector('.react-monaco-editor-container');
- const responseBody = await monacoEditor.getCodeEditorValue();
- return JSON.parse(responseBody);
- }
+ async getResponse(requestName: string) {
+ await this.inspector.open();
+ const response = await this._getResponse(requestName);
+ await this.inspector.close();
+ return response;
+ }
- async getResponseFromDashboardPanel(panelTitle: string, requestName: string) {
- await dashboardPanelActions.openInspectorByTitle(panelTitle);
- const response = await this._getResponse(requestName);
- await inspector.close();
- return response;
+ async _getResponse(requestName: string) {
+ if (requestName) {
+ await this.testSubjects.click('inspectorRequestChooser');
+ await this.testSubjects.click(`inspectorRequestChooser${requestName}`);
}
+ await this.inspector.openInspectorRequestsView();
+ await this.testSubjects.click('inspectorRequestDetailResponse');
+ await this.find.byCssSelector('.react-monaco-editor-container');
+ const responseBody = await this.monacoEditor.getCodeEditorValue();
+ return JSON.parse(responseBody);
+ }
- getInspectorStatRowHit(stats: string[][], rowName: string) {
- const STATS_ROW_NAME_INDEX = 0;
- const STATS_ROW_VALUE_INDEX = 1;
+ async getResponseFromDashboardPanel(panelTitle: string, requestName: string) {
+ await this.dashboardPanelActions.openInspectorByTitle(panelTitle);
+ const response = await this._getResponse(requestName);
+ await this.inspector.close();
+ return response;
+ }
- const statsRow = stats.find((row) => {
- return row[STATS_ROW_NAME_INDEX] === rowName;
- });
- if (!statsRow) {
- throw new Error(`Unable to find value for row ${rowName} in ${stats}`);
- }
+ getInspectorStatRowHit(stats: string[][], rowName: string) {
+ const STATS_ROW_NAME_INDEX = 0;
+ const STATS_ROW_VALUE_INDEX = 1;
- return statsRow[STATS_ROW_VALUE_INDEX];
+ const statsRow = stats.find((row) => {
+ return row[STATS_ROW_NAME_INDEX] === rowName;
+ });
+ if (!statsRow) {
+ throw new Error(`Unable to find value for row ${rowName} in ${stats}`);
}
- async triggerSingleRefresh(refreshInterval: number) {
- log.debug(`triggerSingleRefresh, refreshInterval: ${refreshInterval}`);
- await PageObjects.timePicker.resumeAutoRefresh();
- log.debug('waiting to give time for refresh timer to fire');
- await PageObjects.common.sleep(refreshInterval + refreshInterval / 2);
- await PageObjects.timePicker.pauseAutoRefresh();
- await this.waitForLayersToLoad();
- }
+ return statsRow[STATS_ROW_VALUE_INDEX];
+ }
- async lockTooltipAtPosition(xOffset: number, yOffset: number) {
- await retry.try(async () => {
- const mapContainerElement = await testSubjects.find('mapContainer');
- await mapContainerElement.moveMouseTo({ xOffset, yOffset });
- await mapContainerElement.clickMouseButton({ xOffset, yOffset });
- // Close button is only displayed with tooltip is locked
- const hasCloseButton = await testSubjects.exists('mapTooltipCloseButton');
- if (!hasCloseButton) {
- throw new Error('Tooltip is not locked at position');
- }
- });
- }
+ async triggerSingleRefresh(refreshInterval: number) {
+ this.log.debug(`triggerSingleRefresh, refreshInterval: ${refreshInterval}`);
+ await this.timePicker.resumeAutoRefresh();
+ this.log.debug('waiting to give time for refresh timer to fire');
+ await this.common.sleep(refreshInterval + refreshInterval / 2);
+ await this.timePicker.pauseAutoRefresh();
+ await this.waitForLayersToLoad();
+ }
- async setStyleByValue(styleName: string, fieldName: string) {
- await testSubjects.selectValue(`staticDynamicSelect_${styleName}`, 'DYNAMIC');
- await comboBox.set(`styleFieldSelect_${styleName}`, fieldName);
- }
+ async lockTooltipAtPosition(xOffset: number, yOffset: number) {
+ await this.retry.try(async () => {
+ const mapContainerElement = await this.testSubjects.find('mapContainer');
+ await mapContainerElement.moveMouseTo({ xOffset, yOffset });
+ await mapContainerElement.clickMouseButton({ xOffset, yOffset });
+ // Close button is only displayed with tooltip is locked
+ const hasCloseButton = await this.testSubjects.exists('mapTooltipCloseButton');
+ if (!hasCloseButton) {
+ throw new Error('Tooltip is not locked at position');
+ }
+ });
+ }
- async selectCustomColorRamp(styleName: string) {
- // open super select menu
- await testSubjects.click(`colorMapSelect_${styleName}`);
- // Click option
- await testSubjects.click(`colorMapSelectOption_CUSTOM_COLOR_MAP`);
- }
+ async setStyleByValue(styleName: string, fieldName: string) {
+ await this.testSubjects.selectValue(`staticDynamicSelect_${styleName}`, 'DYNAMIC');
+ await this.comboBox.set(`styleFieldSelect_${styleName}`, fieldName);
+ }
- async getCategorySuggestions() {
- return await comboBox.getOptionsList(`colorStopInput1`);
- }
+ async selectCustomColorRamp(styleName: string) {
+ // open super select menu
+ await this.testSubjects.click(`colorMapSelect_${styleName}`);
+ // Click option
+ await this.testSubjects.click(`colorMapSelectOption_CUSTOM_COLOR_MAP`);
+ }
- async enableAutoFitToBounds() {
- await testSubjects.click('openSettingsButton');
- const isEnabled = await testSubjects.getAttribute('autoFitToDataBoundsSwitch', 'checked');
- if (!isEnabled) {
- await retry.try(async () => {
- await testSubjects.click('autoFitToDataBoundsSwitch');
- const ensureEnabled = await testSubjects.getAttribute(
- 'autoFitToDataBoundsSwitch',
- 'checked'
- );
- if (!ensureEnabled) {
- throw new Error('autoFitToDataBoundsSwitch is not enabled');
- }
- });
- }
- await testSubjects.click('mapSettingSubmitButton');
- }
+ async getCategorySuggestions() {
+ return await this.comboBox.getOptionsList(`colorStopInput1`);
+ }
- async refreshAndClearUnsavedChangesWarning() {
- await browser.refresh();
- // accept alert if it pops up
- const alert = await browser.getAlert();
- await alert?.accept();
+ async enableAutoFitToBounds() {
+ await this.testSubjects.click('openSettingsButton');
+ const isEnabled = await this.testSubjects.getAttribute('autoFitToDataBoundsSwitch', 'checked');
+ if (!isEnabled) {
+ await this.retry.try(async () => {
+ await this.testSubjects.click('autoFitToDataBoundsSwitch');
+ const ensureEnabled = await this.testSubjects.getAttribute(
+ 'autoFitToDataBoundsSwitch',
+ 'checked'
+ );
+ if (!ensureEnabled) {
+ throw new Error('autoFitToDataBoundsSwitch is not enabled');
+ }
+ });
}
+ await this.testSubjects.click('mapSettingSubmitButton');
+ }
+
+ async refreshAndClearUnsavedChangesWarning() {
+ await this.browser.refresh();
+ // accept alert if it pops up
+ const alert = await this.browser.getAlert();
+ await alert?.accept();
}
- return new GisPage();
}
diff --git a/x-pack/test/functional/page_objects/graph_page.ts b/x-pack/test/functional/page_objects/graph_page.ts
index 28d72ef844615..bd9e5100e0c57 100644
--- a/x-pack/test/functional/page_objects/graph_page.ts
+++ b/x-pack/test/functional/page_objects/graph_page.ts
@@ -6,7 +6,7 @@
*/
import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
interface Node {
circle: WebElementWrapper;
@@ -20,264 +20,263 @@ interface Edge {
element: WebElementWrapper;
}
-export function GraphPageProvider({ getService, getPageObjects }: FtrProviderContext) {
- const find = getService('find');
- const log = getService('log');
- const testSubjects = getService('testSubjects');
- const PageObjects = getPageObjects(['common', 'header']);
- const retry = getService('retry');
- const browser = getService('browser');
-
- class GraphPage {
- async selectIndexPattern(pattern: string) {
- await testSubjects.click('graphDatasourceButton');
- await testSubjects.click(`savedObjectTitle${pattern.split(' ').join('-')}`);
- // wait till add fields button becomes available, then the index pattern is loaded completely
- await testSubjects.waitForAttributeToChange(
- 'graph-add-field-button',
- 'aria-disabled',
- 'false'
- );
- // Need document focus to not be on `graphDatasourceButton` so its tooltip does not
- // obscure the next intended click area. Focus the adjaecnt input instead.
- await testSubjects.click('queryInput');
- }
+export class GraphPageObject extends FtrService {
+ private readonly find = this.ctx.getService('find');
+ private readonly log = this.ctx.getService('log');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly browser = this.ctx.getService('browser');
+
+ private readonly common = this.ctx.getPageObject('common');
+ private readonly header = this.ctx.getPageObject('header');
+
+ async selectIndexPattern(pattern: string) {
+ await this.testSubjects.click('graphDatasourceButton');
+ await this.testSubjects.click(`savedObjectTitle${pattern.split(' ').join('-')}`);
+ // wait till add fields button becomes available, then the index pattern is loaded completely
+ await this.testSubjects.waitForAttributeToChange(
+ 'graph-add-field-button',
+ 'aria-disabled',
+ 'false'
+ );
+ // Need document focus to not be on `graphDatasourceButton` so its tooltip does not
+ // obscure the next intended click area. Focus the adjaecnt input instead.
+ await this.testSubjects.click('queryInput');
+ }
- async clickAddField() {
- await retry.try(async () => {
- await testSubjects.click('graph-add-field-button');
- await testSubjects.existOrFail('graph-field-search', { timeout: 3000 });
- });
- }
+ async clickAddField() {
+ await this.retry.try(async () => {
+ await this.testSubjects.click('graph-add-field-button');
+ await this.testSubjects.existOrFail('graph-field-search', { timeout: 3000 });
+ });
+ }
- async selectField(field: string) {
- await testSubjects.setValue('graph-field-search', field);
- await find.clickDisplayedByCssSelector(`[title="${field}"]`);
- }
+ async selectField(field: string) {
+ await this.testSubjects.setValue('graph-field-search', field);
+ await this.find.clickDisplayedByCssSelector(`[title="${field}"]`);
+ }
- async addFields(fields: string[]) {
- log.debug('click Add Field icon');
- await this.clickAddField();
- for (const field of fields) {
- log.debug('select field ' + field);
- await this.selectField(field);
- }
+ async addFields(fields: string[]) {
+ this.log.debug('click Add Field icon');
+ await this.clickAddField();
+ for (const field of fields) {
+ this.log.debug('select field ' + field);
+ await this.selectField(field);
}
+ }
- async query(str: string) {
- await testSubjects.click('queryInput');
- await testSubjects.setValue('queryInput', str);
- await testSubjects.click('graph-explore-button');
- }
+ async query(str: string) {
+ await this.testSubjects.click('queryInput');
+ await this.testSubjects.setValue('queryInput', str);
+ await this.testSubjects.click('graph-explore-button');
+ }
- private getPositionAsString(x: string, y: string) {
- return `${x}-${y}`;
- }
+ private getPositionAsString(x: string, y: string) {
+ return `${x}-${y}`;
+ }
- private async getCirclePosition(element: WebElementWrapper) {
- const x = await element.getAttribute('cx');
- const y = await element.getAttribute('cy');
- return this.getPositionAsString(x, y);
- }
+ private async getCirclePosition(element: WebElementWrapper) {
+ const x = await element.getAttribute('cx');
+ const y = await element.getAttribute('cy');
+ return this.getPositionAsString(x, y);
+ }
- private async getLinePositions(element: WebElementWrapper) {
- const x1 = await element.getAttribute('x1');
- const y1 = await element.getAttribute('y1');
- const x2 = await element.getAttribute('x2');
- const y2 = await element.getAttribute('y2');
- return [this.getPositionAsString(x1, y1), this.getPositionAsString(x2, y2)];
- }
+ private async getLinePositions(element: WebElementWrapper) {
+ const x1 = await element.getAttribute('x1');
+ const y1 = await element.getAttribute('y1');
+ const x2 = await element.getAttribute('x2');
+ const y2 = await element.getAttribute('y2');
+ return [this.getPositionAsString(x1, y1), this.getPositionAsString(x2, y2)];
+ }
- async isolateEdge(from: string, to: string) {
- // select all nodes
- await testSubjects.click('graphSelectAll');
-
- // go through all nodes and remove every node not source or target
- const selections = await find.allByCssSelector('.gphSelectionList__field');
- for (const selection of selections) {
- const labelElement = await selection.findByTagName('span');
- const selectionLabel = await labelElement.getVisibleText();
- log.debug('Looking at selection ' + selectionLabel);
- if (selectionLabel !== from && selectionLabel !== to) {
- (await selection.findByClassName('gphNode__text')).click();
- await PageObjects.common.sleep(200);
- }
+ async isolateEdge(from: string, to: string) {
+ // select all nodes
+ await this.testSubjects.click('graphSelectAll');
+
+ // go through all nodes and remove every node not source or target
+ const selections = await this.find.allByCssSelector('.gphSelectionList__field');
+ for (const selection of selections) {
+ const labelElement = await selection.findByTagName('span');
+ const selectionLabel = await labelElement.getVisibleText();
+ this.log.debug('Looking at selection ' + selectionLabel);
+ if (selectionLabel !== from && selectionLabel !== to) {
+ (await selection.findByClassName('gphNode__text')).click();
+ await this.common.sleep(200);
}
+ }
- // invert selection to select all nodes not source or target
- await testSubjects.click('graphInvertSelection');
+ // invert selection to select all nodes not source or target
+ await this.testSubjects.click('graphInvertSelection');
- // remove all other nodes
- await testSubjects.click('graphRemoveSelection');
- }
+ // remove all other nodes
+ await this.testSubjects.click('graphRemoveSelection');
+ }
- async stopLayout() {
- if (await testSubjects.exists('graphPauseLayout')) {
- await testSubjects.click('graphPauseLayout');
- }
+ async stopLayout() {
+ if (await this.testSubjects.exists('graphPauseLayout')) {
+ await this.testSubjects.click('graphPauseLayout');
}
+ }
- async startLayout() {
- if (await testSubjects.exists('graphResumeLayout')) {
- await testSubjects.click('graphResumeLayout');
- }
+ async startLayout() {
+ if (await this.testSubjects.exists('graphResumeLayout')) {
+ await this.testSubjects.click('graphResumeLayout');
}
+ }
- async getGraphObjects() {
- await this.stopLayout();
- // read node labels directly from DOM because getVisibleText is not reliable for the way the graph is rendered
- const nodeNames: string[] = await browser.execute(`
- const elements = document.querySelectorAll('#graphSvg text.gphNode__label');
- return [...elements].map(element => element.innerHTML);
- `);
- const graphElements = await find.allByCssSelector('#graphSvg line, #graphSvg circle');
- const nodes: Node[] = [];
- const nodePositionMap: Record = {};
- const edges: Edge[] = [];
-
- // find all nodes and save their positions
- for (const element of graphElements) {
- const tagName: string = await element.getTagName();
- // check the position of the circle element
- if (tagName === 'circle') {
- nodes.push({ circle: element, label: nodeNames[nodes.length] });
- const position = await this.getCirclePosition(element);
- nodePositionMap[position] = nodes.length - 1;
- }
+ async getGraphObjects() {
+ await this.stopLayout();
+ // read node labels directly from DOM because getVisibleText is not reliable for the way the graph is rendered
+ const nodeNames: string[] = await this.browser.execute(`
+ const elements = document.querySelectorAll('#graphSvg text.gphNode__label');
+ return [...elements].map(element => element.innerHTML);
+ `);
+ const graphElements = await this.find.allByCssSelector('#graphSvg line, #graphSvg circle');
+ const nodes: Node[] = [];
+ const nodePositionMap: Record = {};
+ const edges: Edge[] = [];
+
+ // find all nodes and save their positions
+ for (const element of graphElements) {
+ const tagName: string = await element.getTagName();
+ // check the position of the circle element
+ if (tagName === 'circle') {
+ nodes.push({ circle: element, label: nodeNames[nodes.length] });
+ const position = await this.getCirclePosition(element);
+ nodePositionMap[position] = nodes.length - 1;
}
+ }
- // find all edges
- for (const element of graphElements) {
- const tagName: string = await element.getTagName();
- if (tagName === 'line') {
- const [sourcePosition, targetPosition] = await this.getLinePositions(element);
- const lineStyle = await element.getAttribute('style');
- // grep out the width of the connection from the style attribute
- const strokeWidth = Number(/stroke-width: ?(\d+(\.\d+)?)/.exec(lineStyle)![1]);
- edges.push({
- element,
- width: strokeWidth,
- // look up source and target node by matching start and end coordinates
- // of the edges and the nodes
- sourceNode: nodes[nodePositionMap[sourcePosition]],
- targetNode: nodes[nodePositionMap[targetPosition]],
- });
- }
+ // find all edges
+ for (const element of graphElements) {
+ const tagName: string = await element.getTagName();
+ if (tagName === 'line') {
+ const [sourcePosition, targetPosition] = await this.getLinePositions(element);
+ const lineStyle = await element.getAttribute('style');
+ // grep out the width of the connection from the style attribute
+ const strokeWidth = Number(/stroke-width: ?(\d+(\.\d+)?)/.exec(lineStyle)![1]);
+ edges.push({
+ element,
+ width: strokeWidth,
+ // look up source and target node by matching start and end coordinates
+ // of the edges and the nodes
+ sourceNode: nodes[nodePositionMap[sourcePosition]],
+ targetNode: nodes[nodePositionMap[targetPosition]],
+ });
}
+ }
- await this.startLayout();
+ await this.startLayout();
- return {
- nodes,
- edges,
- };
- }
+ return {
+ nodes,
+ edges,
+ };
+ }
- async createWorkspace() {
- await testSubjects.click('graphCreateGraphPromptButton');
- }
+ async createWorkspace() {
+ await this.testSubjects.click('graphCreateGraphPromptButton');
+ }
- async newGraph() {
- log.debug('Click New Workspace');
- await retry.try(async () => {
- await testSubjects.click('graphNewButton');
- await testSubjects.existOrFail('confirmModal', { timeout: 3000 });
- });
- await PageObjects.common.clickConfirmOnModal();
- await testSubjects.existOrFail('graphGuidancePanel');
- }
+ async newGraph() {
+ this.log.debug('Click New Workspace');
+ await this.retry.try(async () => {
+ await this.testSubjects.click('graphNewButton');
+ await this.testSubjects.existOrFail('confirmModal', { timeout: 3000 });
+ });
+ await this.common.clickConfirmOnModal();
+ await this.testSubjects.existOrFail('graphGuidancePanel');
+ }
- async saveGraph(name: string) {
- await retry.try(async () => {
- await testSubjects.click('graphSaveButton');
- await testSubjects.existOrFail('savedObjectTitle', { timeout: 3000 });
- });
- await testSubjects.setValue('savedObjectTitle', name);
- await testSubjects.click('confirmSaveSavedObjectButton');
+ async saveGraph(name: string) {
+ await this.retry.try(async () => {
+ await this.testSubjects.click('graphSaveButton');
+ await this.testSubjects.existOrFail('savedObjectTitle', { timeout: 3000 });
+ });
+ await this.testSubjects.setValue('savedObjectTitle', name);
+ await this.testSubjects.click('confirmSaveSavedObjectButton');
- // Confirm that the Graph has been saved.
- return await testSubjects.exists('saveGraphSuccess', { timeout: 10000 });
- }
+ // Confirm that the Graph has been saved.
+ return await this.testSubjects.exists('saveGraphSuccess', { timeout: 10000 });
+ }
- async getSearchFilter() {
- const searchFilter = await find.allByCssSelector('main .euiFieldSearch');
- return searchFilter[0];
- }
+ async getSearchFilter() {
+ const searchFilter = await this.find.allByCssSelector('main .euiFieldSearch');
+ return searchFilter[0];
+ }
- async searchForWorkspaceWithName(name: string) {
- await retry.try(async () => {
- const searchFilter = await this.getSearchFilter();
- await searchFilter.clearValue();
- await searchFilter.click();
- await searchFilter.type(name);
- await PageObjects.common.pressEnterKey();
- await find.waitForDeletedByCssSelector('.euiBasicTable-loading', 5000);
- });
-
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ async searchForWorkspaceWithName(name: string) {
+ await this.retry.try(async () => {
+ const searchFilter = await this.getSearchFilter();
+ await searchFilter.clearValue();
+ await searchFilter.click();
+ await searchFilter.type(name);
+ await this.common.pressEnterKey();
+ await this.find.waitForDeletedByCssSelector('.euiBasicTable-loading', 5000);
+ });
+
+ await this.header.waitUntilLoadingHasFinished();
+ }
- async goToListingPage() {
- await retry.try(async () => {
- await testSubjects.click('breadcrumb graphHomeBreadcrumb first');
- await testSubjects.existOrFail('graphLandingPage', { timeout: 3000 });
- });
- }
+ async goToListingPage() {
+ await this.retry.try(async () => {
+ await this.testSubjects.click('breadcrumb graphHomeBreadcrumb first');
+ await this.testSubjects.existOrFail('graphLandingPage', { timeout: 3000 });
+ });
+ }
- async openGraph(name: string) {
- await this.goToListingPage();
- await this.searchForWorkspaceWithName(name);
- await find.clickByLinkText(name);
- // wait for nodes to show up
- if (!(await find.existsByCssSelector('.gphNode', 10000))) {
- throw new Error('nodes did not show up');
- }
- // let force simulation settle down before continuing
- await PageObjects.common.sleep(5000);
+ async openGraph(name: string) {
+ await this.goToListingPage();
+ await this.searchForWorkspaceWithName(name);
+ await this.find.clickByLinkText(name);
+ // wait for nodes to show up
+ if (!(await this.find.existsByCssSelector('.gphNode', 10000))) {
+ throw new Error('nodes did not show up');
}
+ // let force simulation settle down before continuing
+ await this.common.sleep(5000);
+ }
- async deleteGraph(name: string) {
- await testSubjects.click('checkboxSelectAll');
- await this.clickDeleteSelectedWorkspaces();
- await PageObjects.common.clickConfirmOnModal();
- await testSubjects.find('graphCreateGraphPromptButton');
- }
+ async deleteGraph(name: string) {
+ await this.testSubjects.click('checkboxSelectAll');
+ await this.clickDeleteSelectedWorkspaces();
+ await this.common.clickConfirmOnModal();
+ await this.testSubjects.find('graphCreateGraphPromptButton');
+ }
- async getWorkspaceCount() {
- const workspaceTitles = await find.allByCssSelector(
- '[data-test-subj^="graphListingTitleLink"]'
- );
- return workspaceTitles.length;
- }
+ async getWorkspaceCount() {
+ const workspaceTitles = await this.find.allByCssSelector(
+ '[data-test-subj^="graphListingTitleLink"]'
+ );
+ return workspaceTitles.length;
+ }
- async clickDeleteSelectedWorkspaces() {
- await testSubjects.click('deleteSelectedItems');
- }
+ async clickDeleteSelectedWorkspaces() {
+ await this.testSubjects.click('deleteSelectedItems');
+ }
- async getVennTerm1() {
- const el = await find.byCssSelector('span.gphLinkSummary__term--1');
- return await el.getVisibleText();
- }
+ async getVennTerm1() {
+ const el = await this.find.byCssSelector('span.gphLinkSummary__term--1');
+ return await el.getVisibleText();
+ }
- async getVennTerm2() {
- const el = await find.byCssSelector('span.gphLinkSummary__term--2');
- return await el.getVisibleText();
- }
+ async getVennTerm2() {
+ const el = await this.find.byCssSelector('span.gphLinkSummary__term--2');
+ return await el.getVisibleText();
+ }
- async getSmallVennTerm1() {
- const el = await find.byCssSelector('small.gphLinkSummary__term--1');
- return await el.getVisibleText();
- }
+ async getSmallVennTerm1() {
+ const el = await this.find.byCssSelector('small.gphLinkSummary__term--1');
+ return await el.getVisibleText();
+ }
- async getSmallVennTerm12() {
- const el = await find.byCssSelector('small.gphLinkSummary__term--1-2');
- return await el.getVisibleText();
- }
+ async getSmallVennTerm12() {
+ const el = await this.find.byCssSelector('small.gphLinkSummary__term--1-2');
+ return await el.getVisibleText();
+ }
- async getSmallVennTerm2() {
- const el = await find.byCssSelector('small.gphLinkSummary__term--2');
- return await el.getVisibleText();
- }
+ async getSmallVennTerm2() {
+ const el = await this.find.byCssSelector('small.gphLinkSummary__term--2');
+ return await el.getVisibleText();
}
- return new GraphPage();
}
diff --git a/x-pack/test/functional/page_objects/grok_debugger_page.ts b/x-pack/test/functional/page_objects/grok_debugger_page.ts
index dc875588f381e..89017683f632f 100644
--- a/x-pack/test/functional/page_objects/grok_debugger_page.ts
+++ b/x-pack/test/functional/page_objects/grok_debugger_page.ts
@@ -5,16 +5,14 @@
* 2.0.
*/
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
-export function GrokDebuggerPageProvider({ getPageObjects, getService }: FtrProviderContext) {
- const PageObjects = getPageObjects(['common']);
- const grokDebugger = getService('grokDebugger');
+export class GrokDebuggerPageObject extends FtrService {
+ private readonly common = this.ctx.getPageObject('common');
+ private readonly grokDebugger = this.ctx.getService('grokDebugger');
- return new (class LogstashPage {
- async gotoGrokDebugger() {
- await PageObjects.common.navigateToApp('grokDebugger');
- await grokDebugger.assertExists();
- }
- })();
+ async gotoGrokDebugger() {
+ await this.common.navigateToApp('grokDebugger');
+ await this.grokDebugger.assertExists();
+ }
}
diff --git a/x-pack/test/functional/page_objects/index.ts b/x-pack/test/functional/page_objects/index.ts
index e83420a9cea1d..a362fd7e4b7c2 100644
--- a/x-pack/test/functional/page_objects/index.ts
+++ b/x-pack/test/functional/page_objects/index.ts
@@ -8,22 +8,21 @@
import { pageObjects as kibanaFunctionalPageObjects } from '../../../../test/functional/page_objects';
import { CanvasPageProvider } from './canvas_page';
-import { SecurityPageProvider } from './security_page';
-import { MonitoringPageProvider } from './monitoring_page';
-// @ts-ignore not ts yet
-import { LogstashPageProvider } from './logstash_page';
-import { GraphPageProvider } from './graph_page';
-import { GrokDebuggerPageProvider } from './grok_debugger_page';
-import { WatcherPageProvider } from './watcher_page';
-import { ReportingPageProvider } from './reporting_page';
-import { AccountSettingProvider } from './account_settings_page';
+import { SecurityPageObject } from './security_page';
+import { MonitoringPageObject } from './monitoring_page';
+import { LogstashPageObject } from './logstash_page';
+import { GraphPageObject } from './graph_page';
+import { GrokDebuggerPageObject } from './grok_debugger_page';
+import { WatcherPageObject } from './watcher_page';
+import { ReportingPageObject } from './reporting_page';
+import { AccountSettingsPageObject } from './account_settings_page';
import { InfraHomePageProvider } from './infra_home_page';
import { InfraLogsPageProvider } from './infra_logs_page';
-import { GisPageProvider } from './gis_page';
-import { StatusPagePageProvider } from './status_page';
-import { UpgradeAssistantPageProvider } from './upgrade_assistant_page';
-import { RollupPageProvider } from './rollup_page';
-import { UptimePageProvider } from './uptime_page';
+import { GisPageObject } from './gis_page';
+import { StatusPageObject } from './status_page';
+import { UpgradeAssistantPageObject } from './upgrade_assistant_page';
+import { RollupPageObject } from './rollup_page';
+import { UptimePageObject } from './uptime_page';
import { SyntheticsIntegrationPageProvider } from './synthetics_integration_page';
import { ApiKeysPageProvider } from './api_keys_page';
import { LicenseManagementPageProvider } from './license_management_page';
@@ -36,43 +35,43 @@ import { CopySavedObjectsToSpacePageProvider } from './copy_saved_objects_to_spa
import { LensPageProvider } from './lens_page';
import { InfraMetricExplorerProvider } from './infra_metric_explorer';
import { RoleMappingsPageProvider } from './role_mappings_page';
-import { SpaceSelectorPageProvider } from './space_selector_page';
+import { SpaceSelectorPageObject } from './space_selector_page';
import { IngestPipelinesPageProvider } from './ingest_pipelines_page';
-import { TagManagementPageProvider } from './tag_management_page';
-import { NavigationalSearchProvider } from './navigational_search';
+import { TagManagementPageObject } from './tag_management_page';
+import { NavigationalSearchPageObject } from './navigational_search';
import { SearchSessionsPageProvider } from './search_sessions_management_page';
-import { DetectionsPageProvider } from '../../security_solution_ftr/page_objects/detections';
-import { BannersPageProvider } from './banners_page';
+import { DetectionsPageObject } from '../../security_solution_ftr/page_objects/detections';
+import { BannersPageObject } from './banners_page';
// just like services, PageObjects are defined as a map of
// names to Providers. Merge in Kibana's or pick specific ones
export const pageObjects = {
...kibanaFunctionalPageObjects,
canvas: CanvasPageProvider,
- security: SecurityPageProvider,
- accountSetting: AccountSettingProvider,
- monitoring: MonitoringPageProvider,
- logstash: LogstashPageProvider,
- graph: GraphPageProvider,
- grokDebugger: GrokDebuggerPageProvider,
- watcher: WatcherPageProvider,
- reporting: ReportingPageProvider,
- spaceSelector: SpaceSelectorPageProvider,
+ security: SecurityPageObject,
+ accountSetting: AccountSettingsPageObject,
+ monitoring: MonitoringPageObject,
+ logstash: LogstashPageObject,
+ graph: GraphPageObject,
+ grokDebugger: GrokDebuggerPageObject,
+ watcher: WatcherPageObject,
+ reporting: ReportingPageObject,
+ spaceSelector: SpaceSelectorPageObject,
infraHome: InfraHomePageProvider,
infraMetricExplorer: InfraMetricExplorerProvider,
infraLogs: InfraLogsPageProvider,
- maps: GisPageProvider,
- statusPage: StatusPagePageProvider,
- upgradeAssistant: UpgradeAssistantPageProvider,
- uptime: UptimePageProvider,
+ maps: GisPageObject,
+ statusPage: StatusPageObject,
+ upgradeAssistant: UpgradeAssistantPageObject,
+ uptime: UptimePageObject,
syntheticsIntegration: SyntheticsIntegrationPageProvider,
- rollup: RollupPageProvider,
+ rollup: RollupPageObject,
apiKeys: ApiKeysPageProvider,
licenseManagement: LicenseManagementPageProvider,
indexManagement: IndexManagementPageProvider,
searchSessionsManagement: SearchSessionsPageProvider,
indexLifecycleManagement: IndexLifecycleManagementPageProvider,
- tagManagement: TagManagementPageProvider,
+ tagManagement: TagManagementPageObject,
snapshotRestore: SnapshotRestorePageProvider,
crossClusterReplication: CrossClusterReplicationPageProvider,
remoteClusters: RemoteClustersPageProvider,
@@ -80,7 +79,7 @@ export const pageObjects = {
lens: LensPageProvider,
roleMappings: RoleMappingsPageProvider,
ingestPipelines: IngestPipelinesPageProvider,
- navigationalSearch: NavigationalSearchProvider,
- banners: BannersPageProvider,
- detections: DetectionsPageProvider,
+ navigationalSearch: NavigationalSearchPageObject,
+ banners: BannersPageObject,
+ detections: DetectionsPageObject,
};
diff --git a/x-pack/test/functional/page_objects/logstash_page.js b/x-pack/test/functional/page_objects/logstash_page.js
deleted file mode 100644
index 3b12728d29610..0000000000000
--- a/x-pack/test/functional/page_objects/logstash_page.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.
- */
-
-export function LogstashPageProvider({ getPageObjects, getService }) {
- const PageObjects = getPageObjects(['common']);
- const pipelineList = getService('pipelineList');
- const pipelineEditor = getService('pipelineEditor');
-
- return new (class LogstashPage {
- async gotoPipelineList() {
- await PageObjects.common.navigateToApp('logstashPipelines');
- await pipelineList.assertExists();
- }
-
- async gotoNewPipelineEditor() {
- await this.gotoPipelineList();
- await pipelineList.clickAdd();
- await pipelineEditor.assertExists();
- }
- })();
-}
diff --git a/x-pack/test/functional/page_objects/logstash_page.ts b/x-pack/test/functional/page_objects/logstash_page.ts
new file mode 100644
index 0000000000000..37b907b34fa32
--- /dev/null
+++ b/x-pack/test/functional/page_objects/logstash_page.ts
@@ -0,0 +1,25 @@
+/*
+ * 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 { FtrService } from '../ftr_provider_context';
+
+export class LogstashPageObject extends FtrService {
+ private readonly common = this.ctx.getPageObject('common');
+ private readonly pipelineList = this.ctx.getService('pipelineList');
+ private readonly pipelineEditor = this.ctx.getService('pipelineEditor');
+
+ async gotoPipelineList() {
+ await this.common.navigateToApp('logstashPipelines');
+ await this.pipelineList.assertExists();
+ }
+
+ async gotoNewPipelineEditor() {
+ await this.gotoPipelineList();
+ await this.pipelineList.clickAdd();
+ await this.pipelineEditor.assertExists();
+ }
+}
diff --git a/x-pack/test/functional/page_objects/monitoring_page.ts b/x-pack/test/functional/page_objects/monitoring_page.ts
index d32528f44613c..151b6733509e6 100644
--- a/x-pack/test/functional/page_objects/monitoring_page.ts
+++ b/x-pack/test/functional/page_objects/monitoring_page.ts
@@ -5,45 +5,45 @@
* 2.0.
*/
-import { FtrProviderContext } from '../ftr_provider_context';
-
-export function MonitoringPageProvider({ getPageObjects, getService }: FtrProviderContext) {
- const PageObjects = getPageObjects(['common', 'header', 'security', 'login']);
- const testSubjects = getService('testSubjects');
- return new (class MonitoringPage {
- async getAccessDeniedMessage() {
- return testSubjects.getVisibleText('accessDeniedTitle');
- }
-
- async clickBreadcrumb(subj: string) {
- return testSubjects.click(subj);
- }
-
- async assertTableNoData(subj: string) {
- if (!(await testSubjects.exists(subj))) {
- throw new Error('Expected to find the no data message');
- }
- }
+import { FtrService } from '../ftr_provider_context';
- async tableGetRows(subj: string) {
- const table = await testSubjects.find(subj);
- return table.findAllByTagName('tr');
- }
+export class MonitoringPageObject extends FtrService {
+ private readonly common = this.ctx.getPageObject('common');
+ private readonly header = this.ctx.getPageObject('header');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
- async tableGetRowsFromContainer(subj: string) {
- const table = await testSubjects.find(subj);
- const tbody = await table.findByTagName('tbody');
- return tbody.findAllByTagName('tr');
- }
+ async getAccessDeniedMessage() {
+ return this.testSubjects.getVisibleText('accessDeniedTitle');
+ }
- async tableSetFilter(subj: string, text: string) {
- await testSubjects.setValue(subj, text);
- await PageObjects.common.pressEnterKey();
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ async clickBreadcrumb(subj: string) {
+ return this.testSubjects.click(subj);
+ }
- async tableClearFilter(subj: string) {
- return await testSubjects.setValue(subj, ' \uE003'); // space and backspace to trigger onChange event
+ async assertTableNoData(subj: string) {
+ if (!(await this.testSubjects.exists(subj))) {
+ throw new Error('Expected to find the no data message');
}
- })();
+ }
+
+ async tableGetRows(subj: string) {
+ const table = await this.testSubjects.find(subj);
+ return table.findAllByTagName('tr');
+ }
+
+ async tableGetRowsFromContainer(subj: string) {
+ const table = await this.testSubjects.find(subj);
+ const tbody = await table.findByTagName('tbody');
+ return tbody.findAllByTagName('tr');
+ }
+
+ async tableSetFilter(subj: string, text: string) {
+ await this.testSubjects.setValue(subj, text);
+ await this.common.pressEnterKey();
+ await this.header.waitUntilLoadingHasFinished();
+ }
+
+ async tableClearFilter(subj: string) {
+ return await this.testSubjects.setValue(subj, ' \uE003'); // space and backspace to trigger onChange event
+ }
}
diff --git a/x-pack/test/functional/page_objects/navigational_search.ts b/x-pack/test/functional/page_objects/navigational_search.ts
index a3f91e7921cc4..46fed2814c0df 100644
--- a/x-pack/test/functional/page_objects/navigational_search.ts
+++ b/x-pack/test/functional/page_objects/navigational_search.ts
@@ -6,7 +6,7 @@
*/
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
interface SearchResult {
label: string;
@@ -14,88 +14,84 @@ interface SearchResult {
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
-export function NavigationalSearchProvider({ getService, getPageObjects }: FtrProviderContext) {
- const find = getService('find');
- const testSubjects = getService('testSubjects');
+export class NavigationalSearchPageObject extends FtrService {
+ private readonly find = this.ctx.getService('find');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
- class NavigationalSearch {
- async focus() {
- const field = await testSubjects.find('nav-search-input');
- await field.click();
- }
+ async focus() {
+ const field = await this.testSubjects.find('nav-search-input');
+ await field.click();
+ }
- async blur() {
- await testSubjects.click('helpMenuButton');
- await testSubjects.click('helpMenuButton');
- await find.waitForDeletedByCssSelector('.navSearch__panel');
- }
+ async blur() {
+ await this.testSubjects.click('helpMenuButton');
+ await this.testSubjects.click('helpMenuButton');
+ await this.find.waitForDeletedByCssSelector('.navSearch__panel');
+ }
- async searchFor(
- term: string,
- { clear = true, wait = true }: { clear?: boolean; wait?: boolean } = {}
- ) {
- if (clear) {
- await this.clearField();
- }
- const field = await testSubjects.find('nav-search-input');
- await field.type(term);
- if (wait) {
- await this.waitForResultsLoaded();
- }
+ async searchFor(
+ term: string,
+ { clear = true, wait = true }: { clear?: boolean; wait?: boolean } = {}
+ ) {
+ if (clear) {
+ await this.clearField();
}
-
- async getFieldValue() {
- const field = await testSubjects.find('nav-search-input');
- return field.getAttribute('value');
- }
-
- async clearField() {
- const field = await testSubjects.find('nav-search-input');
- await field.clearValueWithKeyboard();
+ const field = await this.testSubjects.find('nav-search-input');
+ await field.type(term);
+ if (wait) {
+ await this.waitForResultsLoaded();
}
+ }
- async isPopoverDisplayed() {
- return await find.existsByCssSelector('.navSearch__panel');
- }
+ async getFieldValue() {
+ const field = await this.testSubjects.find('nav-search-input');
+ return field.getAttribute('value');
+ }
- async clickOnOption(index: number) {
- const options = await testSubjects.findAll('nav-search-option');
- await options[index].click();
- }
+ async clearField() {
+ const field = await this.testSubjects.find('nav-search-input');
+ await field.clearValueWithKeyboard();
+ }
- async waitForResultsLoaded(waitUntil: number = 3000) {
- await testSubjects.exists('nav-search-option');
- // results are emitted in multiple batches. Each individual batch causes a re-render of
- // the component, causing the current elements to become stale. We can't perform DOM access
- // without heavy flakiness in this situation.
- // there is NO ui indication of any kind to detect when all the emissions are done,
- // so we are forced to fallback to awaiting a given amount of time once the first options are displayed.
- await delay(waitUntil);
- }
+ async isPopoverDisplayed() {
+ return await this.find.existsByCssSelector('.navSearch__panel');
+ }
- async getDisplayedResults() {
- const resultElements = await testSubjects.findAll('nav-search-option');
- return Promise.all(resultElements.map((el) => this.convertResultElement(el)));
- }
+ async clickOnOption(index: number) {
+ const options = await this.testSubjects.findAll('nav-search-option');
+ await options[index].click();
+ }
- async isNoResultsPlaceholderDisplayed(checkAfter: number = 3000) {
- // see comment in `waitForResultsLoaded`
- await delay(checkAfter);
- return testSubjects.exists('nav-search-no-results');
- }
+ async waitForResultsLoaded(waitUntil: number = 3000) {
+ await this.testSubjects.exists('nav-search-option');
+ // results are emitted in multiple batches. Each individual batch causes a re-render of
+ // the component, causing the current elements to become stale. We can't perform DOM access
+ // without heavy flakiness in this situation.
+ // there is NO ui indication of any kind to detect when all the emissions are done,
+ // so we are forced to fallback to awaiting a given amount of time once the first options are displayed.
+ await delay(waitUntil);
+ }
- private async convertResultElement(resultEl: WebElementWrapper): Promise {
- const labelEl = await find.allDescendantDisplayedByCssSelector(
- '.euiSelectableTemplateSitewide__listItemTitle',
- resultEl
- );
- const label = await labelEl[0].getVisibleText();
+ async getDisplayedResults() {
+ const resultElements = await this.testSubjects.findAll('nav-search-option');
+ return Promise.all(resultElements.map((el) => this.convertResultElement(el)));
+ }
- return {
- label,
- };
- }
+ async isNoResultsPlaceholderDisplayed(checkAfter: number = 3000) {
+ // see comment in `waitForResultsLoaded`
+ await delay(checkAfter);
+ return this.testSubjects.exists('nav-search-no-results');
}
- return new NavigationalSearch();
+ private async convertResultElement(resultEl: WebElementWrapper): Promise {
+ const labelEl = await this.find.allDescendantDisplayedByCssSelector(
+ '.euiSelectableTemplateSitewide__listItemTitle',
+ resultEl
+ );
+ const label = await labelEl[0].getVisibleText();
+
+ return {
+ label,
+ };
+ }
}
diff --git a/x-pack/test/functional/page_objects/reporting_page.ts b/x-pack/test/functional/page_objects/reporting_page.ts
index 746df14d31ac4..e8999999ce50b 100644
--- a/x-pack/test/functional/page_objects/reporting_page.ts
+++ b/x-pack/test/functional/page_objects/reporting_page.ts
@@ -9,148 +9,152 @@ import expect from '@kbn/expect';
import { format as formatUrl } from 'url';
import supertestAsPromised from 'supertest-as-promised';
-import { FtrProviderContext } from '../ftr_provider_context';
-
-export function ReportingPageProvider({ getService, getPageObjects }: FtrProviderContext) {
- const browser = getService('browser');
- const config = getService('config');
- const log = getService('log');
- const retry = getService('retry');
- const security = getService('security');
- const testSubjects = getService('testSubjects');
- const PageObjects = getPageObjects(['security', 'share', 'timePicker']);
-
- class ReportingPage {
- async forceSharedItemsContainerSize({ width }: { width: number }) {
- await browser.execute(`
- var el = document.querySelector('[data-shared-items-container]');
- el.style.flex="none";
- el.style.width="${width}px";
- `);
- }
+import { FtrService } from '../ftr_provider_context';
+
+export class ReportingPageObject extends FtrService {
+ private readonly browser = this.ctx.getService('browser');
+ private readonly config = this.ctx.getService('config');
+ private readonly log = this.ctx.getService('log');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly security = this.ctx.getService('security');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly share = this.ctx.getPageObject('share');
+ private readonly timePicker = this.ctx.getPageObject('timePicker');
+
+ async forceSharedItemsContainerSize({ width }: { width: number }) {
+ await this.browser.execute(`
+ var el = document.querySelector('[data-shared-items-container]');
+ el.style.flex="none";
+ el.style.width="${width}px";
+ `);
+ }
- async getReportURL(timeout: number) {
- log.debug('getReportURL');
+ async getReportURL(timeout: number) {
+ this.log.debug('getReportURL');
- const url = await testSubjects.getAttribute('downloadCompletedReportButton', 'href', timeout);
+ const url = await this.testSubjects.getAttribute(
+ 'downloadCompletedReportButton',
+ 'href',
+ timeout
+ );
- log.debug(`getReportURL got url: ${url}`);
+ this.log.debug(`getReportURL got url: ${url}`);
- return url;
- }
+ return url;
+ }
- async removeForceSharedItemsContainerSize() {
- await browser.execute(`
- var el = document.querySelector('[data-shared-items-container]');
- el.style.flex = null;
- el.style.width = null;
- `);
- }
+ async removeForceSharedItemsContainerSize() {
+ await this.browser.execute(`
+ var el = document.querySelector('[data-shared-items-container]');
+ el.style.flex = null;
+ el.style.width = null;
+ `);
+ }
- async getResponse(fullUrl: string): Promise {
- log.debug(`getResponse for ${fullUrl}`);
- const kibanaServerConfig = config.get('servers.kibana');
- const baseURL = formatUrl({
- ...kibanaServerConfig,
- auth: false,
- });
- const urlWithoutBase = fullUrl.replace(baseURL, '');
- const res = await security.testUserSupertest.get(urlWithoutBase);
- return res;
- }
+ async getResponse(fullUrl: string): Promise {
+ this.log.debug(`getResponse for ${fullUrl}`);
+ const kibanaServerConfig = this.config.get('servers.kibana');
+ const baseURL = formatUrl({
+ ...kibanaServerConfig,
+ auth: false,
+ });
+ const urlWithoutBase = fullUrl.replace(baseURL, '');
+ const res = await this.security.testUserSupertest.get(urlWithoutBase);
+ return res;
+ }
- async getRawPdfReportData(url: string): Promise {
- log.debug(`getRawPdfReportData for ${url}`);
- const response = await this.getResponse(url);
- expect(response.body).to.be.a(Buffer);
- return response.body as Buffer;
- }
+ async getRawPdfReportData(url: string): Promise {
+ this.log.debug(`getRawPdfReportData for ${url}`);
+ const response = await this.getResponse(url);
+ expect(response.body).to.be.a(Buffer);
+ return response.body as Buffer;
+ }
- async openCsvReportingPanel() {
- log.debug('openCsvReportingPanel');
- await PageObjects.share.openShareMenuItem('CSV Reports');
- }
+ async openCsvReportingPanel() {
+ this.log.debug('openCsvReportingPanel');
+ await this.share.openShareMenuItem('CSV Reports');
+ }
- async openPdfReportingPanel() {
- log.debug('openPdfReportingPanel');
- await PageObjects.share.openShareMenuItem('PDF Reports');
- }
+ async openPdfReportingPanel() {
+ this.log.debug('openPdfReportingPanel');
+ await this.share.openShareMenuItem('PDF Reports');
+ }
- async openPngReportingPanel() {
- log.debug('openPngReportingPanel');
- await PageObjects.share.openShareMenuItem('PNG Reports');
- }
+ async openPngReportingPanel() {
+ this.log.debug('openPngReportingPanel');
+ await this.share.openShareMenuItem('PNG Reports');
+ }
- async clearToastNotifications() {
- const toasts = await testSubjects.findAll('toastCloseButton');
- await Promise.all(toasts.map(async (t) => await t.click()));
- }
+ async clearToastNotifications() {
+ const toasts = await this.testSubjects.findAll('toastCloseButton');
+ await Promise.all(toasts.map(async (t) => await t.click()));
+ }
- async getQueueReportError() {
- return await testSubjects.exists('queueReportError');
- }
+ async getQueueReportError() {
+ return await this.testSubjects.exists('queueReportError');
+ }
- async getGenerateReportButton() {
- return await retry.try(async () => await testSubjects.find('generateReportButton'));
- }
+ async getGenerateReportButton() {
+ return await this.retry.try(async () => await this.testSubjects.find('generateReportButton'));
+ }
- async isGenerateReportButtonDisabled() {
- const generateReportButton = await this.getGenerateReportButton();
- return await retry.try(async () => {
- const isDisabled = await generateReportButton.getAttribute('disabled');
- return isDisabled;
- });
- }
+ async isGenerateReportButtonDisabled() {
+ const generateReportButton = await this.getGenerateReportButton();
+ return await this.retry.try(async () => {
+ const isDisabled = await generateReportButton.getAttribute('disabled');
+ return isDisabled;
+ });
+ }
- async canReportBeCreated() {
- await this.clickGenerateReportButton();
- const success = await this.checkForReportingToasts();
- return success;
- }
+ async canReportBeCreated() {
+ await this.clickGenerateReportButton();
+ const success = await this.checkForReportingToasts();
+ return success;
+ }
- async checkUsePrintLayout() {
- // The print layout checkbox slides in as part of an animation, and tests can
- // attempt to click it too quickly, leading to flaky tests. The 500ms wait allows
- // the animation to complete before we attempt a click.
- const menuAnimationDelay = 500;
- await retry.tryForTime(menuAnimationDelay, () => testSubjects.click('usePrintLayout'));
- }
+ async checkUsePrintLayout() {
+ // The print layout checkbox slides in as part of an animation, and tests can
+ // attempt to click it too quickly, leading to flaky tests. The 500ms wait allows
+ // the animation to complete before we attempt a click.
+ const menuAnimationDelay = 500;
+ await this.retry.tryForTime(menuAnimationDelay, () =>
+ this.testSubjects.click('usePrintLayout')
+ );
+ }
- async clickGenerateReportButton() {
- await testSubjects.click('generateReportButton');
- }
+ async clickGenerateReportButton() {
+ await this.testSubjects.click('generateReportButton');
+ }
- async toggleReportMode() {
- await testSubjects.click('reportModeToggle');
- }
+ async toggleReportMode() {
+ await this.testSubjects.click('reportModeToggle');
+ }
- async checkForReportingToasts() {
- log.debug('Reporting:checkForReportingToasts');
- const isToastPresent = await testSubjects.exists('completeReportSuccess', {
- allowHidden: true,
- timeout: 90000,
- });
- // Close toast so it doesn't obscure the UI.
- if (isToastPresent) {
- await testSubjects.click('completeReportSuccess > toastCloseButton');
- }
-
- return isToastPresent;
+ async checkForReportingToasts() {
+ this.log.debug('Reporting:checkForReportingToasts');
+ const isToastPresent = await this.testSubjects.exists('completeReportSuccess', {
+ allowHidden: true,
+ timeout: 90000,
+ });
+ // Close toast so it doesn't obscure the UI.
+ if (isToastPresent) {
+ await this.testSubjects.click('completeReportSuccess > toastCloseButton');
}
- async setTimepickerInDataRange() {
- log.debug('Reporting:setTimepickerInDataRange');
- const fromTime = 'Apr 27, 2019 @ 23:56:51.374';
- const toTime = 'Aug 23, 2019 @ 16:18:51.821';
- await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
- }
+ return isToastPresent;
+ }
- async setTimepickerInNoDataRange() {
- log.debug('Reporting:setTimepickerInNoDataRange');
- const fromTime = 'Sep 19, 1999 @ 06:31:44.000';
- const toTime = 'Sep 23, 1999 @ 18:31:44.000';
- await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
- }
+ async setTimepickerInDataRange() {
+ this.log.debug('Reporting:setTimepickerInDataRange');
+ const fromTime = 'Apr 27, 2019 @ 23:56:51.374';
+ const toTime = 'Aug 23, 2019 @ 16:18:51.821';
+ await this.timePicker.setAbsoluteRange(fromTime, toTime);
+ }
+
+ async setTimepickerInNoDataRange() {
+ this.log.debug('Reporting:setTimepickerInNoDataRange');
+ const fromTime = 'Sep 19, 1999 @ 06:31:44.000';
+ const toTime = 'Sep 23, 1999 @ 18:31:44.000';
+ await this.timePicker.setAbsoluteRange(fromTime, toTime);
}
- return new ReportingPage();
}
diff --git a/x-pack/test/functional/page_objects/rollup_page.ts b/x-pack/test/functional/page_objects/rollup_page.ts
index dbdfa4e19c555..0740a8f015da1 100644
--- a/x-pack/test/functional/page_objects/rollup_page.ts
+++ b/x-pack/test/functional/page_objects/rollup_page.ts
@@ -7,137 +7,130 @@
import expect from '@kbn/expect';
import { map as mapAsync } from 'bluebird';
-import { FtrProviderContext } from '../ftr_provider_context';
-
-export function RollupPageProvider({ getService, getPageObjects }: FtrProviderContext) {
- const testSubjects = getService('testSubjects');
- const log = getService('log');
- const find = getService('find');
- const PageObjects = getPageObjects(['header', 'common']);
-
- class RollupJobPage {
- async createNewRollUpJob(
- jobName: string,
- indexPattern: string,
- indexName: string,
- interval: string,
- delay = '1d',
- startImmediately = false,
- scheduledTime = { time: 'minute', cron: true }
- ) {
- let stepNum = 1;
- // Step 1
- await testSubjects.click('createRollupJobButton');
- await this.verifyStepIsActive(stepNum);
- await this.addRollupNameandIndexPattern(jobName, indexPattern);
- await this.verifyIndexPatternAccepted();
- await this.setIndexName(indexName);
- await this.setScheduleTime(scheduledTime.time, scheduledTime.cron);
- await this.setRollupDelay(delay);
- stepNum = await this.moveToNextStep(stepNum);
-
- // Step 2: Histogram
- await this.verifyStepIsActive(stepNum);
- await this.setJobInterval(interval);
- stepNum = await this.moveToNextStep(stepNum);
-
- // Step 3: Terms (optional)
- await this.verifyStepIsActive(stepNum);
- stepNum = await this.moveToNextStep();
-
- // Step 4: Histogram(optional)
- await this.verifyStepIsActive(stepNum);
- stepNum = await this.moveToNextStep();
-
- // Step 5: Metrics(optional)
- await this.verifyStepIsActive(stepNum);
- stepNum = await this.moveToNextStep();
-
- // Step 6: saveJob and verify the name in the list
- await this.verifyStepIsActive(stepNum);
- await this.saveJob(startImmediately);
- }
+import { FtrService } from '../ftr_provider_context';
+
+export class RollupPageObject extends FtrService {
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly log = this.ctx.getService('log');
+ private readonly find = this.ctx.getService('find');
+ private readonly header = this.ctx.getPageObject('header');
+
+ async createNewRollUpJob(
+ jobName: string,
+ indexPattern: string,
+ indexName: string,
+ interval: string,
+ delay = '1d',
+ startImmediately = false,
+ scheduledTime = { time: 'minute', cron: true }
+ ) {
+ let stepNum = 1;
+ // Step 1
+ await this.testSubjects.click('createRollupJobButton');
+ await this.verifyStepIsActive(stepNum);
+ await this.addRollupNameandIndexPattern(jobName, indexPattern);
+ await this.verifyIndexPatternAccepted();
+ await this.setIndexName(indexName);
+ await this.setScheduleTime(scheduledTime.time, scheduledTime.cron);
+ await this.setRollupDelay(delay);
+ stepNum = await this.moveToNextStep(stepNum);
+
+ // Step 2: Histogram
+ await this.verifyStepIsActive(stepNum);
+ await this.setJobInterval(interval);
+ stepNum = await this.moveToNextStep(stepNum);
+
+ // Step 3: Terms (optional)
+ await this.verifyStepIsActive(stepNum);
+ stepNum = await this.moveToNextStep();
+
+ // Step 4: Histogram(optional)
+ await this.verifyStepIsActive(stepNum);
+ stepNum = await this.moveToNextStep();
+
+ // Step 5: Metrics(optional)
+ await this.verifyStepIsActive(stepNum);
+ stepNum = await this.moveToNextStep();
+
+ // Step 6: saveJob and verify the name in the list
+ await this.verifyStepIsActive(stepNum);
+ await this.saveJob(startImmediately);
+ }
- async verifyStepIsActive(stepNumber = 0) {
- await testSubjects.exists(`createRollupStep${stepNumber}--active`);
- }
+ async verifyStepIsActive(stepNumber = 0) {
+ await this.testSubjects.exists(`createRollupStep${stepNumber}--active`);
+ }
- async setScheduleTime(time: string, isCron: boolean) {
- if (isCron) {
- await testSubjects.click('rollupShowAdvancedCronLink');
- await testSubjects.setValue('rollupAdvancedCron', time);
- }
- // TODO: Add handling for if Cron is false to go through clicking options.
+ async setScheduleTime(time: string, isCron: boolean) {
+ if (isCron) {
+ await this.testSubjects.click('rollupShowAdvancedCronLink');
+ await this.testSubjects.setValue('rollupAdvancedCron', time);
}
+ // TODO: Add handling for if Cron is false to go through clicking options.
+ }
- async addRollupNameandIndexPattern(name: string, indexPattern: string) {
- log.debug(`Adding name ${name} to form`);
- await testSubjects.setValue('rollupJobName', name);
- await testSubjects.setValue('rollupIndexPattern', indexPattern);
- }
+ async addRollupNameandIndexPattern(name: string, indexPattern: string) {
+ this.log.debug(`Adding name ${name} to form`);
+ await this.testSubjects.setValue('rollupJobName', name);
+ await this.testSubjects.setValue('rollupIndexPattern', indexPattern);
+ }
- async setRollupDelay(time: string) {
- log.debug(`Setting rollup delay to "${time}"`);
- await testSubjects.setValue('rollupDelay', time);
- }
+ async setRollupDelay(time: string) {
+ this.log.debug(`Setting rollup delay to "${time}"`);
+ await this.testSubjects.setValue('rollupDelay', time);
+ }
- async verifyIndexPatternAccepted() {
- const span = await testSubjects.find('fieldIndexPatternSuccessMessage');
- const message = await span.findByCssSelector('p');
- const text = await message.getVisibleText();
- expect(text).to.be.equal('Success! Index pattern has matching indices.');
- }
+ async verifyIndexPatternAccepted() {
+ const span = await this.testSubjects.find('fieldIndexPatternSuccessMessage');
+ const message = await span.findByCssSelector('p');
+ const text = await message.getVisibleText();
+ expect(text).to.be.equal('Success! Index pattern has matching indices.');
+ }
- async setIndexName(name: string) {
- await testSubjects.setValue('rollupIndexName', name);
- }
+ async setIndexName(name: string) {
+ await this.testSubjects.setValue('rollupIndexName', name);
+ }
- async moveToNextStep(stepNum = 0) {
- await testSubjects.click('rollupJobNextButton');
- return stepNum + 1;
- }
+ async moveToNextStep(stepNum = 0) {
+ await this.testSubjects.click('rollupJobNextButton');
+ return stepNum + 1;
+ }
- async setJobInterval(time: string) {
- await testSubjects.setValue('rollupJobInterval', time);
- }
+ async setJobInterval(time: string) {
+ await this.testSubjects.setValue('rollupJobInterval', time);
+ }
- async saveJob(startImmediately: boolean) {
- if (startImmediately) {
- const checkbox = await find.byCssSelector('.euiCheckbox');
- await checkbox.click();
- }
- await testSubjects.click('rollupJobSaveButton');
- await PageObjects.header.waitUntilLoadingHasFinished();
+ async saveJob(startImmediately: boolean) {
+ if (startImmediately) {
+ const checkbox = await this.find.byCssSelector('.euiCheckbox');
+ await checkbox.click();
}
+ await this.testSubjects.click('rollupJobSaveButton');
+ await this.header.waitUntilLoadingHasFinished();
+ }
- async getJobList() {
- const jobs = await testSubjects.findAll('jobTableRow');
- return mapAsync(jobs, async (job) => {
- const jobNameElement = await job.findByTestSubject('jobTableCell-id');
- const jobStatusElement = await job.findByTestSubject('jobTableCell-status');
- const jobIndexPatternElement = await job.findByTestSubject('jobTableCell-indexPattern');
- const jobRollUpIndexPatternElement = await job.findByTestSubject(
- 'jobTableCell-rollupIndex'
- );
- const jobDelayElement = await job.findByTestSubject('jobTableCell-rollupDelay');
- const jobIntervalElement = await job.findByTestSubject(
- 'jobTableCell-dateHistogramInterval'
- );
- const jobGroupElement = await job.findByTestSubject('jobTableCell-groups');
- const jobMetricsElement = await job.findByTestSubject('jobTableCell-metrics');
-
- return {
- jobName: await jobNameElement.getVisibleText(),
- jobStatus: await jobStatusElement.getVisibleText(),
- jobIndexPattern: await jobIndexPatternElement.getVisibleText(),
- jobRollUpIndexPattern: await jobRollUpIndexPatternElement.getVisibleText(),
- jobDelayElement: await jobDelayElement.getVisibleText(),
- jobInterval: await jobIntervalElement.getVisibleText(),
- jobGroup: await jobGroupElement.getVisibleText(),
- jobMetrics: await jobMetricsElement.getVisibleText(),
- };
- });
- }
+ async getJobList() {
+ const jobs = await this.testSubjects.findAll('jobTableRow');
+ return mapAsync(jobs, async (job) => {
+ const jobNameElement = await job.findByTestSubject('jobTableCell-id');
+ const jobStatusElement = await job.findByTestSubject('jobTableCell-status');
+ const jobIndexPatternElement = await job.findByTestSubject('jobTableCell-indexPattern');
+ const jobRollUpIndexPatternElement = await job.findByTestSubject('jobTableCell-rollupIndex');
+ const jobDelayElement = await job.findByTestSubject('jobTableCell-rollupDelay');
+ const jobIntervalElement = await job.findByTestSubject('jobTableCell-dateHistogramInterval');
+ const jobGroupElement = await job.findByTestSubject('jobTableCell-groups');
+ const jobMetricsElement = await job.findByTestSubject('jobTableCell-metrics');
+
+ return {
+ jobName: await jobNameElement.getVisibleText(),
+ jobStatus: await jobStatusElement.getVisibleText(),
+ jobIndexPattern: await jobIndexPatternElement.getVisibleText(),
+ jobRollUpIndexPattern: await jobRollUpIndexPatternElement.getVisibleText(),
+ jobDelayElement: await jobDelayElement.getVisibleText(),
+ jobInterval: await jobIntervalElement.getVisibleText(),
+ jobGroup: await jobGroupElement.getVisibleText(),
+ jobMetrics: await jobMetricsElement.getVisibleText(),
+ };
+ });
}
- return new RollupJobPage();
}
diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts
index 2ce14fa7a2515..f9128ce21b565 100644
--- a/x-pack/test/functional/page_objects/security_page.ts
+++ b/x-pack/test/functional/page_objects/security_page.ts
@@ -6,130 +6,49 @@
*/
import { adminTestUser } from '@kbn/test';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
import { AuthenticatedUser, Role } from '../../../plugins/security/common/model';
import type { UserFormValues } from '../../../plugins/security/public/management/users/edit_user/user_form';
-export function SecurityPageProvider({ getService, getPageObjects }: FtrProviderContext) {
- const browser = getService('browser');
- const config = getService('config');
- const retry = getService('retry');
- const find = getService('find');
- const log = getService('log');
- const testSubjects = getService('testSubjects');
- const esArchiver = getService('esArchiver');
- const userMenu = getService('userMenu');
- const comboBox = getService('comboBox');
- const supertest = getService('supertestWithoutAuth');
- const deployment = getService('deployment');
- const PageObjects = getPageObjects(['common', 'header', 'error']);
-
- interface LoginOptions {
- expectSpaceSelector?: boolean;
- expectSuccess?: boolean;
- expectForbidden?: boolean;
- }
-
- type LoginExpectedResult = 'spaceSelector' | 'error' | 'chrome';
-
- async function waitForLoginPage() {
- log.debug('Waiting for Login Page to appear.');
- await retry.waitForWithTimeout('login page', config.get('timeouts.waitFor') * 5, async () => {
- // As a part of the cleanup flow tests usually try to log users out, but there are cases when
- // browser/Kibana would like users to confirm that they want to navigate away from the current
- // page and lose the state (e.g. unsaved changes) via native alert dialog.
- const alert = await browser.getAlert();
- if (alert && alert.accept) {
- await alert.accept();
- }
- return await find.existsByDisplayedByCssSelector('.login-form');
- });
- }
-
- async function isLoginFormVisible() {
- return await testSubjects.exists('loginForm');
- }
-
- async function waitForLoginForm() {
- log.debug('Waiting for Login Form to appear.');
- await retry.waitForWithTimeout('login form', config.get('timeouts.waitFor') * 5, async () => {
- return await isLoginFormVisible();
- });
- }
-
- async function waitForLoginSelector() {
- log.debug('Waiting for Login Selector to appear.');
- await retry.waitForWithTimeout(
- 'login selector',
- config.get('timeouts.waitFor') * 5,
- async () => {
- return await testSubjects.exists('loginSelector');
- }
- );
- }
-
- async function waitForLoginHelp(helpText: string) {
- log.debug(`Waiting for Login Help to appear with text: ${helpText}.`);
- await retry.waitForWithTimeout('login help', config.get('timeouts.waitFor') * 5, async () => {
- return (await testSubjects.getVisibleText('loginHelp')) === helpText;
- });
- }
-
- async function waitForLoginResult(expectedResult?: LoginExpectedResult) {
- log.debug(`Waiting for login result, expected: ${expectedResult}.`);
-
- // wait for either space selector, kibanaChrome or loginErrorMessage
- if (expectedResult === 'spaceSelector') {
- await retry.try(() => testSubjects.find('kibanaSpaceSelector'));
- log.debug(
- `Finished login process, landed on space selector. currentUrl = ${await browser.getCurrentUrl()}`
- );
- return;
- }
-
- if (expectedResult === 'error') {
- const rawDataTabLocator = 'a[id=rawdata-tab]';
- if (await find.existsByCssSelector(rawDataTabLocator)) {
- // Firefox has 3 tabs and requires navigation to see Raw output
- await find.clickByCssSelector(rawDataTabLocator);
- }
- await retry.try(async () => {
- if (await find.existsByCssSelector(rawDataTabLocator)) {
- await find.clickByCssSelector(rawDataTabLocator);
- }
- await testSubjects.existOrFail('ResetSessionButton');
- });
- log.debug(
- `Finished login process, found reset session button message. currentUrl = ${await browser.getCurrentUrl()}`
- );
- return;
- }
-
- if (expectedResult === 'chrome') {
- await find.byCssSelector(
- '[data-test-subj="kibanaChrome"] .kbnAppWrapper:not(.kbnAppWrapper--hiddenChrome)',
- 20000
- );
- log.debug(`Finished login process currentUrl = ${await browser.getCurrentUrl()}`);
- }
- }
+interface LoginOptions {
+ expectSpaceSelector?: boolean;
+ expectSuccess?: boolean;
+ expectForbidden?: boolean;
+}
- const loginPage = Object.freeze({
- async login(username?: string, password?: string, options: LoginOptions = {}) {
- if (!(await isLoginFormVisible())) {
- await PageObjects.common.navigateToApp('login');
+type LoginExpectedResult = 'spaceSelector' | 'error' | 'chrome';
+
+export class SecurityPageObject extends FtrService {
+ private readonly browser = this.ctx.getService('browser');
+ private readonly config = this.ctx.getService('config');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly find = this.ctx.getService('find');
+ private readonly log = this.ctx.getService('log');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly esArchiver = this.ctx.getService('esArchiver');
+ private readonly userMenu = this.ctx.getService('userMenu');
+ private readonly comboBox = this.ctx.getService('comboBox');
+ private readonly supertest = this.ctx.getService('supertestWithoutAuth');
+ private readonly deployment = this.ctx.getService('deployment');
+ private readonly common = this.ctx.getPageObject('common');
+ private readonly header = this.ctx.getPageObject('header');
+
+ public loginPage = Object.freeze({
+ login: async (username?: string, password?: string, options: LoginOptions = {}) => {
+ if (!(await this.isLoginFormVisible())) {
+ await this.common.navigateToApp('login');
}
// ensure welcome screen won't be shown. This is relevant for environments which don't allow
// to use the yml setting, e.g. cloud
- await browser.setLocalStorageItem('home:welcome:show', 'false');
- await waitForLoginForm();
+ await this.browser.setLocalStorageItem('home:welcome:show', 'false');
+ await this.waitForLoginForm();
- await testSubjects.setValue('loginUsername', username || adminTestUser.username);
- await testSubjects.setValue('loginPassword', password || adminTestUser.password);
- await testSubjects.click('loginSubmit');
+ await this.testSubjects.setValue('loginUsername', username || adminTestUser.username);
+ await this.testSubjects.setValue('loginPassword', password || adminTestUser.password);
+ await this.testSubjects.click('loginSubmit');
- await waitForLoginResult(
+ await this.waitForLoginResult(
options.expectSpaceSelector
? 'spaceSelector'
: options.expectForbidden
@@ -140,9 +59,11 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider
);
},
- async getErrorMessage() {
- return await retry.try(async () => {
- const errorMessageContainer = await retry.try(() => testSubjects.find('loginErrorMessage'));
+ getErrorMessage: async () => {
+ return await this.retry.try(async () => {
+ const errorMessageContainer = await this.retry.try(() =>
+ this.testSubjects.find('loginErrorMessage')
+ );
const errorMessageText = await errorMessageContainer.getVisibleText();
if (!errorMessageText) {
@@ -154,380 +75,477 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider
},
});
- const loginSelector = Object.freeze({
- async login(providerType: string, providerName: string, options?: Record) {
- log.debug(`Starting login flow for ${providerType}/${providerName}`);
+ public loginSelector = Object.freeze({
+ login: async (providerType: string, providerName: string, options?: Record) => {
+ this.log.debug(`Starting login flow for ${providerType}/${providerName}`);
- await this.verifyLoginSelectorIsVisible();
- await this.selectLoginMethod(providerType, providerName);
+ await this.loginSelector.verifyLoginSelectorIsVisible();
+ await this.loginSelector.selectLoginMethod(providerType, providerName);
if (providerType === 'basic' || providerType === 'token') {
- await waitForLoginForm();
+ await this.waitForLoginForm();
- await testSubjects.setValue('loginUsername', options?.username ?? adminTestUser.username);
- await testSubjects.setValue('loginPassword', options?.password ?? adminTestUser.password);
- await testSubjects.click('loginSubmit');
+ await this.testSubjects.setValue(
+ 'loginUsername',
+ options?.username ?? adminTestUser.username
+ );
+ await this.testSubjects.setValue(
+ 'loginPassword',
+ options?.password ?? adminTestUser.password
+ );
+ await this.testSubjects.click('loginSubmit');
}
- await waitForLoginResult('chrome');
+ await this.waitForLoginResult('chrome');
- log.debug(`Finished login process currentUrl = ${await browser.getCurrentUrl()}`);
+ this.log.debug(`Finished login process currentUrl = ${await this.browser.getCurrentUrl()}`);
},
- async selectLoginMethod(providerType: string, providerName: string) {
+ selectLoginMethod: async (providerType: string, providerName: string) => {
// Ensure welcome screen won't be shown. This is relevant for environments which don't allow
// to use the yml setting, e.g. cloud.
- await browser.setLocalStorageItem('home:welcome:show', 'false');
- await testSubjects.click(`loginCard-${providerType}/${providerName}`);
+ await this.browser.setLocalStorageItem('home:welcome:show', 'false');
+ await this.testSubjects.click(`loginCard-${providerType}/${providerName}`);
},
- async verifyLoginFormIsVisible() {
- await waitForLoginForm();
+ verifyLoginFormIsVisible: async () => {
+ await this.waitForLoginForm();
},
- async verifyLoginSelectorIsVisible() {
- await waitForLoginSelector();
+ verifyLoginSelectorIsVisible: async () => {
+ await this.waitForLoginSelector();
},
- async verifyLoginHelpIsVisible(helpText: string) {
- await waitForLoginHelp(helpText);
+ verifyLoginHelpIsVisible: async (helpText: string) => {
+ await this.waitForLoginHelp(helpText);
},
});
- class SecurityPage {
- public loginPage = loginPage;
- public loginSelector = loginSelector;
-
- async initTests() {
- log.debug('SecurityPage:initTests');
- await esArchiver.load('empty_kibana');
- await esArchiver.loadIfNeeded('logstash_functional');
- await browser.setWindowSize(1600, 1000);
- }
+ private async waitForLoginPage() {
+ this.log.debug('Waiting for Login Page to appear.');
+ await this.retry.waitForWithTimeout(
+ 'login page',
+ this.config.get('timeouts.waitFor') * 5,
+ async () => {
+ // As a part of the cleanup flow tests usually try to log users out, but there are cases when
+ // browser/Kibana would like users to confirm that they want to navigate away from the current
+ // page and lose the state (e.g. unsaved changes) via native alert dialog.
+ const alert = await this.browser.getAlert();
+ if (alert && alert.accept) {
+ await alert.accept();
+ }
+ return await this.find.existsByDisplayedByCssSelector('.login-form');
+ }
+ );
+ }
- async login(username?: string, password?: string, options: LoginOptions = {}) {
- await this.loginPage.login(username, password, options);
+ private async isLoginFormVisible() {
+ return await this.testSubjects.exists('loginForm');
+ }
- if (options.expectSpaceSelector || options.expectForbidden) {
- return;
+ private async waitForLoginForm() {
+ this.log.debug('Waiting for Login Form to appear.');
+ await this.retry.waitForWithTimeout(
+ 'login form',
+ this.config.get('timeouts.waitFor') * 5,
+ async () => {
+ return await this.isLoginFormVisible();
}
+ );
+ }
- await retry.waitFor('logout button visible', async () => await userMenu.logoutLinkExists());
- }
-
- async logout() {
- log.debug('SecurityPage.logout');
+ private async waitForLoginSelector() {
+ this.log.debug('Waiting for Login Selector to appear.');
+ await this.retry.waitForWithTimeout(
+ 'login selector',
+ this.config.get('timeouts.waitFor') * 5,
+ async () => {
+ return await this.testSubjects.exists('loginSelector');
+ }
+ );
+ }
- if (!(await userMenu.logoutLinkExists())) {
- log.debug('Logout not found');
- return;
+ private async waitForLoginHelp(helpText: string) {
+ this.log.debug(`Waiting for Login Help to appear with text: ${helpText}.`);
+ await this.retry.waitForWithTimeout(
+ 'login help',
+ this.config.get('timeouts.waitFor') * 5,
+ async () => {
+ return (await this.testSubjects.getVisibleText('loginHelp')) === helpText;
}
+ );
+ }
+
+ private async waitForLoginResult(expectedResult?: LoginExpectedResult) {
+ this.log.debug(`Waiting for login result, expected: ${expectedResult}.`);
- await userMenu.clickLogoutButton();
- await waitForLoginPage();
+ // wait for either space selector, kibanaChrome or loginErrorMessage
+ if (expectedResult === 'spaceSelector') {
+ await this.retry.try(() => this.testSubjects.find('kibanaSpaceSelector'));
+ this.log.debug(
+ `Finished login process, landed on space selector. currentUrl = ${await this.browser.getCurrentUrl()}`
+ );
+ return;
}
- async getCurrentUser() {
- const sidCookie = await browser.getCookie('sid');
- if (!sidCookie?.value) {
- log.debug('User is not authenticated yet.');
- return null;
+ if (expectedResult === 'error') {
+ const rawDataTabLocator = 'a[id=rawdata-tab]';
+ if (await this.find.existsByCssSelector(rawDataTabLocator)) {
+ // Firefox has 3 tabs and requires navigation to see Raw output
+ await this.find.clickByCssSelector(rawDataTabLocator);
}
+ await this.retry.try(async () => {
+ if (await this.find.existsByCssSelector(rawDataTabLocator)) {
+ await this.find.clickByCssSelector(rawDataTabLocator);
+ }
+ await this.testSubjects.existOrFail('ResetSessionButton');
+ });
+ this.log.debug(
+ `Finished login process, found reset session button message. currentUrl = ${await this.browser.getCurrentUrl()}`
+ );
+ return;
+ }
- const { body: user } = await supertest
- .get('/internal/security/me')
- .set('kbn-xsrf', 'xxx')
- .set('Cookie', `sid=${sidCookie.value}`)
- .expect(200);
- return user as AuthenticatedUser;
+ if (expectedResult === 'chrome') {
+ await this.find.byCssSelector(
+ '[data-test-subj="kibanaChrome"] .kbnAppWrapper:not(.kbnAppWrapper--hiddenChrome)',
+ 20000
+ );
+ this.log.debug(`Finished login process currentUrl = ${await this.browser.getCurrentUrl()}`);
}
+ }
- async forceLogout() {
- log.debug('SecurityPage.forceLogout');
- if (await find.existsByDisplayedByCssSelector('.login-form', 100)) {
- log.debug('Already on the login page, not forcing anything');
- return;
- }
+ async initTests() {
+ this.log.debug('SecurityPage:initTests');
+ await this.esArchiver.load('empty_kibana');
+ await this.esArchiver.loadIfNeeded('logstash_functional');
+ await this.browser.setWindowSize(1600, 1000);
+ }
- log.debug('Redirecting to /logout to force the logout');
- const url = deployment.getHostPort() + '/logout';
- await browser.get(url);
- log.debug('Waiting on the login form to appear');
- await waitForLoginPage();
- }
+ async login(username?: string, password?: string, options: LoginOptions = {}) {
+ await this.loginPage.login(username, password, options);
- async clickRolesSection() {
- await testSubjects.click('roles');
+ if (options.expectSpaceSelector || options.expectForbidden) {
+ return;
}
- async clickUsersSection() {
- await testSubjects.click('users');
- }
+ await this.retry.waitFor(
+ 'logout button visible',
+ async () => await this.userMenu.logoutLinkExists()
+ );
+ }
- async clickCreateNewUser() {
- await retry.try(() => testSubjects.click('createUserButton'));
- }
+ async logout() {
+ this.log.debug('SecurityPage.logout');
- async clickCreateNewRole() {
- await retry.try(() => testSubjects.click('createRoleButton'));
+ if (!(await this.userMenu.logoutLinkExists())) {
+ this.log.debug('Logout not found');
+ return;
}
- async clickCloneRole(roleName: string) {
- await retry.try(() => testSubjects.click(`clone-role-action-${roleName}`));
- }
+ await this.userMenu.clickLogoutButton();
+ await this.waitForLoginPage();
+ }
- async clickCancelEditUser() {
- await find.clickByButtonText('Cancel');
+ async getCurrentUser() {
+ const sidCookie = await this.browser.getCookie('sid');
+ if (!sidCookie?.value) {
+ this.log.debug('User is not authenticated yet.');
+ return null;
}
- async clickCancelEditRole() {
- await testSubjects.click('roleFormCancelButton');
- }
+ const { body: user } = await this.supertest
+ .get('/internal/security/me')
+ .set('kbn-xsrf', 'xxx')
+ .set('Cookie', `sid=${sidCookie.value}`)
+ .expect(200);
+ return user as AuthenticatedUser;
+ }
- async clickSaveEditUser() {
- await find.clickByButtonText('Update user');
- await PageObjects.header.waitUntilLoadingHasFinished();
+ async forceLogout() {
+ this.log.debug('SecurityPage.forceLogout');
+ if (await this.find.existsByDisplayedByCssSelector('.login-form', 100)) {
+ this.log.debug('Already on the login page, not forcing anything');
+ return;
}
- async clickSaveCreateUser() {
- await find.clickByButtonText('Create user');
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ this.log.debug('Redirecting to /logout to force the logout');
+ const url = this.deployment.getHostPort() + '/logout';
+ await this.browser.get(url);
+ this.log.debug('Waiting on the login form to appear');
+ await this.waitForLoginPage();
+ }
- async clickSaveEditRole() {
- const saveButton = await retry.try(() => testSubjects.find('roleFormSaveButton'));
- await saveButton.moveMouseTo();
- await saveButton.click();
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ async clickRolesSection() {
+ await this.testSubjects.click('roles');
+ }
- async addIndexToRole(index: string) {
- log.debug(`Adding index ${index} to role`);
- await comboBox.setCustom('indicesInput0', index);
- }
+ async clickUsersSection() {
+ await this.testSubjects.click('users');
+ }
- async addPrivilegeToRole(privilege: string) {
- log.debug(`Adding privilege ${privilege} to role`);
- const privilegeInput = await retry.try(() =>
- find.byCssSelector('[data-test-subj="privilegesInput0"] input')
- );
- await privilegeInput.type(privilege);
+ async clickCreateNewUser() {
+ await this.retry.try(() => this.testSubjects.click('createUserButton'));
+ }
- const btn = await find.byButtonText(privilege);
- await btn.click();
+ async clickCreateNewRole() {
+ await this.retry.try(() => this.testSubjects.click('createRoleButton'));
+ }
- // const options = await find.byCssSelector(`.euiFilterSelectItem`);
- // Object.entries(options).forEach(([key, prop]) => {
- // console.log({ key, proto: prop.__proto__ });
- // });
+ async clickCloneRole(roleName: string) {
+ await this.retry.try(() => this.testSubjects.click(`clone-role-action-${roleName}`));
+ }
- // await options.click();
- }
+ async clickCancelEditUser() {
+ await this.find.clickByButtonText('Cancel');
+ }
- async assignRoleToUser(role: string) {
- await this.selectRole(role);
- }
+ async clickCancelEditRole() {
+ await this.testSubjects.click('roleFormCancelButton');
+ }
- async navigateTo() {
- await PageObjects.common.navigateToApp('settings');
- }
+ async clickSaveEditUser() {
+ await this.find.clickByButtonText('Update user');
+ await this.header.waitUntilLoadingHasFinished();
+ }
- async clickElasticsearchUsers() {
- await this.navigateTo();
- await this.clickUsersSection();
- }
+ async clickSaveCreateUser() {
+ await this.find.clickByButtonText('Create user');
+ await this.header.waitUntilLoadingHasFinished();
+ }
- async clickElasticsearchRoles() {
- await this.navigateTo();
- await this.clickRolesSection();
- }
+ async clickSaveEditRole() {
+ const saveButton = await this.retry.try(() => this.testSubjects.find('roleFormSaveButton'));
+ await saveButton.moveMouseTo();
+ await saveButton.click();
+ await this.header.waitUntilLoadingHasFinished();
+ }
- async getElasticsearchUsers() {
- const users = [];
- await testSubjects.click('tablePaginationPopoverButton');
- await testSubjects.click('tablePagination-100-rows');
- for (const user of await testSubjects.findAll('userRow')) {
- const fullnameElement = await user.findByTestSubject('userRowFullName');
- const usernameElement = await user.findByTestSubject('userRowUserName');
- const emailElement = await user.findByTestSubject('userRowEmail');
- const rolesElement = await user.findByTestSubject('userRowRoles');
- // findAll is substantially faster than `find.descendantExistsByCssSelector for negative cases
- const isUserReserved = (await user.findAllByTestSubject('userReserved', 1)).length > 0;
- const isUserDeprecated = (await user.findAllByTestSubject('userDeprecated', 1)).length > 0;
-
- users.push({
- username: await usernameElement.getVisibleText(),
- fullname: await fullnameElement.getVisibleText(),
- email: await emailElement.getVisibleText(),
- roles: (await rolesElement.getVisibleText()).split('\n').map((role) => role.trim()),
- reserved: isUserReserved,
- deprecated: isUserDeprecated,
- });
- }
+ async addIndexToRole(index: string) {
+ this.log.debug(`Adding index ${index} to role`);
+ await this.comboBox.setCustom('indicesInput0', index);
+ }
- return users;
- }
+ async addPrivilegeToRole(privilege: string) {
+ this.log.debug(`Adding privilege ${privilege} to role`);
+ const privilegeInput = await this.retry.try(() =>
+ this.find.byCssSelector('[data-test-subj="privilegesInput0"] input')
+ );
+ await privilegeInput.type(privilege);
- async getElasticsearchRoles() {
- const roles = [];
- await testSubjects.click('tablePaginationPopoverButton');
- await testSubjects.click('tablePagination-100-rows');
- for (const role of await testSubjects.findAll('roleRow')) {
- const [rolename, reserved, deprecated] = await Promise.all([
- role.findByTestSubject('roleRowName').then((el) => el.getVisibleText()),
- // findAll is substantially faster than `find.descendantExistsByCssSelector for negative cases
- role.findAllByTestSubject('roleReserved', 1).then((el) => el.length > 0),
- // findAll is substantially faster than `find.descendantExistsByCssSelector for negative cases
- role.findAllByTestSubject('roleDeprecated', 1).then((el) => el.length > 0),
- ]);
-
- roles.push({ rolename, reserved, deprecated });
- }
+ const btn = await this.find.byButtonText(privilege);
+ await btn.click();
- return roles;
- }
+ // const options = await this.find.byCssSelector(`.euiFilterSelectItem`);
+ // Object.entries(options).forEach(([key, prop]) => {
+ // console.log({ key, proto: prop.__proto__ });
+ // });
- /**
- * @deprecated Use `PageObjects.security.clickCreateNewUser` instead
- */
- async clickNewUser() {
- return await testSubjects.click('createUserButton');
- }
+ // await options.click();
+ }
- /**
- * @deprecated Use `PageObjects.security.clickCreateNewUser` instead
- */
- async clickNewRole() {
- return await testSubjects.click('createRoleButton');
- }
+ async assignRoleToUser(role: string) {
+ await this.selectRole(role);
+ }
- async fillUserForm(user: UserFormValues) {
- if (user.username) {
- await find.setValue('[name=username]', user.username);
- }
- if (user.password) {
- await find.setValue('[name=password]', user.password);
- }
- if (user.confirm_password) {
- await find.setValue('[name=confirm_password]', user.confirm_password);
- }
- if (user.full_name) {
- await find.setValue('[name=full_name]', user.full_name);
- }
- if (user.email) {
- await find.setValue('[name=email]', user.email);
- }
+ async navigateTo() {
+ await this.common.navigateToApp('settings');
+ }
- const rolesToAdd = user.roles || [];
- for (let i = 0; i < rolesToAdd.length; i++) {
- await this.selectRole(rolesToAdd[i]);
- }
+ async clickElasticsearchUsers() {
+ await this.navigateTo();
+ await this.clickUsersSection();
+ }
+
+ async clickElasticsearchRoles() {
+ await this.navigateTo();
+ await this.clickRolesSection();
+ }
+
+ async getElasticsearchUsers() {
+ const users = [];
+ await this.testSubjects.click('tablePaginationPopoverButton');
+ await this.testSubjects.click('tablePagination-100-rows');
+ for (const user of await this.testSubjects.findAll('userRow')) {
+ const fullnameElement = await user.findByTestSubject('userRowFullName');
+ const usernameElement = await user.findByTestSubject('userRowUserName');
+ const emailElement = await user.findByTestSubject('userRowEmail');
+ const rolesElement = await user.findByTestSubject('userRowRoles');
+ // findAll is substantially faster than `find.descendantExistsByCssSelector for negative cases
+ const isUserReserved = (await user.findAllByTestSubject('userReserved', 1)).length > 0;
+ const isUserDeprecated = (await user.findAllByTestSubject('userDeprecated', 1)).length > 0;
+
+ users.push({
+ username: await usernameElement.getVisibleText(),
+ fullname: await fullnameElement.getVisibleText(),
+ email: await emailElement.getVisibleText(),
+ roles: (await rolesElement.getVisibleText()).split('\n').map((role) => role.trim()),
+ reserved: isUserReserved,
+ deprecated: isUserDeprecated,
+ });
}
- async submitCreateUserForm() {
- await find.clickByButtonText('Create user');
+ return users;
+ }
+
+ async getElasticsearchRoles() {
+ const roles = [];
+ await this.testSubjects.click('tablePaginationPopoverButton');
+ await this.testSubjects.click('tablePagination-100-rows');
+ for (const role of await this.testSubjects.findAll('roleRow')) {
+ const [rolename, reserved, deprecated] = await Promise.all([
+ role.findByTestSubject('roleRowName').then((el) => el.getVisibleText()),
+ // findAll is substantially faster than `find.descendantExistsByCssSelector for negative cases
+ role.findAllByTestSubject('roleReserved', 1).then((el) => el.length > 0),
+ // findAll is substantially faster than `find.descendantExistsByCssSelector for negative cases
+ role.findAllByTestSubject('roleDeprecated', 1).then((el) => el.length > 0),
+ ]);
+
+ roles.push({ rolename, reserved, deprecated });
}
- async createUser(user: UserFormValues) {
- await this.clickElasticsearchUsers();
- await this.clickCreateNewUser();
- await this.fillUserForm(user);
- await this.submitCreateUserForm();
+ return roles;
+ }
+
+ /**
+ * @deprecated Use `this.security.clickCreateNewUser` instead
+ */
+ async clickNewUser() {
+ return await this.testSubjects.click('createUserButton');
+ }
+
+ /**
+ * @deprecated Use `this.security.clickCreateNewUser` instead
+ */
+ async clickNewRole() {
+ return await this.testSubjects.click('createRoleButton');
+ }
+
+ async fillUserForm(user: UserFormValues) {
+ if (user.username) {
+ await this.find.setValue('[name=username]', user.username);
+ }
+ if (user.password) {
+ await this.find.setValue('[name=password]', user.password);
+ }
+ if (user.confirm_password) {
+ await this.find.setValue('[name=confirm_password]', user.confirm_password);
+ }
+ if (user.full_name) {
+ await this.find.setValue('[name=full_name]', user.full_name);
+ }
+ if (user.email) {
+ await this.find.setValue('[name=email]', user.email);
}
- async addRole(roleName: string, roleObj: Role) {
- const self = this;
+ const rolesToAdd = user.roles || [];
+ for (let i = 0; i < rolesToAdd.length; i++) {
+ await this.selectRole(rolesToAdd[i]);
+ }
+ }
- await this.clickCreateNewRole();
+ async submitCreateUserForm() {
+ await this.find.clickByButtonText('Create user');
+ }
- // We have to use non-test-subject selectors because this markup is generated by ui-select.
- log.debug('roleObj.indices[0].names = ' + roleObj.elasticsearch.indices[0].names);
- await testSubjects.append('roleFormNameInput', roleName);
+ async createUser(user: UserFormValues) {
+ await this.clickElasticsearchUsers();
+ await this.clickCreateNewUser();
+ await this.fillUserForm(user);
+ await this.submitCreateUserForm();
+ }
- for (const indexName of roleObj.elasticsearch.indices[0].names) {
- await comboBox.setCustom('indicesInput0', indexName);
- }
+ async addRole(roleName: string, roleObj: Role) {
+ const self = this;
- if (roleObj.elasticsearch.indices[0].query) {
- await testSubjects.click('restrictDocumentsQuery0');
- await testSubjects.setValue('queryInput0', roleObj.elasticsearch.indices[0].query);
- }
+ await this.clickCreateNewRole();
- const globalPrivileges = (roleObj.kibana as any).global;
- if (globalPrivileges) {
- for (const privilegeName of globalPrivileges) {
- await testSubjects.click('addSpacePrivilegeButton');
+ // We have to use non-test-subject selectors because this markup is generated by ui-select.
+ this.log.debug('roleObj.indices[0].names = ' + roleObj.elasticsearch.indices[0].names);
+ await this.testSubjects.append('roleFormNameInput', roleName);
- await testSubjects.click('spaceSelectorComboBox');
+ for (const indexName of roleObj.elasticsearch.indices[0].names) {
+ await this.comboBox.setCustom('indicesInput0', indexName);
+ }
- const globalSpaceOption = await find.byCssSelector(`#spaceOption_\\*`);
- await globalSpaceOption.click();
+ if (roleObj.elasticsearch.indices[0].query) {
+ await this.testSubjects.click('restrictDocumentsQuery0');
+ await this.testSubjects.setValue('queryInput0', roleObj.elasticsearch.indices[0].query);
+ }
- await testSubjects.click(`basePrivilege_${privilegeName}`);
+ const globalPrivileges = (roleObj.kibana as any).global;
+ if (globalPrivileges) {
+ for (const privilegeName of globalPrivileges) {
+ await this.testSubjects.click('addSpacePrivilegeButton');
- await testSubjects.click('createSpacePrivilegeButton');
- }
- }
+ await this.testSubjects.click('spaceSelectorComboBox');
- function addPrivilege(privileges: string[]) {
- return privileges.reduce(function (promise: Promise, privilegeName: string) {
- return promise
- .then(() => self.addPrivilegeToRole(privilegeName))
- .then(() => PageObjects.common.sleep(250));
- }, Promise.resolve());
- }
+ const globalSpaceOption = await this.find.byCssSelector(`#spaceOption_\\*`);
+ await globalSpaceOption.click();
- await addPrivilege(roleObj.elasticsearch.indices[0].privileges);
+ await this.testSubjects.click(`basePrivilege_${privilegeName}`);
- async function addGrantedField(fields: string[]) {
- for (const entry of fields) {
- await comboBox.setCustom('fieldInput0', entry);
- }
+ await this.testSubjects.click('createSpacePrivilegeButton');
}
+ }
- // clicking the Granted fields and removing the asterix
- if (roleObj.elasticsearch.indices[0].field_security) {
- // Toggle FLS switch
- await testSubjects.click('restrictFieldsQuery0');
+ const addPrivilege = (privileges: string[]) => {
+ return privileges.reduce((promise: Promise, privilegeName: string) => {
+ return promise
+ .then(() => self.addPrivilegeToRole(privilegeName))
+ .then(() => this.common.sleep(250));
+ }, Promise.resolve());
+ };
- // have to remove the '*'
- await find.clickByCssSelector(
- 'div[data-test-subj="fieldInput0"] [title="Remove * from selection in this group"] svg.euiIcon'
- );
+ await addPrivilege(roleObj.elasticsearch.indices[0].privileges);
- await addGrantedField(roleObj.elasticsearch.indices[0].field_security!.grant!);
+ const addGrantedField = async (fields: string[]) => {
+ for (const entry of fields) {
+ await this.comboBox.setCustom('fieldInput0', entry);
}
+ };
- log.debug('click save button');
- await testSubjects.click('roleFormSaveButton');
+ // clicking the Granted fields and removing the asterix
+ if (roleObj.elasticsearch.indices[0].field_security) {
+ // Toggle FLS switch
+ await this.testSubjects.click('restrictFieldsQuery0');
- // Signifies that the role management page redirected back to the role grid page,
- // and successfully refreshed the grid
- await testSubjects.existOrFail('roleRow');
- }
+ // have to remove the '*'
+ await this.find.clickByCssSelector(
+ 'div[data-test-subj="fieldInput0"] [title="Remove * from selection in this group"] svg.euiIcon'
+ );
- async selectRole(role: string) {
- const dropdown = await testSubjects.find('rolesDropdown');
- const input = await dropdown.findByCssSelector('input');
- await input.type(role);
- await find.clickByCssSelector(`[role=option][title="${role}"]`);
- await testSubjects.click('comboBoxToggleListButton');
+ await addGrantedField(roleObj.elasticsearch.indices[0].field_security!.grant!);
}
- async deleteUser(username: string) {
- log.debug('Delete user ' + username);
- await find.clickByDisplayedLinkText(username);
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ this.log.debug('click save button');
+ await this.testSubjects.click('roleFormSaveButton');
+
+ // Signifies that the role management page redirected back to the role grid page,
+ // and successfully refreshed the grid
+ await this.testSubjects.existOrFail('roleRow');
+ }
- log.debug('Find delete button and click');
- await find.clickByButtonText('Delete user');
- await PageObjects.common.sleep(2000);
+ async selectRole(role: string) {
+ const dropdown = await this.testSubjects.find('rolesDropdown');
+ const input = await dropdown.findByCssSelector('input');
+ await input.type(role);
+ await this.find.clickByCssSelector(`[role=option][title="${role}"]`);
+ await this.testSubjects.click('comboBoxToggleListButton');
+ }
- const confirmText = await testSubjects.getVisibleText('confirmModalBodyText');
- log.debug('Delete user alert text = ' + confirmText);
- await testSubjects.click('confirmModalConfirmButton');
- return confirmText;
- }
+ async deleteUser(username: string) {
+ this.log.debug('Delete user ' + username);
+ await this.find.clickByDisplayedLinkText(username);
+ await this.header.awaitGlobalLoadingIndicatorHidden();
+
+ this.log.debug('Find delete button and click');
+ await this.find.clickByButtonText('Delete user');
+ await this.common.sleep(2000);
+
+ const confirmText = await this.testSubjects.getVisibleText('confirmModalBodyText');
+ this.log.debug('Delete user alert text = ' + confirmText);
+ await this.testSubjects.click('confirmModalConfirmButton');
+ return confirmText;
}
- return new SecurityPage();
}
diff --git a/x-pack/test/functional/page_objects/space_selector_page.ts b/x-pack/test/functional/page_objects/space_selector_page.ts
index 0a41e4b90287f..9e05ae1f2c42b 100644
--- a/x-pack/test/functional/page_objects/space_selector_page.ts
+++ b/x-pack/test/functional/page_objects/space_selector_page.ts
@@ -6,191 +6,187 @@
*/
import expect from '@kbn/expect';
-import { FtrProviderContext } from '../ftr_provider_context';
-
-export function SpaceSelectorPageProvider({ getService, getPageObjects }: FtrProviderContext) {
- const retry = getService('retry');
- const log = getService('log');
- const testSubjects = getService('testSubjects');
- const browser = getService('browser');
- const find = getService('find');
- const PageObjects = getPageObjects(['common']);
-
- class SpaceSelectorPage {
- async initTests() {
- log.debug('SpaceSelectorPage:initTests');
- }
-
- async clickSpaceCard(spaceId: string) {
- return await retry.try(async () => {
- log.info(`SpaceSelectorPage:clickSpaceCard(${spaceId})`);
- await testSubjects.click(`space-card-${spaceId}`);
- await PageObjects.common.sleep(1000);
- });
- }
+import { FtrService } from '../ftr_provider_context';
+
+export class SpaceSelectorPageObject extends FtrService {
+ private readonly retry = this.ctx.getService('retry');
+ private readonly log = this.ctx.getService('log');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly browser = this.ctx.getService('browser');
+ private readonly find = this.ctx.getService('find');
+ private readonly common = this.ctx.getPageObject('common');
+
+ async initTests() {
+ this.log.debug('SpaceSelectorPage:initTests');
+ }
- async expectHomePage(spaceId: string) {
- return await this.expectRoute(spaceId, `/app/home#/`);
- }
+ async clickSpaceCard(spaceId: string) {
+ return await this.retry.try(async () => {
+ this.log.info(`SpaceSelectorPage:clickSpaceCard(${spaceId})`);
+ await this.testSubjects.click(`space-card-${spaceId}`);
+ await this.common.sleep(1000);
+ });
+ }
- async expectRoute(spaceId: string, route: string) {
- return await retry.try(async () => {
- log.debug(`expectRoute(${spaceId}, ${route})`);
- await find.byCssSelector('[data-test-subj="kibanaChrome"] nav:not(.ng-hide) ', 20000);
- const url = await browser.getCurrentUrl();
- if (spaceId === 'default') {
- expect(url).to.contain(route);
- } else {
- expect(url).to.contain(`/s/${spaceId}${route}`);
- }
- });
- }
+ async expectHomePage(spaceId: string) {
+ return await this.expectRoute(spaceId, `/app/home#/`);
+ }
- async openSpacesNav() {
- log.debug('openSpacesNav()');
- return await testSubjects.click('spacesNavSelector');
- }
+ async expectRoute(spaceId: string, route: string) {
+ return await this.retry.try(async () => {
+ this.log.debug(`expectRoute(${spaceId}, ${route})`);
+ await this.find.byCssSelector('[data-test-subj="kibanaChrome"] nav:not(.ng-hide) ', 20000);
+ const url = await this.browser.getCurrentUrl();
+ if (spaceId === 'default') {
+ expect(url).to.contain(route);
+ } else {
+ expect(url).to.contain(`/s/${spaceId}${route}`);
+ }
+ });
+ }
- async clickManageSpaces() {
- await testSubjects.click('manageSpaces');
- }
+ async openSpacesNav() {
+ this.log.debug('openSpacesNav()');
+ return await this.testSubjects.click('spacesNavSelector');
+ }
- async clickCreateSpace() {
- await testSubjects.click('createSpace');
- }
+ async clickManageSpaces() {
+ await this.testSubjects.click('manageSpaces');
+ }
- async clickEnterSpaceName() {
- await testSubjects.click('addSpaceName');
- }
+ async clickCreateSpace() {
+ await this.testSubjects.click('createSpace');
+ }
- async addSpaceName(spaceName: string) {
- await testSubjects.setValue('addSpaceName', spaceName);
- }
+ async clickEnterSpaceName() {
+ await this.testSubjects.click('addSpaceName');
+ }
- async clickCustomizeSpaceAvatar(spaceId: string) {
- await testSubjects.click(`space-avatar-${spaceId}`);
- }
+ async addSpaceName(spaceName: string) {
+ await this.testSubjects.setValue('addSpaceName', spaceName);
+ }
- async clickSpaceInitials() {
- await testSubjects.click('spaceLetterInitial');
- }
+ async clickCustomizeSpaceAvatar(spaceId: string) {
+ await this.testSubjects.click(`space-avatar-${spaceId}`);
+ }
- async addSpaceInitials(spaceInitials: string) {
- await testSubjects.setValue('spaceLetterInitial', spaceInitials);
- }
+ async clickSpaceInitials() {
+ await this.testSubjects.click('spaceLetterInitial');
+ }
- async clickColorPicker() {
- await testSubjects.click('colorPickerAnchor');
- }
+ async addSpaceInitials(spaceInitials: string) {
+ await this.testSubjects.setValue('spaceLetterInitial', spaceInitials);
+ }
- async setColorinPicker(hexValue: string) {
- await testSubjects.setValue('colorPickerAnchor', hexValue);
- }
+ async clickColorPicker() {
+ await this.testSubjects.click('colorPickerAnchor');
+ }
- async clickShowFeatures() {
- await testSubjects.click('show-hide-section-link');
- }
+ async setColorinPicker(hexValue: string) {
+ await this.testSubjects.setValue('colorPickerAnchor', hexValue);
+ }
- async clickChangeAllPriv() {
- await testSubjects.click('changeAllPrivilegesButton');
- }
+ async clickShowFeatures() {
+ await this.testSubjects.click('show-hide-section-link');
+ }
- async clickSaveSpaceCreation() {
- await testSubjects.click('save-space-button');
- }
+ async clickChangeAllPriv() {
+ await this.testSubjects.click('changeAllPrivilegesButton');
+ }
- async clickSpaceEditButton(spaceName: string) {
- await testSubjects.click(`${spaceName}-editSpace`);
- }
+ async clickSaveSpaceCreation() {
+ await this.testSubjects.click('save-space-button');
+ }
- async clickGoToRolesPage() {
- await testSubjects.click('rolesManagementPage');
- }
+ async clickSpaceEditButton(spaceName: string) {
+ await this.testSubjects.click(`${spaceName}-editSpace`);
+ }
- async clickCancelSpaceCreation() {
- await testSubjects.click('cancel-space-button');
- }
+ async clickGoToRolesPage() {
+ await this.testSubjects.click('rolesManagementPage');
+ }
- async clickOnCustomizeURL() {
- await testSubjects.click('CustomizeOrReset');
- }
+ async clickCancelSpaceCreation() {
+ await this.testSubjects.click('cancel-space-button');
+ }
- async clickOnSpaceURLDisplay() {
- await testSubjects.click('spaceURLDisplay');
- }
+ async clickOnCustomizeURL() {
+ await this.testSubjects.click('CustomizeOrReset');
+ }
- async setSpaceURL(spaceURL: string) {
- await testSubjects.setValue('spaceURLDisplay', spaceURL);
- }
+ async clickOnSpaceURLDisplay() {
+ await this.testSubjects.click('spaceURLDisplay');
+ }
- async clickHideAllFeatures() {
- await testSubjects.click('spc-toggle-all-features-hide');
- }
+ async setSpaceURL(spaceURL: string) {
+ await this.testSubjects.setValue('spaceURLDisplay', spaceURL);
+ }
- async clickShowAllFeatures() {
- await testSubjects.click('spc-toggle-all-features-show');
- }
+ async clickHideAllFeatures() {
+ await this.testSubjects.click('spc-toggle-all-features-hide');
+ }
- async openFeatureCategory(categoryName: string) {
- const category = await find.byCssSelector(
- `button[aria-controls=featureCategory_${categoryName}]`
- );
- const isCategoryExpanded = (await category.getAttribute('aria-expanded')) === 'true';
- if (!isCategoryExpanded) {
- await category.click();
- }
- }
+ async clickShowAllFeatures() {
+ await this.testSubjects.click('spc-toggle-all-features-show');
+ }
- async closeFeatureCategory(categoryName: string) {
- const category = await find.byCssSelector(
- `button[aria-controls=featureCategory_${categoryName}]`
- );
- const isCategoryExpanded = (await category.getAttribute('aria-expanded')) === 'true';
- if (isCategoryExpanded) {
- await category.click();
- }
+ async openFeatureCategory(categoryName: string) {
+ const category = await this.find.byCssSelector(
+ `button[aria-controls=featureCategory_${categoryName}]`
+ );
+ const isCategoryExpanded = (await category.getAttribute('aria-expanded')) === 'true';
+ if (!isCategoryExpanded) {
+ await category.click();
}
+ }
- async toggleFeatureCategoryVisibility(categoryName: string) {
- await testSubjects.click(`featureCategoryButton_${categoryName}`);
+ async closeFeatureCategory(categoryName: string) {
+ const category = await this.find.byCssSelector(
+ `button[aria-controls=featureCategory_${categoryName}]`
+ );
+ const isCategoryExpanded = (await category.getAttribute('aria-expanded')) === 'true';
+ if (isCategoryExpanded) {
+ await category.click();
}
+ }
- async clickOnDescriptionOfSpace() {
- await testSubjects.click('descriptionSpaceText');
- }
+ async toggleFeatureCategoryVisibility(categoryName: string) {
+ await this.testSubjects.click(`featureCategoryButton_${categoryName}`);
+ }
- async setOnDescriptionOfSpace(descriptionSpace: string) {
- await testSubjects.setValue('descriptionSpaceText', descriptionSpace);
- }
+ async clickOnDescriptionOfSpace() {
+ await this.testSubjects.click('descriptionSpaceText');
+ }
- async clickOnDeleteSpaceButton(spaceName: string) {
- await testSubjects.click(`${spaceName}-deleteSpace`);
- }
+ async setOnDescriptionOfSpace(descriptionSpace: string) {
+ await this.testSubjects.setValue('descriptionSpaceText', descriptionSpace);
+ }
- async cancelDeletingSpace() {
- await testSubjects.click('confirmModalCancelButton');
- }
+ async clickOnDeleteSpaceButton(spaceName: string) {
+ await this.testSubjects.click(`${spaceName}-deleteSpace`);
+ }
- async confirmDeletingSpace() {
- await testSubjects.click('confirmModalConfirmButton');
- }
+ async cancelDeletingSpace() {
+ await this.testSubjects.click('confirmModalCancelButton');
+ }
- async clickOnSpaceb() {
- await testSubjects.click('space-avatar-space_b');
- }
+ async confirmDeletingSpace() {
+ await this.testSubjects.click('confirmModalConfirmButton');
+ }
- async goToSpecificSpace(spaceName: string) {
- await testSubjects.click(`${spaceName}-gotoSpace`);
- }
+ async clickOnSpaceb() {
+ await this.testSubjects.click('space-avatar-space_b');
+ }
- async clickSpaceAvatar(spaceId: string) {
- return await retry.try(async () => {
- log.info(`SpaceSelectorPage:clickSpaceAvatar(${spaceId})`);
- await testSubjects.click(`space-avatar-${spaceId}`);
- await PageObjects.common.sleep(1000);
- });
- }
+ async goToSpecificSpace(spaceName: string) {
+ await this.testSubjects.click(`${spaceName}-gotoSpace`);
}
- return new SpaceSelectorPage();
+ async clickSpaceAvatar(spaceId: string) {
+ return await this.retry.try(async () => {
+ this.log.info(`SpaceSelectorPage:clickSpaceAvatar(${spaceId})`);
+ await this.testSubjects.click(`space-avatar-${spaceId}`);
+ await this.common.sleep(1000);
+ });
+ }
}
diff --git a/x-pack/test/functional/page_objects/status_page.ts b/x-pack/test/functional/page_objects/status_page.ts
index ed90aef954770..fa9ff392f0aa1 100644
--- a/x-pack/test/functional/page_objects/status_page.ts
+++ b/x-pack/test/functional/page_objects/status_page.ts
@@ -5,20 +5,17 @@
* 2.0.
*/
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
-export function StatusPagePageProvider({ getService }: FtrProviderContext) {
- const log = getService('log');
- const find = getService('find');
- class StatusPage {
- async initTests() {
- log.debug('StatusPage:initTests');
- }
+export class StatusPageObject extends FtrService {
+ private readonly log = this.ctx.getService('log');
+ private readonly find = this.ctx.getService('find');
- async expectStatusPage(): Promise {
- await find.byCssSelector('[data-test-subj="statusPageRoot"]', 20000);
- }
+ async initTests() {
+ this.log.debug('StatusPage:initTests');
}
- return new StatusPage();
+ async expectStatusPage(): Promise {
+ await this.find.byCssSelector('[data-test-subj="statusPageRoot"]', 20000);
+ }
}
diff --git a/x-pack/test/functional/page_objects/tag_management_page.ts b/x-pack/test/functional/page_objects/tag_management_page.ts
index c503d2282cf6c..b3f6622b04a57 100644
--- a/x-pack/test/functional/page_objects/tag_management_page.ts
+++ b/x-pack/test/functional/page_objects/tag_management_page.ts
@@ -5,9 +5,10 @@
* 2.0.
*/
-// eslint-disable-next-line max-classes-per-file
+/* eslint-disable max-classes-per-file */
+
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService, FtrProviderContext } from '../ftr_provider_context';
interface FillTagFormFields {
name?: string;
@@ -17,463 +18,467 @@ interface FillTagFormFields {
type TagFormValidation = FillTagFormFields;
-export function TagManagementPageProvider({ getService, getPageObjects }: FtrProviderContext) {
- const testSubjects = getService('testSubjects');
- const find = getService('find');
- const browser = getService('browser');
- const PageObjects = getPageObjects(['header', 'common', 'savedObjects', 'settings']);
- const retry = getService('retry');
+/**
+ * Sub page object to manipulate the create/edit tag modal.
+ */
+class TagModal extends FtrService {
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly header = this.ctx.getPageObject('header');
+
+ constructor(ctx: FtrProviderContext, private readonly page: TagManagementPageObject) {
+ super(ctx);
+ }
/**
- * Sub page object to manipulate the create/edit tag modal.
+ * Open the create tag modal, by clicking on the associated button.
*/
- class TagModal {
- constructor(private readonly page: TagManagementPage) {}
- /**
- * Open the create tag modal, by clicking on the associated button.
- */
- async openCreate() {
- return await testSubjects.click('createTagButton');
- }
-
- /**
- * Open the edit tag modal for given tag name. The tag must be in the currently displayed tags.
- */
- async openEdit(tagName: string) {
- await this.page.clickEdit(tagName);
- }
+ async openCreate() {
+ return await this.testSubjects.click('createTagButton');
+ }
- /**
- * Fills the given fields in the form.
- *
- * If a field is undefined, will not set the value (use a empty string for that)
- * If `submit` is true, will call `clickConfirm` once the fields have been filled.
- */
- async fillForm(fields: FillTagFormFields, { submit = false }: { submit?: boolean } = {}) {
- if (fields.name !== undefined) {
- await testSubjects.click('createModalField-name');
- await testSubjects.setValue('createModalField-name', fields.name);
- }
- if (fields.color !== undefined) {
- // EuiColorPicker does not allow to specify data-test-subj for the colorpicker input
- await testSubjects.setValue('colorPickerAnchor', fields.color);
- }
- if (fields.description !== undefined) {
- await testSubjects.click('createModalField-description');
- await testSubjects.setValue('createModalField-description', fields.description);
- }
+ /**
+ * Open the edit tag modal for given tag name. The tag must be in the currently displayed tags.
+ */
+ async openEdit(tagName: string) {
+ await this.page.clickEdit(tagName);
+ }
- if (submit) {
- await this.clickConfirm();
- }
+ /**
+ * Fills the given fields in the form.
+ *
+ * If a field is undefined, will not set the value (use a empty string for that)
+ * If `submit` is true, will call `clickConfirm` once the fields have been filled.
+ */
+ async fillForm(fields: FillTagFormFields, { submit = false }: { submit?: boolean } = {}) {
+ if (fields.name !== undefined) {
+ await this.testSubjects.click('createModalField-name');
+ await this.testSubjects.setValue('createModalField-name', fields.name);
+ }
+ if (fields.color !== undefined) {
+ // EuiColorPicker does not allow to specify data-test-subj for the colorpicker input
+ await this.testSubjects.setValue('colorPickerAnchor', fields.color);
+ }
+ if (fields.description !== undefined) {
+ await this.testSubjects.click('createModalField-description');
+ await this.testSubjects.setValue('createModalField-description', fields.description);
}
- /**
- * Return the values currently displayed in the form.
- */
- async getFormValues(): Promise> {
- return {
- name: await testSubjects.getAttribute('createModalField-name', 'value'),
- color: await testSubjects.getAttribute('colorPickerAnchor', 'value'),
- description: await testSubjects.getAttribute('createModalField-description', 'value'),
- };
+ if (submit) {
+ await this.clickConfirm();
}
+ }
- /**
- * Return the validation errors currently displayed for each field.
- */
- async getValidationErrors(): Promise {
- const errors: TagFormValidation = {};
+ /**
+ * Return the values currently displayed in the form.
+ */
+ async getFormValues(): Promise> {
+ return {
+ name: await this.testSubjects.getAttribute('createModalField-name', 'value'),
+ color: await this.testSubjects.getAttribute('colorPickerAnchor', 'value'),
+ description: await this.testSubjects.getAttribute('createModalField-description', 'value'),
+ };
+ }
- const getError = async (rowDataTestSubj: string) => {
- const row = await testSubjects.find(rowDataTestSubj);
- const errorElements = await row.findAllByClassName('euiFormErrorText');
- return errorElements.length ? await errorElements[0].getVisibleText() : undefined;
- };
+ /**
+ * Return the validation errors currently displayed for each field.
+ */
+ async getValidationErrors(): Promise {
+ const errors: TagFormValidation = {};
- errors.name = await getError('createModalRow-name');
- errors.color = await getError('createModalRow-color');
- errors.description = await getError('createModalRow-description');
+ const getError = async (rowDataTestSubj: string) => {
+ const row = await this.testSubjects.find(rowDataTestSubj);
+ const errorElements = await row.findAllByClassName('euiFormErrorText');
+ return errorElements.length ? await errorElements[0].getVisibleText() : undefined;
+ };
- return errors;
- }
+ errors.name = await getError('createModalRow-name');
+ errors.color = await getError('createModalRow-color');
+ errors.description = await getError('createModalRow-description');
- /**
- * Returns true if the form as at least one error displayed, false otherwise
- */
- async hasError() {
- const errors = await this.getValidationErrors();
- return Boolean(errors.name || errors.color || errors.description);
- }
+ return errors;
+ }
- /**
- * Click on the 'cancel' button in the create/edit modal.
- */
- async clickCancel() {
- await testSubjects.click('createModalCancelButton');
- }
+ /**
+ * Returns true if the form as at least one error displayed, false otherwise
+ */
+ async hasError() {
+ const errors = await this.getValidationErrors();
+ return Boolean(errors.name || errors.color || errors.description);
+ }
- /**
- * Click on the 'confirm' button in the create/edit modal if not disabled.
- */
- async clickConfirm() {
- await testSubjects.click('createModalConfirmButton');
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ /**
+ * Click on the 'cancel' button in the create/edit modal.
+ */
+ async clickCancel() {
+ await this.testSubjects.click('createModalCancelButton');
+ }
- /**
- * Return true if the confirm button is disabled, false otherwise.
- */
- async isConfirmDisabled() {
- const disabled = await testSubjects.getAttribute('createModalConfirmButton', 'disabled');
- return disabled === 'true';
- }
+ /**
+ * Click on the 'confirm' button in the create/edit modal if not disabled.
+ */
+ async clickConfirm() {
+ await this.testSubjects.click('createModalConfirmButton');
+ await this.header.waitUntilLoadingHasFinished();
+ }
- /**
- * Return true if the modal is currently opened.
- */
- async isOpened() {
- return await testSubjects.exists('tagModalForm');
- }
+ /**
+ * Return true if the confirm button is disabled, false otherwise.
+ */
+ async isConfirmDisabled() {
+ const disabled = await this.testSubjects.getAttribute('createModalConfirmButton', 'disabled');
+ return disabled === 'true';
+ }
- /**
- * Wait until the modal is closed.
- */
- async waitUntilClosed() {
- await retry.try(async () => {
- if (await this.isOpened()) {
- throw new Error('save modal still open');
- }
- });
- }
+ /**
+ * Return true if the modal is currently opened.
+ */
+ async isOpened() {
+ return await this.testSubjects.exists('tagModalForm');
+ }
- /**
- * Close the modal if currently opened.
- */
- async closeIfOpened() {
+ /**
+ * Wait until the modal is closed.
+ */
+ async waitUntilClosed() {
+ await this.retry.try(async () => {
if (await this.isOpened()) {
- await this.clickCancel();
+ throw new Error('save modal still open');
}
- }
+ });
}
/**
- * Sub page object to manipulate the assign flyout.
+ * Close the modal if currently opened.
*/
- class TagAssignmentFlyout {
- constructor(private readonly page: TagManagementPage) {}
-
- /**
- * Open the tag assignment flyout, by selected given `tagNames` in the table, then clicking on the `assign`
- * action in the bulk action menu.
- */
- async open(tagNames: string[]) {
- for (const tagName of tagNames) {
- await this.page.selectTagByName(tagName);
- }
- await this.page.clickOnBulkAction('assign');
- await this.waitUntilResultsAreLoaded();
+ async closeIfOpened() {
+ if (await this.isOpened()) {
+ await this.clickCancel();
}
+ }
+}
- /**
- * Click on the 'cancel' button in the assign flyout.
- */
- async clickCancel() {
- await testSubjects.click('assignFlyoutCancelButton');
- await this.page.waitUntilTableIsLoaded();
- }
+/**
+ * Sub page object to manipulate the assign flyout.
+ */
+class TagAssignmentFlyout extends FtrService {
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly find = this.ctx.getService('find');
- /**
- * Click on the 'confirm' button in the assign flyout.
- */
- async clickConfirm() {
- await testSubjects.click('assignFlyoutConfirmButton');
- await this.waitForFlyoutToClose();
- await this.page.waitUntilTableIsLoaded();
- }
+ constructor(ctx: FtrProviderContext, private readonly page: TagManagementPageObject) {
+ super(ctx);
+ }
- /**
- * Click on an assignable object result line in the flyout result list.
- */
- async clickOnResult(type: string, id: string) {
- await testSubjects.click(`assign-result-${type}-${id}`);
+ /**
+ * Open the tag assignment flyout, by selected given `tagNames` in the table, then clicking on the `assign`
+ * action in the bulk action menu.
+ */
+ async open(tagNames: string[]) {
+ for (const tagName of tagNames) {
+ await this.page.selectTagByName(tagName);
}
+ await this.page.clickOnBulkAction('assign');
+ await this.waitUntilResultsAreLoaded();
+ }
- /**
- * Wait until the assignable object results are displayed in the flyout.
- */
- async waitUntilResultsAreLoaded() {
- return find.waitForDeletedByCssSelector(
- '*[data-test-subj="assignFlyoutResultList"] .euiLoadingSpinner'
- );
- }
+ /**
+ * Click on the 'cancel' button in the assign flyout.
+ */
+ async clickCancel() {
+ await this.testSubjects.click('assignFlyoutCancelButton');
+ await this.page.waitUntilTableIsLoaded();
+ }
- /**
- * Wait until the flyout is closed.
- */
- async waitForFlyoutToClose() {
- return testSubjects.waitForDeleted('assignFlyoutResultList');
- }
+ /**
+ * Click on the 'confirm' button in the assign flyout.
+ */
+ async clickConfirm() {
+ await this.testSubjects.click('assignFlyoutConfirmButton');
+ await this.waitForFlyoutToClose();
+ await this.page.waitUntilTableIsLoaded();
}
/**
- * Tag management page object.
- *
- * @remarks All the table manipulation helpers makes the assumption
- * that all tags are displayed on a single page. Pagination
- * and finding / interacting with a tag on another page is not supported.
- */
- class TagManagementPage {
- public readonly tagModal = new TagModal(this);
- public readonly assignFlyout = new TagAssignmentFlyout(this);
-
- /**
- * Navigate to the tag management section, by accessing the management app, then clicking
- * on the `tags` link.
- */
- async navigateTo() {
- await PageObjects.settings.navigateTo();
- await testSubjects.click('tags');
- await this.waitUntilTableIsLoaded();
- }
+ * Click on an assignable object result line in the flyout result list.
+ */
+ async clickOnResult(type: string, id: string) {
+ await this.testSubjects.click(`assign-result-${type}-${id}`);
+ }
- /**
- * Wait until the tags table is displayed and is not in a the loading state
- */
- async waitUntilTableIsLoaded() {
- return retry.try(async () => {
- const isLoaded = await find.existsByDisplayedByCssSelector(
- '*[data-test-subj="tagsManagementTable"]:not(.euiBasicTable-loading)'
- );
-
- if (isLoaded) {
- return true;
- } else {
- throw new Error('Waiting');
- }
- });
- }
+ /**
+ * Wait until the assignable object results are displayed in the flyout.
+ */
+ async waitUntilResultsAreLoaded() {
+ return this.find.waitForDeletedByCssSelector(
+ '*[data-test-subj="assignFlyoutResultList"] .euiLoadingSpinner'
+ );
+ }
- /**
- * Type given `term` in the table's search bar
- */
- async searchForTerm(term: string) {
- const searchBox = await testSubjects.find('tagsManagementSearchBar');
- await searchBox.clearValue();
- await searchBox.type(term);
- await searchBox.pressKeys(browser.keys.ENTER);
- await PageObjects.header.waitUntilLoadingHasFinished();
- await this.waitUntilTableIsLoaded();
- }
+ /**
+ * Wait until the flyout is closed.
+ */
+ async waitForFlyoutToClose() {
+ return this.testSubjects.waitForDeleted('assignFlyoutResultList');
+ }
+}
- /**
- * Return true if the `Create new tag` button is visible, false otherwise.
- */
- async isCreateButtonVisible() {
- return await testSubjects.exists('createTagButton');
- }
+/**
+ * Tag management page object.
+ *
+ * @remarks All the table manipulation helpers makes the assumption
+ * that all tags are displayed on a single page. Pagination
+ * and finding / interacting with a tag on another page is not supported.
+ */
+export class TagManagementPageObject extends FtrService {
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly find = this.ctx.getService('find');
+ private readonly browser = this.ctx.getService('browser');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly header = this.ctx.getPageObject('header');
+ private readonly settings = this.ctx.getPageObject('settings');
- /**
- * Returns true if given action is available from the table action column
- */
- async isActionAvailable(action: string) {
- const rows = await testSubjects.findAll('tagsTableRow');
- const firstRow = rows[0];
- // if there is more than 2 actions, they are wrapped in a popover that opens from a new action.
- const menuActionPresent = await testSubjects.descendantExists(
- 'euiCollapsedItemActionsButton',
- firstRow
- );
- if (menuActionPresent) {
- const actionButton = await testSubjects.findDescendant(
- 'euiCollapsedItemActionsButton',
- firstRow
- );
- await actionButton.click();
- const actionPresent = await testSubjects.exists(`tagsTableAction-${action}`);
- await actionButton.click();
- return actionPresent;
- } else {
- return await testSubjects.exists(`tagsTableAction-${action}`);
- }
- }
+ public readonly tagModal = new TagModal(this.ctx, this);
+ public readonly assignFlyout = new TagAssignmentFlyout(this.ctx, this);
- /**
- * Return the (table ordered) name of the tags currently displayed in the table.
- */
- async getDisplayedTagNames() {
- const tags = await this.getDisplayedTagsInfo();
- return tags.map((tag) => tag.name);
- }
+ /**
+ * Navigate to the tag management section, by accessing the management app, then clicking
+ * on the `tags` link.
+ */
+ async navigateTo() {
+ await this.settings.navigateTo();
+ await this.testSubjects.click('tags');
+ await this.waitUntilTableIsLoaded();
+ }
- /**
- * Return true if the 'connections' link is displayed for given tag name.
- *
- * Having the link not displayed can either mean that the user don't have the view
- * permission to see the connections, or that the tag don't have any.
- */
- async isConnectionLinkDisplayed(tagName: string) {
- const tags = await this.getDisplayedTagsInfo();
- const tag = tags.find((t) => t.name === tagName);
- return tag ? tag.connectionCount !== undefined : false;
- }
+ /**
+ * Wait until the tags table is displayed and is not in a the loading state
+ */
+ async waitUntilTableIsLoaded() {
+ return this.retry.try(async () => {
+ const isLoaded = await this.find.existsByDisplayedByCssSelector(
+ '*[data-test-subj="tagsManagementTable"]:not(.euiBasicTable-loading)'
+ );
- /**
- * Click the 'edit' action button in the table for given tag name.
- */
- async clickEdit(tagName: string) {
- const tagRow = await this.getRowByName(tagName);
- if (tagRow) {
- const editButton = await testSubjects.findDescendant('tagsTableAction-edit', tagRow);
- await editButton?.click();
+ if (isLoaded) {
+ return true;
+ } else {
+ throw new Error('Waiting');
}
- }
+ });
+ }
- /**
- * Return the raw `WebElementWrapper` of the table's row for given tag name.
- */
- async getRowByName(tagName: string) {
- const tagNames = await this.getDisplayedTagNames();
- const tagIndex = tagNames.indexOf(tagName);
- const rows = await testSubjects.findAll('tagsTableRow');
- return rows[tagIndex];
- }
+ /**
+ * Type given `term` in the table's search bar
+ */
+ async searchForTerm(term: string) {
+ const searchBox = await this.testSubjects.find('tagsManagementSearchBar');
+ await searchBox.clearValue();
+ await searchBox.type(term);
+ await searchBox.pressKeys(this.browser.keys.ENTER);
+ await this.header.waitUntilLoadingHasFinished();
+ await this.waitUntilTableIsLoaded();
+ }
- /**
- * Click on the 'connections' link in the table for given tag name.
- */
- async clickOnConnectionsLink(tagName: string) {
- const tagRow = await this.getRowByName(tagName);
- const connectionLink = await testSubjects.findDescendant(
- 'tagsTableRowConnectionsLink',
- tagRow
- );
- await connectionLink.click();
- }
+ /**
+ * Return true if the `Create new tag` button is visible, false otherwise.
+ */
+ async isCreateButtonVisible() {
+ return await this.testSubjects.exists('createTagButton');
+ }
- /**
- * Return true if the selection column is displayed on the table, false otherwise.
- */
- async isSelectionColumnDisplayed() {
- const firstRow = await testSubjects.find('tagsTableRow');
- const checkbox = await firstRow.findAllByCssSelector(
- '.euiTableRowCellCheckbox .euiCheckbox__input'
+ /**
+ * Returns true if given action is available from the table action column
+ */
+ async isActionAvailable(action: string) {
+ const rows = await this.testSubjects.findAll('tagsTableRow');
+ const firstRow = rows[0];
+ // if there is more than 2 actions, they are wrapped in a popover that opens from a new action.
+ const menuActionPresent = await this.testSubjects.descendantExists(
+ 'euiCollapsedItemActionsButton',
+ firstRow
+ );
+ if (menuActionPresent) {
+ const actionButton = await this.testSubjects.findDescendant(
+ 'euiCollapsedItemActionsButton',
+ firstRow
);
- return Boolean(checkbox.length);
+ await actionButton.click();
+ const actionPresent = await this.testSubjects.exists(`tagsTableAction-${action}`);
+ await actionButton.click();
+ return actionPresent;
+ } else {
+ return await this.testSubjects.exists(`tagsTableAction-${action}`);
}
+ }
- /**
- * Click on the selection checkbox of the tag matching given tag name.
- */
- async selectTagByName(tagName: string) {
- const tagRow = await this.getRowByName(tagName);
- const checkbox = await tagRow.findByCssSelector(
- '.euiTableRowCellCheckbox .euiCheckbox__input'
- );
- await checkbox.click();
- }
+ /**
+ * Return the (table ordered) name of the tags currently displayed in the table.
+ */
+ async getDisplayedTagNames() {
+ const tags = await this.getDisplayedTagsInfo();
+ return tags.map((tag) => tag.name);
+ }
- /**
- * Returns true if the tag bulk action menu is displayed, false otherwise.
- */
- async isActionMenuButtonDisplayed() {
- return testSubjects.exists('actionBar-contextMenuButton');
- }
+ /**
+ * Return true if the 'connections' link is displayed for given tag name.
+ *
+ * Having the link not displayed can either mean that the user don't have the view
+ * permission to see the connections, or that the tag don't have any.
+ */
+ async isConnectionLinkDisplayed(tagName: string) {
+ const tags = await this.getDisplayedTagsInfo();
+ const tag = tags.find((t) => t.name === tagName);
+ return tag ? tag.connectionCount !== undefined : false;
+ }
- /**
- * Open the bulk action menu if not already opened.
- */
- async openActionMenu() {
- if (!(await this.isActionMenuOpened())) {
- await this.toggleActionMenu();
- }
+ /**
+ * Click the 'edit' action button in the table for given tag name.
+ */
+ async clickEdit(tagName: string) {
+ const tagRow = await this.getRowByName(tagName);
+ if (tagRow) {
+ const editButton = await this.testSubjects.findDescendant('tagsTableAction-edit', tagRow);
+ await editButton?.click();
}
+ }
- /**
- * Check if the action for given `actionId` is present in the bulk action menu.
- *
- * The menu will automatically be opened if not already, but the test must still
- * select tags to make the action menu button appear.
- */
- async isBulkActionPresent(actionId: string) {
- if (!(await this.isActionMenuButtonDisplayed())) {
- return false;
- }
- const menuWasOpened = await this.isActionMenuOpened();
- if (!menuWasOpened) {
- await this.openActionMenu();
- }
+ /**
+ * Return the raw `WebElementWrapper` of the table's row for given tag name.
+ */
+ async getRowByName(tagName: string) {
+ const tagNames = await this.getDisplayedTagNames();
+ const tagIndex = tagNames.indexOf(tagName);
+ const rows = await this.testSubjects.findAll('tagsTableRow');
+ return rows[tagIndex];
+ }
+
+ /**
+ * Click on the 'connections' link in the table for given tag name.
+ */
+ async clickOnConnectionsLink(tagName: string) {
+ const tagRow = await this.getRowByName(tagName);
+ const connectionLink = await this.testSubjects.findDescendant(
+ 'tagsTableRowConnectionsLink',
+ tagRow
+ );
+ await connectionLink.click();
+ }
- const actionExists = await testSubjects.exists(`actionBar-button-${actionId}`);
+ /**
+ * Return true if the selection column is displayed on the table, false otherwise.
+ */
+ async isSelectionColumnDisplayed() {
+ const firstRow = await this.testSubjects.find('tagsTableRow');
+ const checkbox = await firstRow.findAllByCssSelector(
+ '.euiTableRowCellCheckbox .euiCheckbox__input'
+ );
+ return Boolean(checkbox.length);
+ }
- if (!menuWasOpened) {
- await this.toggleActionMenu();
- }
+ /**
+ * Click on the selection checkbox of the tag matching given tag name.
+ */
+ async selectTagByName(tagName: string) {
+ const tagRow = await this.getRowByName(tagName);
+ const checkbox = await tagRow.findByCssSelector('.euiTableRowCellCheckbox .euiCheckbox__input');
+ await checkbox.click();
+ }
+
+ /**
+ * Returns true if the tag bulk action menu is displayed, false otherwise.
+ */
+ async isActionMenuButtonDisplayed() {
+ return this.testSubjects.exists('actionBar-contextMenuButton');
+ }
- return actionExists;
+ /**
+ * Open the bulk action menu if not already opened.
+ */
+ async openActionMenu() {
+ if (!(await this.isActionMenuOpened())) {
+ await this.toggleActionMenu();
}
+ }
- /**
- * Click on given bulk action button
- */
- async clickOnBulkAction(actionId: string) {
+ /**
+ * Check if the action for given `actionId` is present in the bulk action menu.
+ *
+ * The menu will automatically be opened if not already, but the test must still
+ * select tags to make the action menu button appear.
+ */
+ async isBulkActionPresent(actionId: string) {
+ if (!(await this.isActionMenuButtonDisplayed())) {
+ return false;
+ }
+ const menuWasOpened = await this.isActionMenuOpened();
+ if (!menuWasOpened) {
await this.openActionMenu();
- await testSubjects.click(`actionBar-button-${actionId}`);
}
- /**
- * Toggle (close if opened, open if closed) the bulk action menu.
- */
- async toggleActionMenu() {
- await testSubjects.click('actionBar-contextMenuButton');
- }
+ const actionExists = await this.testSubjects.exists(`actionBar-button-${actionId}`);
- /**
- * Return true if the bulk action menu is opened, false otherwise.
- */
- async isActionMenuOpened() {
- return testSubjects.exists('actionBar-contextMenuPopover');
+ if (!menuWasOpened) {
+ await this.toggleActionMenu();
}
- /**
- * Return the info of all the tags currently displayed in the table (in table's order)
- */
- async getDisplayedTagsInfo() {
- const rows = await testSubjects.findAll('tagsTableRow');
- return Promise.all([...rows.map(parseTableRow)]);
- }
+ return actionExists;
+ }
- async getDisplayedTagInfo(tagName: string) {
- const rows = await this.getDisplayedTagsInfo();
- return rows.find((row) => row.name === tagName);
- }
+ /**
+ * Click on given bulk action button
+ */
+ async clickOnBulkAction(actionId: string) {
+ await this.openActionMenu();
+ await this.testSubjects.click(`actionBar-button-${actionId}`);
+ }
- /**
- * Converts the tagName to the format used in test subjects
- * @param tagName
- */
- testSubjFriendly(tagName: string) {
- return tagName.replace(' ', '_');
- }
+ /**
+ * Toggle (close if opened, open if closed) the bulk action menu.
+ */
+ async toggleActionMenu() {
+ await this.testSubjects.click('actionBar-contextMenuButton');
}
- const parseTableRow = async (row: WebElementWrapper) => {
- const dom = await row.parseDomContent();
+ /**
+ * Return true if the bulk action menu is opened, false otherwise.
+ */
+ async isActionMenuOpened() {
+ return this.testSubjects.exists('actionBar-contextMenuPopover');
+ }
- const connectionsText = dom.findTestSubject('tagsTableRowConnectionsLink').text();
- const rawConnectionCount = connectionsText.replace(/[^0-9]/g, '');
- const connectionCount = rawConnectionCount ? parseInt(rawConnectionCount, 10) : undefined;
+ /**
+ * Return the info of all the tags currently displayed in the table (in table's order)
+ */
+ async getDisplayedTagsInfo() {
+ const rows = await this.testSubjects.findAll('tagsTableRow');
+ return Promise.all([...rows.map(parseTableRow)]);
+ }
- // ideally we would also return the color, but it can't be easily retrieved from the DOM
- return {
- name: dom.findTestSubject('tagsTableRowName').find('.euiTableCellContent').text(),
- description: dom
- .findTestSubject('tagsTableRowDescription')
- .find('.euiTableCellContent')
- .text(),
- connectionCount,
- };
- };
+ async getDisplayedTagInfo(tagName: string) {
+ const rows = await this.getDisplayedTagsInfo();
+ return rows.find((row) => row.name === tagName);
+ }
- return new TagManagementPage();
+ /**
+ * Converts the tagName to the format used in test subjects
+ * @param tagName
+ */
+ testSubjFriendly(tagName: string) {
+ return tagName.replace(' ', '_');
+ }
}
+
+const parseTableRow = async (row: WebElementWrapper) => {
+ const dom = await row.parseDomContent();
+
+ const connectionsText = dom.findTestSubject('tagsTableRowConnectionsLink').text();
+ const rawConnectionCount = connectionsText.replace(/[^0-9]/g, '');
+ const connectionCount = rawConnectionCount ? parseInt(rawConnectionCount, 10) : undefined;
+
+ // ideally we would also return the color, but it can't be easily retrieved from the DOM
+ return {
+ name: dom.findTestSubject('tagsTableRowName').find('.euiTableCellContent').text(),
+ description: dom.findTestSubject('tagsTableRowDescription').find('.euiTableCellContent').text(),
+ connectionCount,
+ };
+};
diff --git a/x-pack/test/functional/page_objects/upgrade_assistant_page.ts b/x-pack/test/functional/page_objects/upgrade_assistant_page.ts
index 1c4a85450a8da..211bcbbd59219 100644
--- a/x-pack/test/functional/page_objects/upgrade_assistant_page.ts
+++ b/x-pack/test/functional/page_objects/upgrade_assistant_page.ts
@@ -5,76 +5,72 @@
* 2.0.
*/
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
-export function UpgradeAssistantPageProvider({ getPageObjects, getService }: FtrProviderContext) {
- const retry = getService('retry');
- const log = getService('log');
- const browser = getService('browser');
- const find = getService('find');
- const testSubjects = getService('testSubjects');
- const { common } = getPageObjects(['common']);
+export class UpgradeAssistantPageObject extends FtrService {
+ private readonly retry = this.ctx.getService('retry');
+ private readonly log = this.ctx.getService('log');
+ private readonly browser = this.ctx.getService('browser');
+ private readonly find = this.ctx.getService('find');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly common = this.ctx.getPageObject('common');
- class UpgradeAssistant {
- async initTests() {
- log.debug('UpgradeAssistant:initTests');
- }
+ async initTests() {
+ this.log.debug('UpgradeAssistant:initTests');
+ }
- async navigateToPage() {
- return await retry.try(async () => {
- await common.navigateToApp('settings');
- await testSubjects.click('upgrade_assistant');
- await retry.waitFor('url to contain /upgrade_assistant', async () => {
- const url = await browser.getCurrentUrl();
- return url.includes('/upgrade_assistant');
- });
+ async navigateToPage() {
+ return await this.retry.try(async () => {
+ await this.common.navigateToApp('settings');
+ await this.testSubjects.click('upgrade_assistant');
+ await this.retry.waitFor('url to contain /upgrade_assistant', async () => {
+ const url = await this.browser.getCurrentUrl();
+ return url.includes('/upgrade_assistant');
});
- }
-
- async toggleDeprecationLogging() {
- log.debug('toggleDeprecationLogging()');
- await testSubjects.click('upgradeAssistantDeprecationToggle');
- }
+ });
+ }
- async isDeprecationLoggingEnabled() {
- const isDeprecationEnabled = await testSubjects.getAttribute(
- 'upgradeAssistantDeprecationToggle',
- 'aria-checked'
- );
- log.debug(`Deprecation enabled == ${isDeprecationEnabled}`);
- return isDeprecationEnabled === 'true';
- }
+ async toggleDeprecationLogging() {
+ this.log.debug('toggleDeprecationLogging()');
+ await this.testSubjects.click('upgradeAssistantDeprecationToggle');
+ }
- async deprecationLoggingEnabledLabel() {
- const loggingEnabledLabel = await find.byCssSelector(
- '[data-test-subj="upgradeAssistantDeprecationToggle"] ~ span'
- );
- return await loggingEnabledLabel.getVisibleText();
- }
+ async isDeprecationLoggingEnabled() {
+ const isDeprecationEnabled = await this.testSubjects.getAttribute(
+ 'upgradeAssistantDeprecationToggle',
+ 'aria-checked'
+ );
+ this.log.debug(`Deprecation enabled == ${isDeprecationEnabled}`);
+ return isDeprecationEnabled === 'true';
+ }
- async clickTab(tabId: string) {
- return await retry.try(async () => {
- log.debug('clickTab()');
- await find.clickByCssSelector(`.euiTabs .euiTab#${tabId}`);
- });
- }
+ async deprecationLoggingEnabledLabel() {
+ const loggingEnabledLabel = await this.find.byCssSelector(
+ '[data-test-subj="upgradeAssistantDeprecationToggle"] ~ span'
+ );
+ return await loggingEnabledLabel.getVisibleText();
+ }
- async waitForTelemetryHidden() {
- const self = this;
- await retry.waitFor('Telemetry to disappear.', async () => {
- return (await self.isTelemetryExists()) === false;
- });
- }
+ async clickTab(tabId: string) {
+ return await this.retry.try(async () => {
+ this.log.debug('clickTab()');
+ await this.find.clickByCssSelector(`.euiTabs .euiTab#${tabId}`);
+ });
+ }
- async issueSummaryText() {
- log.debug('expectIssueSummary()');
- return await testSubjects.getVisibleText('upgradeAssistantIssueSummary');
- }
+ async waitForTelemetryHidden() {
+ const self = this;
+ await this.retry.waitFor('Telemetry to disappear.', async () => {
+ return (await self.isTelemetryExists()) === false;
+ });
+ }
- async isTelemetryExists() {
- return await testSubjects.exists('upgradeAssistantTelemetryRunning');
- }
+ async issueSummaryText() {
+ this.log.debug('expectIssueSummary()');
+ return await this.testSubjects.getVisibleText('upgradeAssistantIssueSummary');
}
- return new UpgradeAssistant();
+ async isTelemetryExists() {
+ return await this.testSubjects.exists('upgradeAssistantTelemetryRunning');
+ }
}
diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts
index 03e1513eac043..366511f7f43c5 100644
--- a/x-pack/test/functional/page_objects/uptime_page.ts
+++ b/x-pack/test/functional/page_objects/uptime_page.ts
@@ -6,117 +6,121 @@
*/
import expect from '@kbn/expect';
-import { FtrProviderContext } from '../ftr_provider_context';
-
-export function UptimePageProvider({ getPageObjects, getService }: FtrProviderContext) {
- const pageObjects = getPageObjects(['timePicker', 'header']);
- const { common: commonService, monitor, navigation } = getService('uptime');
- const retry = getService('retry');
-
- return new (class UptimePage {
- public async goToRoot(refresh?: boolean) {
- await navigation.goToUptime();
- if (refresh) {
- await navigation.refreshApp();
- }
- }
-
- public async setDateRange(start: string, end: string) {
- const { start: prevStart, end: prevEnd } = await pageObjects.timePicker.getTimeConfig();
- if (start !== prevStart || prevEnd !== end) {
- await pageObjects.timePicker.setAbsoluteRange(start, end);
- } else {
- await navigation.refreshApp();
- }
- }
-
- public async goToUptimeOverviewAndLoadData(
- dateStart: string,
- dateEnd: string,
- monitorIdToCheck?: string
- ) {
- await navigation.goToUptime();
- await this.setDateRange(dateStart, dateEnd);
- if (monitorIdToCheck) {
- await commonService.monitorIdExists(monitorIdToCheck);
- }
- await pageObjects.header.waitUntilLoadingHasFinished();
- }
-
- public async loadDataAndGoToMonitorPage(dateStart: string, dateEnd: string, monitorId: string) {
- await pageObjects.header.waitUntilLoadingHasFinished();
- await this.setDateRange(dateStart, dateEnd);
- await navigation.goToMonitor(monitorId);
- }
-
- public async inputFilterQuery(filterQuery: string) {
- await commonService.setFilterText(filterQuery);
- }
-
- public async pageHasDataMissing() {
- return await commonService.pageHasDataMissing();
- }
-
- public async pageHasExpectedIds(monitorIdsToCheck: string[]): Promise {
- return retry.tryForTime(15000, async () => {
- await Promise.all(monitorIdsToCheck.map((id) => commonService.monitorPageLinkExists(id)));
- });
- }
-
- public async pageUrlContains(value: string, expected: boolean = true): Promise {
- return retry.tryForTime(12000, async () => {
- expect(await commonService.urlContains(value)).to.eql(expected);
- });
- }
-
- public async changePage(direction: 'next' | 'prev') {
- if (direction === 'next') {
- await commonService.goToNextPage();
- } else if (direction === 'prev') {
- await commonService.goToPreviousPage();
- }
- }
-
- public async setStatusFilter(value: 'up' | 'down') {
- if (value === 'up') {
- await commonService.setStatusFilterUp();
- } else if (value === 'down') {
- await commonService.setStatusFilterDown();
- }
- }
-
- public async selectFilterItems(filters: Record) {
- for (const key in filters) {
- if (filters.hasOwnProperty(key)) {
- const values = filters[key];
- for (let i = 0; i < values.length; i++) {
- await commonService.selectFilterItem(key, values[i]);
- }
+import { FtrService } from '../ftr_provider_context';
+
+export class UptimePageObject extends FtrService {
+ private readonly timePicker = this.ctx.getPageObject('timePicker');
+ private readonly header = this.ctx.getPageObject('header');
+
+ private readonly commonService = this.ctx.getService('uptime').common;
+ private readonly monitor = this.ctx.getService('uptime').monitor;
+ private readonly navigation = this.ctx.getService('uptime').navigation;
+ private readonly retry = this.ctx.getService('retry');
+
+ public async goToRoot(refresh?: boolean) {
+ await this.navigation.goToUptime();
+ if (refresh) {
+ await this.navigation.refreshApp();
+ }
+ }
+
+ public async setDateRange(start: string, end: string) {
+ const { start: prevStart, end: prevEnd } = await this.timePicker.getTimeConfig();
+ if (start !== prevStart || prevEnd !== end) {
+ await this.timePicker.setAbsoluteRange(start, end);
+ } else {
+ await this.navigation.refreshApp();
+ }
+ }
+
+ public async goToUptimeOverviewAndLoadData(
+ dateStart: string,
+ dateEnd: string,
+ monitorIdToCheck?: string
+ ) {
+ await this.navigation.goToUptime();
+ await this.setDateRange(dateStart, dateEnd);
+ if (monitorIdToCheck) {
+ await this.commonService.monitorIdExists(monitorIdToCheck);
+ }
+ await this.header.waitUntilLoadingHasFinished();
+ }
+
+ public async loadDataAndGoToMonitorPage(dateStart: string, dateEnd: string, monitorId: string) {
+ await this.header.waitUntilLoadingHasFinished();
+ await this.setDateRange(dateStart, dateEnd);
+ await this.navigation.goToMonitor(monitorId);
+ }
+
+ public async inputFilterQuery(filterQuery: string) {
+ await this.commonService.setFilterText(filterQuery);
+ }
+
+ public async pageHasDataMissing() {
+ return await this.commonService.pageHasDataMissing();
+ }
+
+ public async pageHasExpectedIds(monitorIdsToCheck: string[]): Promise {
+ return this.retry.tryForTime(15000, async () => {
+ await Promise.all(
+ monitorIdsToCheck.map((id) => this.commonService.monitorPageLinkExists(id))
+ );
+ });
+ }
+
+ public async pageUrlContains(value: string, expected: boolean = true): Promise {
+ return this.retry.tryForTime(12000, async () => {
+ expect(await this.commonService.urlContains(value)).to.eql(expected);
+ });
+ }
+
+ public async changePage(direction: 'next' | 'prev') {
+ if (direction === 'next') {
+ await this.commonService.goToNextPage();
+ } else if (direction === 'prev') {
+ await this.commonService.goToPreviousPage();
+ }
+ }
+
+ public async setStatusFilter(value: 'up' | 'down') {
+ if (value === 'up') {
+ await this.commonService.setStatusFilterUp();
+ } else if (value === 'down') {
+ await this.commonService.setStatusFilterDown();
+ }
+ }
+
+ public async selectFilterItems(filters: Record) {
+ for (const key in filters) {
+ if (filters.hasOwnProperty(key)) {
+ const values = filters[key];
+ for (let i = 0; i < values.length; i++) {
+ await this.commonService.selectFilterItem(key, values[i]);
}
}
}
+ }
- public async getSnapshotCount() {
- return await commonService.getSnapshotCount();
- }
+ public async getSnapshotCount() {
+ return await this.commonService.getSnapshotCount();
+ }
- public async setAlertKueryBarText(filters: string) {
- const { setKueryBarText } = commonService;
- await setKueryBarText('xpack.uptime.alerts.monitorStatus.filterBar', filters);
- }
+ public async setAlertKueryBarText(filters: string) {
+ const { setKueryBarText } = this.commonService;
+ await setKueryBarText('xpack.uptime.alerts.monitorStatus.filterBar', filters);
+ }
- public async setMonitorListPageSize(size: number): Promise {
- await commonService.openPageSizeSelectPopover();
- return commonService.clickPageSizeSelectPopoverItem(size);
- }
+ public async setMonitorListPageSize(size: number): Promise {
+ await this.commonService.openPageSizeSelectPopover();
+ return this.commonService.clickPageSizeSelectPopoverItem(size);
+ }
- public async checkPingListInteractions(timestamps: string[]): Promise {
- return monitor.checkForPingListTimestamps(timestamps);
- }
+ public async checkPingListInteractions(timestamps: string[]): Promise {
+ return this.monitor.checkForPingListTimestamps(timestamps);
+ }
- public async resetFilters() {
- await this.inputFilterQuery('');
- await commonService.resetStatusFilter();
- }
- })();
+ public async resetFilters() {
+ await this.inputFilterQuery('');
+ await this.commonService.resetStatusFilter();
+ }
}
diff --git a/x-pack/test/functional/page_objects/watcher_page.ts b/x-pack/test/functional/page_objects/watcher_page.ts
index d0db5c8c3b267..5aeaddffff581 100644
--- a/x-pack/test/functional/page_objects/watcher_page.ts
+++ b/x-pack/test/functional/page_objects/watcher_page.ts
@@ -6,64 +6,61 @@
*/
import { map as mapAsync } from 'bluebird';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
-export function WatcherPageProvider({ getPageObjects, getService }: FtrProviderContext) {
- const PageObjects = getPageObjects(['header']);
- const find = getService('find');
- const testSubjects = getService('testSubjects');
+export class WatcherPageObject extends FtrService {
+ private readonly header = this.ctx.getPageObject('header');
+ private readonly find = this.ctx.getService('find');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
- class WatcherPage {
- async clearAllWatches() {
- const checkBoxExists = await testSubjects.exists('checkboxSelectAll');
- if (checkBoxExists) {
- await testSubjects.click('checkboxSelectAll');
- await testSubjects.click('btnDeleteWatches');
- await testSubjects.click('confirmModalConfirmButton');
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ async clearAllWatches() {
+ const checkBoxExists = await this.testSubjects.exists('checkboxSelectAll');
+ if (checkBoxExists) {
+ await this.testSubjects.click('checkboxSelectAll');
+ await this.testSubjects.click('btnDeleteWatches');
+ await this.testSubjects.click('confirmModalConfirmButton');
+ await this.header.waitUntilLoadingHasFinished();
}
+ }
- async createWatch(watchName: string, name: string) {
- await testSubjects.click('createWatchButton');
- await testSubjects.click('jsonWatchCreateLink');
- await find.setValue('#id', watchName);
- await find.setValue('#watchName', name);
- await find.clickByCssSelector('[type="submit"]');
- await PageObjects.header.waitUntilLoadingHasFinished();
- }
+ async createWatch(watchName: string, name: string) {
+ await this.testSubjects.click('createWatchButton');
+ await this.testSubjects.click('jsonWatchCreateLink');
+ await this.find.setValue('#id', watchName);
+ await this.find.setValue('#watchName', name);
+ await this.find.clickByCssSelector('[type="submit"]');
+ await this.header.waitUntilLoadingHasFinished();
+ }
- async getWatch(watchID: string) {
- const watchIdColumn = await testSubjects.find(`watchIdColumn-${watchID}`);
- const watchNameColumn = await testSubjects.find(`watchNameColumn-${watchID}`);
- const id = await watchIdColumn.getVisibleText();
- const name = await watchNameColumn.getVisibleText();
- return {
- id,
- name,
- };
- }
+ async getWatch(watchID: string) {
+ const watchIdColumn = await this.testSubjects.find(`watchIdColumn-${watchID}`);
+ const watchNameColumn = await this.testSubjects.find(`watchNameColumn-${watchID}`);
+ const id = await watchIdColumn.getVisibleText();
+ const name = await watchNameColumn.getVisibleText();
+ return {
+ id,
+ name,
+ };
+ }
- async deleteWatch() {
- await testSubjects.click('checkboxSelectAll');
- await testSubjects.click('btnDeleteWatches');
- }
+ async deleteWatch() {
+ await this.testSubjects.click('checkboxSelectAll');
+ await this.testSubjects.click('btnDeleteWatches');
+ }
- // get all the watches in the list
- async getWatches() {
- const watches = await find.allByCssSelector('.euiTableRow');
- return mapAsync(watches, async (watch) => {
- const checkBox = await watch.findByCssSelector('td:nth-child(1)');
- const id = await watch.findByCssSelector('td:nth-child(2)');
- const name = await watch.findByCssSelector('td:nth-child(3)');
+ // get all the watches in the list
+ async getWatches() {
+ const watches = await this.find.allByCssSelector('.euiTableRow');
+ return mapAsync(watches, async (watch) => {
+ const checkBox = await watch.findByCssSelector('td:nth-child(1)');
+ const id = await watch.findByCssSelector('td:nth-child(2)');
+ const name = await watch.findByCssSelector('td:nth-child(3)');
- return {
- checkBox: (await checkBox.getAttribute('innerHTML')).includes('input'),
- id: await id.getVisibleText(),
- name: (await name.getVisibleText()).split(',').map((role) => role.trim()),
- };
- });
- }
+ return {
+ checkBox: (await checkBox.getAttribute('innerHTML')).includes('input'),
+ id: await id.getVisibleText(),
+ name: (await name.getVisibleText()).split(',').map((role) => role.trim()),
+ };
+ });
}
- return new WatcherPage();
}
diff --git a/x-pack/test/functional/services/index.ts b/x-pack/test/functional/services/index.ts
index 0da518a5e8967..99293c71676b4 100644
--- a/x-pack/test/functional/services/index.ts
+++ b/x-pack/test/functional/services/index.ts
@@ -58,7 +58,7 @@ import {
DashboardDrilldownsManageProvider,
DashboardPanelTimeRangeProvider,
} from './dashboard';
-import { SearchSessionsProvider } from './search_sessions';
+import { SearchSessionsService } from './search_sessions';
// define the name and providers for services that should be
// available to your tests. If you don't specify anything here
@@ -107,5 +107,5 @@ export const services = {
dashboardDrilldownPanelActions: DashboardDrilldownPanelActionsProvider,
dashboardDrilldownsManage: DashboardDrilldownsManageProvider,
dashboardPanelTimeRange: DashboardPanelTimeRangeProvider,
- searchSessions: SearchSessionsProvider,
+ searchSessions: SearchSessionsService,
};
diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts
index b61bc8871482f..31cf17575bdd9 100644
--- a/x-pack/test/functional/services/ml/common_ui.ts
+++ b/x-pack/test/functional/services/ml/common_ui.ts
@@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
import type { CanvasElementColorStats } from '../canvas_element';
-interface SetValueOptions {
+export interface SetValueOptions {
clearWithKeyboard?: boolean;
typeCharByChar?: boolean;
}
diff --git a/x-pack/test/functional/services/ml/job_wizard_common.ts b/x-pack/test/functional/services/ml/job_wizard_common.ts
index 2990f70059767..7df09652f8953 100644
--- a/x-pack/test/functional/services/ml/job_wizard_common.ts
+++ b/x-pack/test/functional/services/ml/job_wizard_common.ts
@@ -11,6 +11,10 @@ import { FtrProviderContext } from '../../ftr_provider_context';
import { MlCommonUI } from './common_ui';
import { MlCustomUrls } from './custom_urls';
+export interface SectionOptions {
+ withAdvancedSection: boolean;
+}
+
export function MachineLearningJobWizardCommonProvider(
{ getService }: FtrProviderContext,
mlCommonUI: MlCommonUI,
@@ -20,10 +24,6 @@ export function MachineLearningJobWizardCommonProvider(
const retry = getService('retry');
const testSubjects = getService('testSubjects');
- interface SectionOptions {
- withAdvancedSection: boolean;
- }
-
function advancedSectionSelector(subSelector?: string) {
const subj = 'mlJobWizardAdvancedSection';
return !subSelector ? subj : `${subj} > ${subSelector}`;
diff --git a/x-pack/test/functional/services/search_sessions.ts b/x-pack/test/functional/services/search_sessions.ts
index 63dc880fda380..4490b87006833 100644
--- a/x-pack/test/functional/services/search_sessions.ts
+++ b/x-pack/test/functional/services/search_sessions.ts
@@ -8,7 +8,7 @@
import expect from '@kbn/expect';
import { SavedObjectsFindResponse } from 'src/core/server';
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
const SEARCH_SESSION_INDICATOR_TEST_SUBJ = 'searchSessionIndicator';
const SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ = 'searchSessionIndicatorPopoverContainer';
@@ -25,155 +25,153 @@ type SessionStateType =
| 'restored'
| 'canceled';
-export function SearchSessionsProvider({ getService }: FtrProviderContext) {
- const testSubjects = getService('testSubjects');
- const log = getService('log');
- const retry = getService('retry');
- const browser = getService('browser');
- const supertest = getService('supertest');
-
- return new (class SearchSessionsService {
- public async find(): Promise {
- return testSubjects.find(SEARCH_SESSION_INDICATOR_TEST_SUBJ);
- }
-
- public async exists(): Promise {
- return testSubjects.exists(SEARCH_SESSION_INDICATOR_TEST_SUBJ);
- }
-
- public async missingOrFail(): Promise {
- return testSubjects.missingOrFail(SEARCH_SESSION_INDICATOR_TEST_SUBJ);
- }
-
- public async disabledOrFail() {
- await this.exists();
- await expect(await (await this.find()).getAttribute('data-save-disabled')).to.be('true');
- }
-
- public async expectState(state: SessionStateType) {
- return retry.waitFor(`searchSessions indicator to get into state = ${state}`, async () => {
- const currentState = await (
- await testSubjects.find(SEARCH_SESSION_INDICATOR_TEST_SUBJ)
- ).getAttribute('data-state');
- log.info(`searchSessions state current: ${currentState} expected: ${state}`);
- return currentState === state;
+export class SearchSessionsService extends FtrService {
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly log = this.ctx.getService('log');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly browser = this.ctx.getService('browser');
+ private readonly supertest = this.ctx.getService('supertest');
+
+ public async find(): Promise {
+ return this.testSubjects.find(SEARCH_SESSION_INDICATOR_TEST_SUBJ);
+ }
+
+ public async exists(): Promise {
+ return this.testSubjects.exists(SEARCH_SESSION_INDICATOR_TEST_SUBJ);
+ }
+
+ public async missingOrFail(): Promise {
+ return this.testSubjects.missingOrFail(SEARCH_SESSION_INDICATOR_TEST_SUBJ);
+ }
+
+ public async disabledOrFail() {
+ await this.exists();
+ await expect(await (await this.find()).getAttribute('data-save-disabled')).to.be('true');
+ }
+
+ public async expectState(state: SessionStateType) {
+ return this.retry.waitFor(`searchSessions indicator to get into state = ${state}`, async () => {
+ const currentState = await (
+ await this.testSubjects.find(SEARCH_SESSION_INDICATOR_TEST_SUBJ)
+ ).getAttribute('data-state');
+ this.log.info(`searchSessions state current: ${currentState} expected: ${state}`);
+ return currentState === state;
+ });
+ }
+
+ public async viewSearchSessions() {
+ this.log.debug('viewSearchSessions');
+ await this.ensurePopoverOpened();
+ await this.testSubjects.click('searchSessionIndicatorViewSearchSessionsLink');
+ }
+
+ public async save({ searchSessionName }: { searchSessionName?: string } = {}) {
+ this.log.debug('save the search session');
+ await this.ensurePopoverOpened();
+ await this.testSubjects.click('searchSessionIndicatorSaveBtn');
+
+ if (searchSessionName) {
+ await this.testSubjects.click('searchSessionNameEdit');
+ await this.testSubjects.setValue('searchSessionNameInput', searchSessionName, {
+ clearWithKeyboard: true,
});
- }
-
- public async viewSearchSessions() {
- log.debug('viewSearchSessions');
- await this.ensurePopoverOpened();
- await testSubjects.click('searchSessionIndicatorViewSearchSessionsLink');
- }
-
- public async save({ searchSessionName }: { searchSessionName?: string } = {}) {
- log.debug('save the search session');
- await this.ensurePopoverOpened();
- await testSubjects.click('searchSessionIndicatorSaveBtn');
-
- if (searchSessionName) {
- await testSubjects.click('searchSessionNameEdit');
- await testSubjects.setValue('searchSessionNameInput', searchSessionName, {
- clearWithKeyboard: true,
- });
- await testSubjects.click('searchSessionNameSave');
+ await this.testSubjects.click('searchSessionNameSave');
+ }
+
+ await this.ensurePopoverClosed();
+ }
+
+ public async cancel() {
+ this.log.debug('cancel the search session');
+ await this.ensurePopoverOpened();
+ await this.testSubjects.click('searchSessionIndicatorCancelBtn');
+ await this.ensurePopoverClosed();
+ }
+
+ public async openPopover() {
+ await this.ensurePopoverOpened();
+ }
+
+ public async openedOrFail() {
+ return this.testSubjects.existOrFail(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ, {
+ timeout: 15000, // because popover auto opens after search takes 10s
+ });
+ }
+
+ public async closedOrFail() {
+ return this.testSubjects.missingOrFail(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ, {
+ timeout: 15000, // because popover auto opens after search takes 10s
+ });
+ }
+
+ private async ensurePopoverOpened() {
+ this.log.debug('ensurePopoverOpened');
+ const isAlreadyOpen = await this.testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ);
+ if (isAlreadyOpen) {
+ this.log.debug('Popover is already open');
+ return;
+ }
+ return this.retry.waitFor(`searchSessions popover opened`, async () => {
+ await this.testSubjects.click(SEARCH_SESSION_INDICATOR_TEST_SUBJ);
+ return await this.testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ);
+ });
+ }
+
+ private async ensurePopoverClosed() {
+ this.log.debug('ensurePopoverClosed');
+ const isAlreadyClosed = !(await this.testSubjects.exists(
+ SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ
+ ));
+ if (isAlreadyClosed) return;
+ return this.retry.waitFor(`searchSessions popover closed`, async () => {
+ await this.browser.pressKeys(this.browser.keys.ESCAPE);
+ return !(await this.testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ));
+ });
+ }
+
+ /*
+ * This cleanup function should be used by tests that create new search sessions.
+ * Tests should not end with new search sessions remaining in storage since that interferes with functional tests that check the _find API.
+ * Alternatively, a test can navigate to `Management > Search Sessions` and use the UI to delete any created tests.
+ */
+ public async deleteAllSearchSessions() {
+ this.log.debug('Deleting created search sessions');
+ // ignores 409 errs and keeps retrying
+ await this.retry.tryForTime(10000, async () => {
+ const { body } = await this.supertest
+ .post('/internal/session/_find')
+ .set('kbn-xsrf', 'anything')
+ .set('kbn-system-request', 'true')
+ .send({ page: 1, perPage: 10000, sortField: 'created', sortOrder: 'asc' })
+ .expect(200);
+
+ const { saved_objects: savedObjects } = body as SavedObjectsFindResponse;
+ if (savedObjects.length) {
+ this.log.debug(`Found created search sessions: ${savedObjects.map(({ id }) => id)}`);
}
-
- await this.ensurePopoverClosed();
- }
-
- public async cancel() {
- log.debug('cancel the search session');
- await this.ensurePopoverOpened();
- await testSubjects.click('searchSessionIndicatorCancelBtn');
- await this.ensurePopoverClosed();
- }
-
- public async openPopover() {
- await this.ensurePopoverOpened();
- }
-
- public async openedOrFail() {
- return testSubjects.existOrFail(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ, {
- timeout: 15000, // because popover auto opens after search takes 10s
- });
- }
-
- public async closedOrFail() {
- return testSubjects.missingOrFail(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ, {
- timeout: 15000, // because popover auto opens after search takes 10s
- });
- }
-
- private async ensurePopoverOpened() {
- log.debug('ensurePopoverOpened');
- const isAlreadyOpen = await testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ);
- if (isAlreadyOpen) {
- log.debug('Popover is already open');
- return;
- }
- return retry.waitFor(`searchSessions popover opened`, async () => {
- await testSubjects.click(SEARCH_SESSION_INDICATOR_TEST_SUBJ);
- return await testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ);
- });
- }
-
- private async ensurePopoverClosed() {
- log.debug('ensurePopoverClosed');
- const isAlreadyClosed = !(await testSubjects.exists(
- SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ
- ));
- if (isAlreadyClosed) return;
- return retry.waitFor(`searchSessions popover closed`, async () => {
- await browser.pressKeys(browser.keys.ESCAPE);
- return !(await testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ));
- });
- }
-
- /*
- * This cleanup function should be used by tests that create new search sessions.
- * Tests should not end with new search sessions remaining in storage since that interferes with functional tests that check the _find API.
- * Alternatively, a test can navigate to `Management > Search Sessions` and use the UI to delete any created tests.
- */
- public async deleteAllSearchSessions() {
- log.debug('Deleting created search sessions');
- // ignores 409 errs and keeps retrying
- await retry.tryForTime(10000, async () => {
- const { body } = await supertest
- .post('/internal/session/_find')
- .set('kbn-xsrf', 'anything')
- .set('kbn-system-request', 'true')
- .send({ page: 1, perPage: 10000, sortField: 'created', sortOrder: 'asc' })
- .expect(200);
-
- const { saved_objects: savedObjects } = body as SavedObjectsFindResponse;
- if (savedObjects.length) {
- log.debug(`Found created search sessions: ${savedObjects.map(({ id }) => id)}`);
- }
- await Promise.all(
- savedObjects.map(async (so) => {
- log.debug(`Deleting search session: ${so.id}`);
- await supertest
- .delete(`/internal/session/${so.id}`)
- .set(`kbn-xsrf`, `anything`)
- .expect(200);
- })
- );
- });
- }
-
- public async markTourDone() {
- await Promise.all([
- browser.setLocalStorageItem(TOUR_TAKING_TOO_LONG_STEP_KEY, 'true'),
- browser.setLocalStorageItem(TOUR_RESTORE_STEP_KEY, 'true'),
- ]);
- }
-
- public async markTourUndone() {
- await Promise.all([
- browser.removeLocalStorageItem(TOUR_TAKING_TOO_LONG_STEP_KEY),
- browser.removeLocalStorageItem(TOUR_RESTORE_STEP_KEY),
- ]);
- }
- })();
+ await Promise.all(
+ savedObjects.map(async (so) => {
+ this.log.debug(`Deleting search session: ${so.id}`);
+ await this.supertest
+ .delete(`/internal/session/${so.id}`)
+ .set(`kbn-xsrf`, `anything`)
+ .expect(200);
+ })
+ );
+ });
+ }
+
+ public async markTourDone() {
+ await Promise.all([
+ this.browser.setLocalStorageItem(TOUR_TAKING_TOO_LONG_STEP_KEY, 'true'),
+ this.browser.setLocalStorageItem(TOUR_RESTORE_STEP_KEY, 'true'),
+ ]);
+ }
+
+ public async markTourUndone() {
+ await Promise.all([
+ this.browser.removeLocalStorageItem(TOUR_TAKING_TOO_LONG_STEP_KEY),
+ this.browser.removeLocalStorageItem(TOUR_RESTORE_STEP_KEY),
+ ]);
+ }
}
diff --git a/x-pack/test/reporting_api_integration/services/usage.ts b/x-pack/test/reporting_api_integration/services/usage.ts
index ababbbf03e4c1..4e8d464467191 100644
--- a/x-pack/test/reporting_api_integration/services/usage.ts
+++ b/x-pack/test/reporting_api_integration/services/usage.ts
@@ -31,7 +31,7 @@ export interface ReportingUsageStats {
[jobType: string]: any;
}
-interface UsageStats {
+export interface UsageStats {
reporting: ReportingUsageStats;
}
diff --git a/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_utils.ts b/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_utils.ts
index 088e594a41b88..b712c2882ee0f 100644
--- a/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_utils.ts
+++ b/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_utils.ts
@@ -191,6 +191,64 @@ export const expectResponses = {
},
};
+const commonUsers = {
+ noAccess: {
+ ...NOT_A_KIBANA_USER,
+ description: 'user with no access',
+ authorizedAtSpaces: [],
+ },
+ superuser: {
+ ...SUPERUSER,
+ description: 'superuser',
+ authorizedAtSpaces: ['*'],
+ },
+ legacyAll: { ...KIBANA_LEGACY_USER, description: 'legacy user', authorizedAtSpaces: [] },
+ allGlobally: {
+ ...KIBANA_RBAC_USER,
+ description: 'rbac user with all globally',
+ authorizedAtSpaces: ['*'],
+ },
+ readGlobally: {
+ ...KIBANA_RBAC_DASHBOARD_ONLY_USER,
+ description: 'rbac user with read globally',
+ authorizedAtSpaces: ['*'],
+ },
+ dualAll: {
+ ...KIBANA_DUAL_PRIVILEGES_USER,
+ description: 'dual-privileges user',
+ authorizedAtSpaces: ['*'],
+ },
+ dualRead: {
+ ...KIBANA_DUAL_PRIVILEGES_DASHBOARD_ONLY_USER,
+ description: 'dual-privileges readonly user',
+ authorizedAtSpaces: ['*'],
+ },
+};
+
+interface Security {
+ modifier?: T;
+ users: Record<
+ | keyof typeof commonUsers
+ | 'allAtDefaultSpace'
+ | 'readAtDefaultSpace'
+ | 'allAtSpace1'
+ | 'readAtSpace1',
+ TestUser
+ >;
+}
+interface SecurityAndSpaces {
+ modifier?: T;
+ users: Record<
+ keyof typeof commonUsers | 'allAtSpace' | 'readAtSpace' | 'allAtOtherSpace',
+ TestUser
+ >;
+ spaceId: string;
+}
+interface Spaces {
+ modifier?: T;
+ spaceId: string;
+}
+
/**
* Get test scenarios for each type of suite.
* @param modifier Use this to generate additional permutations of test scenarios.
@@ -203,66 +261,10 @@ export const expectResponses = {
* ]
*/
export const getTestScenarios = (modifiers?: T[]) => {
- const commonUsers = {
- noAccess: {
- ...NOT_A_KIBANA_USER,
- description: 'user with no access',
- authorizedAtSpaces: [],
- },
- superuser: {
- ...SUPERUSER,
- description: 'superuser',
- authorizedAtSpaces: ['*'],
- },
- legacyAll: { ...KIBANA_LEGACY_USER, description: 'legacy user', authorizedAtSpaces: [] },
- allGlobally: {
- ...KIBANA_RBAC_USER,
- description: 'rbac user with all globally',
- authorizedAtSpaces: ['*'],
- },
- readGlobally: {
- ...KIBANA_RBAC_DASHBOARD_ONLY_USER,
- description: 'rbac user with read globally',
- authorizedAtSpaces: ['*'],
- },
- dualAll: {
- ...KIBANA_DUAL_PRIVILEGES_USER,
- description: 'dual-privileges user',
- authorizedAtSpaces: ['*'],
- },
- dualRead: {
- ...KIBANA_DUAL_PRIVILEGES_DASHBOARD_ONLY_USER,
- description: 'dual-privileges readonly user',
- authorizedAtSpaces: ['*'],
- },
- };
-
- interface Security {
- modifier?: T;
- users: Record<
- | keyof typeof commonUsers
- | 'allAtDefaultSpace'
- | 'readAtDefaultSpace'
- | 'allAtSpace1'
- | 'readAtSpace1',
- TestUser
- >;
- }
- interface SecurityAndSpaces {
- modifier?: T;
- users: Record<
- keyof typeof commonUsers | 'allAtSpace' | 'readAtSpace' | 'allAtOtherSpace',
- TestUser
- >;
- spaceId: string;
- }
- interface Spaces {
- modifier?: T;
- spaceId: string;
- }
-
- let spaces: Spaces[] = [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID].map((x) => ({ spaceId: x }));
- let security: Security[] = [
+ let spaces: Array> = [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID].map((x) => ({
+ spaceId: x,
+ }));
+ let security: Array> = [
{
users: {
...commonUsers,
@@ -289,7 +291,7 @@ export const getTestScenarios = (modifiers?: T[]) => {
},
},
];
- let securityAndSpaces: SecurityAndSpaces[] = [
+ let securityAndSpaces: Array> = [
{
spaceId: DEFAULT_SPACE_ID,
users: {
diff --git a/x-pack/test/security_solution_ftr/page_objects/detections/index.ts b/x-pack/test/security_solution_ftr/page_objects/detections/index.ts
index dd17548df6e3f..6f26e0b3ad364 100644
--- a/x-pack/test/security_solution_ftr/page_objects/detections/index.ts
+++ b/x-pack/test/security_solution_ftr/page_objects/detections/index.ts
@@ -5,145 +5,144 @@
* 2.0.
*/
-import { FtrProviderContext } from '../../../functional/ftr_provider_context';
+import { FtrService } from '../../../functional/ftr_provider_context';
import { WebElementWrapper } from '../../../../../test/functional/services/lib/web_element_wrapper';
-export function DetectionsPageProvider({ getService, getPageObjects }: FtrProviderContext) {
- const find = getService('find');
- const { common } = getPageObjects(['common']);
- const testSubjects = getService('testSubjects');
-
- class DetectionsPage {
- async navigateHome(): Promise {
- await this.navigateToDetectionsPage();
- }
-
- async navigateToRules(): Promise {
- await this.navigateToDetectionsPage('rules');
- }
-
- async navigateToRuleMonitoring(): Promise {
- await common.clickAndValidate('allRulesTableTab-monitoring', 'monitoring-table');
- }
-
- async navigateToExceptionList(): Promise {
- await common.clickAndValidate('allRulesTableTab-exceptions', 'exceptions-table');
- }
-
- async navigateToCreateRule(): Promise {
- await this.navigateToDetectionsPage('rules/create');
- }
-
- async replaceIndexPattern(): Promise {
- const buttons = await find.allByCssSelector('[data-test-subj="comboBoxInput"] button');
- await buttons.map(async (button: WebElementWrapper) => await button.click());
- await testSubjects.setValue('comboBoxSearchInput', '*');
- }
-
- async openImportQueryModal(): Promise {
- const element = await testSubjects.find('importQueryFromSavedTimeline');
- await element.click(500);
- await testSubjects.exists('open-timeline-modal-body-filter-default');
- }
-
- async viewTemplatesInImportQueryModal(): Promise {
- await common.clickAndValidate('open-timeline-modal-body-filter-template', 'timelines-table');
- }
-
- async closeImportQueryModal(): Promise {
- await find.clickByCssSelector('.euiButtonIcon.euiButtonIcon--text.euiModal__closeIcon');
- }
-
- async selectMachineLearningJob(): Promise {
- await find.clickByCssSelector('[data-test-subj="mlJobSelect"] button');
- await find.clickByCssSelector('#high_distinct_count_error_message');
- }
-
- async openAddFilterPopover(): Promise {
- const addButtons = await testSubjects.findAll('addFilter');
- await addButtons[1].click();
- await testSubjects.exists('saveFilter');
- }
-
- async closeAddFilterPopover(): Promise {
- await testSubjects.click('cancelSaveFilter');
- }
-
- async toggleFilterActions(): Promise {
- const filterActions = await testSubjects.findAll('addFilter');
- await filterActions[1].click();
- }
-
- async toggleSavedQueries(): Promise {
- const filterActions = await find.allByCssSelector(
- '[data-test-subj="saved-query-management-popover-button"]'
- );
- await filterActions[1].click();
- }
-
- async addNameAndDescription(
- name: string = 'test rule name',
- description: string = 'test rule description'
- ): Promise {
- await find.setValue(`[aria-describedby="detectionEngineStepAboutRuleName"]`, name, 500);
- await find.setValue(
- `[aria-describedby="detectionEngineStepAboutRuleDescription"]`,
- description,
- 500
- );
- }
-
- async goBackToAllRules(): Promise {
- await common.clickAndValidate('ruleDetailsBackToAllRules', 'create-new-rule');
- }
-
- async revealAdvancedSettings(): Promise {
- await common.clickAndValidate(
- 'advancedSettings',
- 'detectionEngineStepAboutRuleReferenceUrls'
- );
- }
-
- async preview(): Promise {
- await common.clickAndValidate(
- 'queryPreviewButton',
- 'queryPreviewCustomHistogram',
- undefined,
- 500
- );
- }
-
- async continue(prefix: string): Promise {
- await testSubjects.click(`${prefix}-continue`);
- }
-
- async addCustomQuery(query: string): Promise {
- await testSubjects.setValue('queryInput', query, undefined, 500);
- }
-
- async selectMLRule(): Promise {
- await common.clickAndValidate('machineLearningRuleType', 'mlJobSelect');
- }
-
- async selectEQLRule(): Promise {
- await common.clickAndValidate('eqlRuleType', 'eqlQueryBarTextInput');
- }
-
- async selectIndicatorMatchRule(): Promise {
- await common.clickAndValidate('threatMatchRuleType', 'comboBoxInput');
- }
-
- async selectThresholdRule(): Promise {
- await common.clickAndValidate('thresholdRuleType', 'input');
- }
-
- private async navigateToDetectionsPage(path: string = ''): Promise {
- const subUrl = `detections${path ? `/${path}` : ''}`;
- await common.navigateToUrl('securitySolution', subUrl, {
- shouldUseHashForSubUrl: false,
- });
- }
- }
-
- return new DetectionsPage();
+export class DetectionsPageObject extends FtrService {
+ private readonly find = this.ctx.getService('find');
+ private readonly common = this.ctx.getPageObject('common');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+
+ async navigateHome(): Promise {
+ await this.navigateToDetectionsPage();
+ }
+
+ async navigateToRules(): Promise {
+ await this.navigateToDetectionsPage('rules');
+ }
+
+ async navigateToRuleMonitoring(): Promise {
+ await this.common.clickAndValidate('allRulesTableTab-monitoring', 'monitoring-table');
+ }
+
+ async navigateToExceptionList(): Promise {
+ await this.common.clickAndValidate('allRulesTableTab-exceptions', 'exceptions-table');
+ }
+
+ async navigateToCreateRule(): Promise {
+ await this.navigateToDetectionsPage('rules/create');
+ }
+
+ async replaceIndexPattern(): Promise {
+ const buttons = await this.find.allByCssSelector('[data-test-subj="comboBoxInput"] button');
+ await buttons.map(async (button: WebElementWrapper) => await button.click());
+ await this.testSubjects.setValue('comboBoxSearchInput', '*');
+ }
+
+ async openImportQueryModal(): Promise {
+ const element = await this.testSubjects.find('importQueryFromSavedTimeline');
+ await element.click(500);
+ await this.testSubjects.exists('open-timeline-modal-body-filter-default');
+ }
+
+ async viewTemplatesInImportQueryModal(): Promise {
+ await this.common.clickAndValidate(
+ 'open-timeline-modal-body-filter-template',
+ 'timelines-table'
+ );
+ }
+
+ async closeImportQueryModal(): Promise {
+ await this.find.clickByCssSelector('.euiButtonIcon.euiButtonIcon--text.euiModal__closeIcon');
+ }
+
+ async selectMachineLearningJob(): Promise {
+ await this.find.clickByCssSelector('[data-test-subj="mlJobSelect"] button');
+ await this.find.clickByCssSelector('#high_distinct_count_error_message');
+ }
+
+ async openAddFilterPopover(): Promise {
+ const addButtons = await this.testSubjects.findAll('addFilter');
+ await addButtons[1].click();
+ await this.testSubjects.exists('saveFilter');
+ }
+
+ async closeAddFilterPopover(): Promise {
+ await this.testSubjects.click('cancelSaveFilter');
+ }
+
+ async toggleFilterActions(): Promise {
+ const filterActions = await this.testSubjects.findAll('addFilter');
+ await filterActions[1].click();
+ }
+
+ async toggleSavedQueries(): Promise {
+ const filterActions = await this.find.allByCssSelector(
+ '[data-test-subj="saved-query-management-popover-button"]'
+ );
+ await filterActions[1].click();
+ }
+
+ async addNameAndDescription(
+ name: string = 'test rule name',
+ description: string = 'test rule description'
+ ): Promise {
+ await this.find.setValue(`[aria-describedby="detectionEngineStepAboutRuleName"]`, name, 500);
+ await this.find.setValue(
+ `[aria-describedby="detectionEngineStepAboutRuleDescription"]`,
+ description,
+ 500
+ );
+ }
+
+ async goBackToAllRules(): Promise {
+ await this.common.clickAndValidate('ruleDetailsBackToAllRules', 'create-new-rule');
+ }
+
+ async revealAdvancedSettings(): Promise {
+ await this.common.clickAndValidate(
+ 'advancedSettings',
+ 'detectionEngineStepAboutRuleReferenceUrls'
+ );
+ }
+
+ async preview(): Promise {
+ await this.common.clickAndValidate(
+ 'queryPreviewButton',
+ 'queryPreviewCustomHistogram',
+ undefined,
+ 500
+ );
+ }
+
+ async continue(prefix: string): Promise {
+ await this.testSubjects.click(`${prefix}-continue`);
+ }
+
+ async addCustomQuery(query: string): Promise {
+ await this.testSubjects.setValue('queryInput', query, undefined, 500);
+ }
+
+ async selectMLRule(): Promise {
+ await this.common.clickAndValidate('machineLearningRuleType', 'mlJobSelect');
+ }
+
+ async selectEQLRule(): Promise {
+ await this.common.clickAndValidate('eqlRuleType', 'eqlQueryBarTextInput');
+ }
+
+ async selectIndicatorMatchRule(): Promise {
+ await this.common.clickAndValidate('threatMatchRuleType', 'comboBoxInput');
+ }
+
+ async selectThresholdRule(): Promise {
+ await this.common.clickAndValidate('thresholdRuleType', 'input');
+ }
+
+ private async navigateToDetectionsPage(path: string = ''): Promise {
+ const subUrl = `detections${path ? `/${path}` : ''}`;
+ await this.common.navigateToUrl('securitySolution', subUrl, {
+ shouldUseHashForSubUrl: false,
+ });
+ }
}
diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json
index 7c95a53b75ee7..0424891064cd3 100644
--- a/x-pack/test/tsconfig.json
+++ b/x-pack/test/tsconfig.json
@@ -1,12 +1,24 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- // overhead is too significant
- "incremental": false,
+ "composite": true,
+ "outDir": "./target/types",
+ "emitDeclarationOnly": true,
+ "declaration": true,
+ "declarationMap": true,
"types": ["node"]
},
- "include": ["**/*", "../../typings/**/*", "../../packages/kbn-test/types/ftr_globals/**/*"],
+ "include": [
+ "**/*",
+ "./api_integration/apis/logstash/pipeline/fixtures/*.json",
+ "./api_integration/apis/logstash/pipelines/fixtures/*.json",
+ "./api_integration/apis/telemetry/fixtures/*.json",
+ "../../typings/**/*",
+ "../../packages/kbn-test/types/ftr_globals/**/*",
+ ],
+ "exclude": ["target/**/*"],
"references": [
+ { "path": "../../test/tsconfig.json" },
{ "path": "../../src/core/tsconfig.json" },
{ "path": "../../src/plugins/bfetch/tsconfig.json" },
{ "path": "../../src/plugins/charts/tsconfig.json" },
@@ -84,6 +96,7 @@
{ "path": "../plugins/stack_alerts/tsconfig.json" },
{ "path": "../plugins/task_manager/tsconfig.json" },
{ "path": "../plugins/telemetry_collection_xpack/tsconfig.json" },
+ { "path": "../plugins/timelines/tsconfig.json" },
{ "path": "../plugins/transform/tsconfig.json" },
{ "path": "../plugins/triggers_actions_ui/tsconfig.json" },
{ "path": "../plugins/ui_actions_enhanced/tsconfig.json" },