From 31778b31c272e0e184b53480515dee5f6d42c94a Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 25 May 2021 13:59:21 -0700
Subject: [PATCH 01/17] Update dependency @elastic/charts to v29.2.0 (#100587)
Co-authored-by: Renovate Bot
---
package.json | 2 +-
yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index 73f3e5585faf7..3cdde5e52584a 100644
--- a/package.json
+++ b/package.json
@@ -98,7 +98,7 @@
"dependencies": {
"@elastic/apm-rum": "^5.6.1",
"@elastic/apm-rum-react": "^1.2.5",
- "@elastic/charts": "29.1.0",
+ "@elastic/charts": "29.2.0",
"@elastic/datemath": "link:bazel-bin/packages/elastic-datemath/npm_module",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.4",
"@elastic/ems-client": "7.13.0",
diff --git a/yarn.lock b/yarn.lock
index 9967cedea9fde..1f09ede5e7900 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1366,10 +1366,10 @@
dependencies:
object-hash "^1.3.0"
-"@elastic/charts@29.1.0":
- version "29.1.0"
- resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-29.1.0.tgz#2850aa30d5e00aa8a1ab4974ea36f3c960a8e457"
- integrity sha512-/nHT8niLtvSwX3dyEeIQWXEEZrB3xgjLIdlnqZhQXEdHqDQnxlehOMsTqWWws7jS/5uRq/sg+8N2z1xEb+odDw==
+"@elastic/charts@29.2.0":
+ version "29.2.0"
+ resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-29.2.0.tgz#cd65887c0ca1d420493aee1b570c862ca0df5311"
+ integrity sha512-gj3Gew9zy8XPNEkAAznOjncO5AF63jy/X1k1VIcNPqdqMi07YYCZwCQjMzUVoS4RN6X4GSzxhrYfGAeyZP8gqg==
dependencies:
"@popperjs/core" "^2.4.0"
chroma-js "^2.1.0"
From fea499c8ab651a6dfc7f38633651670287771dff Mon Sep 17 00:00:00 2001
From: Tiago Costa
Date: Wed, 26 May 2021 00:58:43 +0100
Subject: [PATCH 02/17] refact(NA): remove extra pkg_npm target and add
specific target folders for @kbn/analytics on Bazel (#100569)
* refact(NA): remove extra pkg_npm target and add specific target folders on @kbn/analytics
* chore(NA): update import on target_types
---
packages/kbn-analytics/BUILD.bazel | 30 +++----------------
packages/kbn-analytics/package.json | 6 ++--
packages/kbn-analytics/tsconfig.browser.json | 2 +-
packages/kbn-analytics/tsconfig.json | 4 +--
.../apis/ui_metric/ui_metric.ts | 2 +-
5 files changed, 11 insertions(+), 33 deletions(-)
diff --git a/packages/kbn-analytics/BUILD.bazel b/packages/kbn-analytics/BUILD.bazel
index 1749a3dcdc816..9eeaf4dc5842e 100644
--- a/packages/kbn-analytics/BUILD.bazel
+++ b/packages/kbn-analytics/BUILD.bazel
@@ -56,10 +56,10 @@ ts_project(
srcs = SRCS,
deps = DEPS,
declaration = True,
- declaration_dir = "types",
+ declaration_dir = "target_types",
declaration_map = True,
incremental = True,
- out_dir = "node",
+ out_dir = "target_node",
source_map = True,
root_dir = "src",
tsconfig = ":tsconfig",
@@ -72,38 +72,16 @@ ts_project(
deps = DEPS,
declaration = False,
incremental = True,
- out_dir = "web",
+ out_dir = "target_web",
source_map = True,
root_dir = "src",
tsconfig = ":tsconfig_browser",
)
-filegroup(
- name = "tsc_types",
- srcs = [":tsc"],
- output_group = "types",
-)
-
-filegroup(
- name = "target_files",
- srcs = [
- ":tsc",
- ":tsc_browser",
- ":tsc_types",
- ],
-)
-
-pkg_npm(
- name = "target",
- deps = [
- ":target_files",
- ],
-)
-
js_library(
name = PKG_BASE_NAME,
srcs = NPM_MODULE_EXTRA_FILES,
- deps = DEPS + [":target"],
+ deps = DEPS + [":tsc", ":tsc_browser"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
diff --git a/packages/kbn-analytics/package.json b/packages/kbn-analytics/package.json
index 726b62e1c61b8..177c0eb815760 100644
--- a/packages/kbn-analytics/package.json
+++ b/packages/kbn-analytics/package.json
@@ -3,9 +3,9 @@
"private": true,
"version": "1.0.0",
"description": "Kibana Analytics tool",
- "main": "target/node/index.js",
- "types": "target/types/index.d.ts",
- "browser": "target/web/index.js",
+ "main": "target_node/index.js",
+ "types": "target_types/index.d.ts",
+ "browser": "target_web/index.js",
"author": "Ahmad Bamieh ",
"license": "SSPL-1.0 OR Elastic License 2.0"
}
\ No newline at end of file
diff --git a/packages/kbn-analytics/tsconfig.browser.json b/packages/kbn-analytics/tsconfig.browser.json
index 12f70b77008e7..8a65c551d3bc4 100644
--- a/packages/kbn-analytics/tsconfig.browser.json
+++ b/packages/kbn-analytics/tsconfig.browser.json
@@ -2,7 +2,7 @@
"extends": "../../tsconfig.browser.json",
"compilerOptions": {
"incremental": true,
- "outDir": "./target/web",
+ "outDir": "./target_web",
"stripInternal": true,
"declaration": false,
"isolatedModules": true,
diff --git a/packages/kbn-analytics/tsconfig.json b/packages/kbn-analytics/tsconfig.json
index 165a8b695db57..2eaa88cd3ce5f 100644
--- a/packages/kbn-analytics/tsconfig.json
+++ b/packages/kbn-analytics/tsconfig.json
@@ -2,8 +2,8 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"incremental": true,
- "declarationDir": "./target/types",
- "outDir": "./target/node",
+ "declarationDir": "./target_types",
+ "outDir": "./target_node",
"stripInternal": true,
"declaration": true,
"declarationMap": true,
diff --git a/test/api_integration/apis/ui_metric/ui_metric.ts b/test/api_integration/apis/ui_metric/ui_metric.ts
index 47d10da9a1b29..3f0a4c0778911 100644
--- a/test/api_integration/apis/ui_metric/ui_metric.ts
+++ b/test/api_integration/apis/ui_metric/ui_metric.ts
@@ -8,7 +8,7 @@
import expect from '@kbn/expect';
import { ReportManager, METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics';
-import { UserAgentMetric } from '@kbn/analytics/target/types/metrics/user_agent';
+import { UserAgentMetric } from '@kbn/analytics/target_types/metrics/user_agent';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
From a591af2856e78ca0d1f3a66a99fdcb68ba0c41f7 Mon Sep 17 00:00:00 2001
From: Jonathan Budzenski
Date: Tue, 25 May 2021 20:33:43 -0500
Subject: [PATCH 03/17] [build] Clean jest configs (#100594)
---
src/dev/build/tasks/clean_tasks.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/dev/build/tasks/clean_tasks.ts b/src/dev/build/tasks/clean_tasks.ts
index 3051579d2e6f8..d4b4f98ed295b 100644
--- a/src/dev/build/tasks/clean_tasks.ts
+++ b/src/dev/build/tasks/clean_tasks.ts
@@ -62,6 +62,7 @@ export const CleanExtraFilesFromModules: Task = {
// tests
'**/test',
'**/tests',
+ '**/jest.config.js',
'**/__tests__',
'**/*.test.js',
'**/*.snap',
From 5ebded21058146e1d757d42ca90572fe6002fc87 Mon Sep 17 00:00:00 2001
From: Yuliia Naumenko
Date: Tue, 25 May 2021 19:00:29 -0700
Subject: [PATCH 04/17] [triggersActionsUi] Reduce page load bundle to under
100kB (#97770)
* [triggersActionsUi] Reduce page load bundle to under 100kB
* removed old code
* removed fragment
* changed svg logo to lazy react components
* fixed type checks and translations
* fixed type checks
* fixed type checks
* fixed type checks
* fixed tests
* fixed tests
* fixed iconClass
* fixed due to comments
* added info about new IconType to readme file
* fixed key errors
---
packages/kbn-optimizer/limits.yml | 2 +-
.../connectors_dropdown.test.tsx | 40 ++++-
.../public/components/connectors/types.ts | 3 +-
.../translations/translations/ja-JP.json | 28 ---
.../translations/translations/zh-CN.json | 28 ---
x-pack/plugins/triggers_actions_ui/README.md | 36 ++--
.../application/action_type_registry.mock.ts | 4 +-
.../public/application/app.tsx | 2 +-
.../components/add_message_variables.tsx | 4 +-
.../email/email_params.tsx | 10 +-
.../builtin_action_types/jira/jira.tsx | 3 +-
.../builtin_action_types/jira/jira_params.tsx | 6 +-
.../builtin_action_types/jira/logo.svg | 9 -
.../builtin_action_types/jira/logo.tsx | 35 ++++
.../builtin_action_types/pagerduty/logo.tsx | 37 ++++
.../pagerduty/pagerduty.svg | 13 --
.../pagerduty/pagerduty.test.tsx | 2 +-
.../pagerduty/pagerduty.tsx | 3 +-
.../pagerduty/pagerduty_connectors.tsx | 10 +-
.../pagerduty/pagerduty_params.tsx | 6 +-
.../resilient/{logo.svg => logo.tsx} | 23 ++-
.../resilient/resilient.tsx | 3 +-
.../resilient/resilient_params.tsx | 6 +-
.../server_log/server_log_params.tsx | 6 +-
.../builtin_action_types/servicenow/logo.svg | 5 -
.../builtin_action_types/servicenow/logo.tsx | 33 ++++
.../servicenow/servicenow.tsx | 5 +-
.../servicenow/servicenow_itsm_params.tsx | 6 +-
.../servicenow/servicenow_sir_params.tsx | 6 +-
.../slack/slack_connectors.tsx | 10 +-
.../teams/{teams.svg => logo.tsx} | 42 ++++-
.../builtin_action_types/teams/teams.tsx | 3 +-
.../webhook/webhook_connectors.tsx | 10 +-
.../application/components/health_check.tsx | 10 +-
.../prompts/empty_connectors_prompt.tsx | 6 +-
.../public/application/home.tsx | 52 +++---
.../lib/check_action_type_enabled.test.tsx | 2 +-
.../lib/check_action_type_enabled.tsx | 8 +-
.../action_connector_form.tsx | 6 +-
.../action_form.test.tsx | 6 +-
.../action_connector_form/action_form.tsx | 110 ++++++------
.../action_type_form.tsx | 162 +++++++++---------
.../connector_add_flyout.tsx | 23 +--
.../connector_add_inline.tsx | 28 +--
.../connector_add_modal.test.tsx | 2 +-
.../connector_add_modal.tsx | 10 +-
.../connector_edit_flyout.tsx | 33 ++--
.../sections/action_connector_form/index.ts | 8 +
.../test_connector_form.tsx | 10 +-
.../actions_connectors_list.test.tsx | 7 +-
.../components/actions_connectors_list.tsx | 20 ++-
.../components/alert_details.tsx | 14 +-
.../components/alert_instances.tsx | 10 +-
.../sections/alert_form/alert_add.test.tsx | 3 +-
.../sections/alert_form/alert_add.tsx | 17 +-
.../alert_form/alert_conditions_group.tsx | 4 +-
.../sections/alert_form/alert_edit.test.tsx | 2 +-
.../sections/alert_form/alert_edit.tsx | 24 +--
.../sections/alert_form/alert_form.test.tsx | 12 +-
.../sections/alert_form/alert_form.tsx | 73 ++++----
.../sections/alert_form/alert_notify_when.tsx | 22 +--
.../alerts_list/components/alerts_list.tsx | 9 +-
.../components/bulk_operation_popover.tsx | 4 +-
.../common/expression_items/group_by_over.tsx | 6 +-
.../common/expression_items/threshold.tsx | 2 +-
.../public/common/get_add_alert_flyout.tsx | 12 +-
.../common/get_add_connector_flyout.tsx | 14 +-
.../public/common/get_edit_alert_flyout.tsx | 12 +-
.../common/get_edit_connector_flyout.tsx | 14 +-
.../triggers_actions_ui/public/index.ts | 17 +-
.../triggers_actions_ui/public/mocks.ts | 13 +-
.../triggers_actions_ui/public/plugin.ts | 13 +-
.../triggers_actions_ui/public/types.ts | 50 +++++-
73 files changed, 696 insertions(+), 593 deletions(-)
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.svg
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.svg
rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/{logo.svg => logo.tsx} (77%)
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.svg
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.tsx
rename x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/{teams.svg => logo.tsx} (91%)
diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml
index a585a0fc7542f..2e76c26dd7b38 100644
--- a/packages/kbn-optimizer/limits.yml
+++ b/packages/kbn-optimizer/limits.yml
@@ -77,7 +77,7 @@ pageLoadAssetSize:
tileMap: 65337
timelion: 29920
transform: 41007
- triggersActionsUi: 186732
+ triggersActionsUi: 100000
uiActions: 97717
uiActionsEnhanced: 313011
upgradeAssistant: 81241
diff --git a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx
index 0070bc18dfe12..86f80f772944a 100644
--- a/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx
+++ b/x-pack/plugins/cases/public/components/configure_cases/connectors_dropdown.test.tsx
@@ -73,7 +73,14 @@ describe('ConnectorsDropdown', () => {
>
@@ -96,7 +103,14 @@ describe('ConnectorsDropdown', () => {
>
@@ -119,7 +133,14 @@ describe('ConnectorsDropdown', () => {
>
@@ -142,7 +163,14 @@ describe('ConnectorsDropdown', () => {
>
@@ -182,7 +210,9 @@ describe('ConnectorsDropdown', () => {
wrappingComponent: TestProviders,
});
- expect(newWrapper.find('button span:not([data-euiicon-type])').text()).toEqual('My Connector');
+ expect(newWrapper.find('button span:not([data-euiicon-type])').at(1).text()).toBe(
+ 'My Connector'
+ );
});
test('if the props hideConnectorServiceNowSir is true, the connector should not be part of the list of options ', () => {
diff --git a/x-pack/plugins/cases/public/components/connectors/types.ts b/x-pack/plugins/cases/public/components/connectors/types.ts
index fc2f66d331700..1657153ab645b 100644
--- a/x-pack/plugins/cases/public/components/connectors/types.ts
+++ b/x-pack/plugins/cases/public/components/connectors/types.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { IconType } from '@elastic/eui/src/components/icon/icon';
import React from 'react';
import {
@@ -26,7 +27,7 @@ export interface ThirdPartyField {
export interface ConnectorConfiguration {
name: string;
- logo: string;
+ logo: IconType;
}
export interface CaseConnector {
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index a68efbf379ad4..417c8360d0df3 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -23651,19 +23651,6 @@
"xpack.triggersActionsUI.sections.alertEdit.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.alertEdit.saveErrorNotificationText": "ルールを更新できません",
"xpack.triggersActionsUI.sections.alertEdit.saveSuccessNotificationText": "'{ruleName}'を更新しました",
- "xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel": "削除",
- "xpack.triggersActionsUI.sections.alertForm.actionDisabledTitle": "このアクションは無効です",
- "xpack.triggersActionsUI.sections.alertForm.actionIdLabel": "{connectorInstance} コネクター",
- "xpack.triggersActionsUI.sections.alertForm.actionRunWhenInActionGroup": "次のときに実行",
- "xpack.triggersActionsUI.sections.alertForm.actionSectionsTitle": "アクション",
- "xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByConfigMessageTitle": "この機能は Kibana の構成で無効になっています。",
- "xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByLicenseLinkTitle": "ライセンスオプションを表示",
- "xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByLicenseMessageDescription": "このアクションを再び有効にするには、ライセンスをアップグレードしてください。",
- "xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByLicenseMessageTitle": "この機能には {minimumLicenseRequired} ライセンスが必要です。",
- "xpack.triggersActionsUI.sections.alertForm.addActionButtonLabel": "アクションの追加",
- "xpack.triggersActionsUI.sections.alertForm.addConnectorButtonLabel": "コネクターを作成する",
- "xpack.triggersActionsUI.sections.alertForm.addNewActionConnectorActionGroup.display": "{actionGroupName} (現在サポートされていません) ",
- "xpack.triggersActionsUI.sections.alertForm.addNewConnectorEmptyButton": "コネクターの追加",
"xpack.triggersActionsUI.sections.alertForm.alertNameLabel": "名前",
"xpack.triggersActionsUI.sections.alertForm.alertNotifyWhen.label": "毎",
"xpack.triggersActionsUI.sections.alertForm.alertNotifyWhen.onActionGroupChange.description": "アラートステータスが変更されるときにアクションを実行します。",
@@ -23681,37 +23668,22 @@
"xpack.triggersActionsUI.sections.alertForm.conditions.addConditionLabel": "追加:",
"xpack.triggersActionsUI.sections.alertForm.conditions.removeConditionLabel": "削除",
"xpack.triggersActionsUI.sections.alertForm.conditions.title": "条件:",
- "xpack.triggersActionsUI.sections.alertForm.connectorAddInline.actionIdLabel": "別の{connectorInstance}コネクターを使用",
- "xpack.triggersActionsUI.sections.alertForm.connectorAddInline.addNewConnectorEmptyButton": "コネクターの追加",
"xpack.triggersActionsUI.sections.alertForm.documentationLabel": "ドキュメント",
- "xpack.triggersActionsUI.sections.alertForm.emptyConnectorsLabel": "{actionTypeName}コネクターがありません",
"xpack.triggersActionsUI.sections.alertForm.error.noAuthorizedRuleTypes": "ルールを{operation}するには、適切な権限が付与されている必要があります。",
"xpack.triggersActionsUI.sections.alertForm.error.noAuthorizedRuleTypesTitle": "ルールタイプを{operation}する権限がありません。",
"xpack.triggersActionsUI.sections.alertForm.error.requiredActionConnector": "{actionTypeId}コネクターのアクションが必要です。",
"xpack.triggersActionsUI.sections.alertForm.error.requiredIntervalText": "確認間隔が必要です。",
"xpack.triggersActionsUI.sections.alertForm.error.requiredNameText": "名前が必要です。",
"xpack.triggersActionsUI.sections.alertForm.error.requiredRuleTypeIdText": "ルールタイプは必須です。",
- "xpack.triggersActionsUI.sections.alertForm.existingAlertActionTypeEditTitle": "{actionConnectorName}",
- "xpack.triggersActionsUI.sections.alertForm.loadingConnectorsDescription": "コネクターを読み込んでいます…",
- "xpack.triggersActionsUI.sections.alertForm.loadingConnectorTypesDescription": "コネクタータイプを読み込んでいます...",
"xpack.triggersActionsUI.sections.alertForm.loadingRuleTypeParamsDescription": "ルールタイプパラメーターを読み込んでいます…",
"xpack.triggersActionsUI.sections.alertForm.loadingRuleTypesDescription": "ルールタイプを読み込んでいます…",
- "xpack.triggersActionsUI.sections.alertForm.newAlertActionTypeEditTitle": "{actionConnectorName}",
- "xpack.triggersActionsUI.sections.alertForm.preconfiguredTitleMessage": "構成済み",
"xpack.triggersActionsUI.sections.alertForm.renotifyFieldLabel": "通知",
"xpack.triggersActionsUI.sections.alertForm.renotifyWithTooltip": "ルールがアクティブな間にアクションを繰り返す頻度を定義します。",
"xpack.triggersActionsUI.sections.alertForm.ruleTypeSelectLabel": "ルールタイプを選択",
"xpack.triggersActionsUI.sections.alertForm.searchPlaceholderTitle": "検索",
- "xpack.triggersActionsUI.sections.alertForm.selectConnectorTypeTitle": "コネクタータイプを選択",
"xpack.triggersActionsUI.sections.alertForm.solutionFilterLabel": "ユースケースでフィルタリング",
"xpack.triggersActionsUI.sections.alertForm.tagsFieldLabel": "タグ (任意) ",
- "xpack.triggersActionsUI.sections.alertForm.unableToAddAction": "デフォルトアクショングループの定義がないのでアクションを追加できません",
- "xpack.triggersActionsUI.sections.alertForm.unableToLoadActionsMessage": "コネクターを読み込めません",
- "xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTitle": "コネクターを読み込めません。",
- "xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTitle'": "コネクターを読み込めません。",
- "xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTypesMessage": "コネクタータイプを読み込めません",
"xpack.triggersActionsUI.sections.alertForm.unableToLoadRuleTypesMessage": "ルールタイプを読み込めません",
- "xpack.triggersActionsUI.sections.alertForm.unauthorizedToCreateForEmptyConnectors": "許可されたユーザーのみがコネクターを構成できます。管理者にお問い合わせください。",
"xpack.triggersActionsUI.sections.alertsList.actionTypeFilterLabel": "アクションタイプ",
"xpack.triggersActionsUI.sections.alertsList.addRuleButtonLabel": "ルールを作成",
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonDecrypting": "ルールの復号中にエラーが発生しました。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 233bafeb71f3a..8f0694e02ad91 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -24016,19 +24016,6 @@
"xpack.triggersActionsUI.sections.alertEdit.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.alertEdit.saveErrorNotificationText": "无法更新规则。",
"xpack.triggersActionsUI.sections.alertEdit.saveSuccessNotificationText": "已更新“{ruleName}”",
- "xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel": "删除",
- "xpack.triggersActionsUI.sections.alertForm.actionDisabledTitle": "此操作已禁用",
- "xpack.triggersActionsUI.sections.alertForm.actionIdLabel": "{connectorInstance} 连接器",
- "xpack.triggersActionsUI.sections.alertForm.actionRunWhenInActionGroup": "运行条件",
- "xpack.triggersActionsUI.sections.alertForm.actionSectionsTitle": "操作",
- "xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByConfigMessageTitle": "此功能已由 Kibana 配置禁用。",
- "xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByLicenseLinkTitle": "查看许可证选项",
- "xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByLicenseMessageDescription": "要重新启用此操作,请升级您的许可证。",
- "xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByLicenseMessageTitle": "此功能需要{minimumLicenseRequired}许可证。",
- "xpack.triggersActionsUI.sections.alertForm.addActionButtonLabel": "添加操作",
- "xpack.triggersActionsUI.sections.alertForm.addConnectorButtonLabel": "创建连接器",
- "xpack.triggersActionsUI.sections.alertForm.addNewActionConnectorActionGroup.display": "{actionGroupName} (当前不支持) ",
- "xpack.triggersActionsUI.sections.alertForm.addNewConnectorEmptyButton": "添加连接器",
"xpack.triggersActionsUI.sections.alertForm.alertNameLabel": "名称",
"xpack.triggersActionsUI.sections.alertForm.alertNotifyWhen.label": "每",
"xpack.triggersActionsUI.sections.alertForm.alertNotifyWhen.onActionGroupChange.description": "操作在告警状态更改时运行。",
@@ -24046,37 +24033,22 @@
"xpack.triggersActionsUI.sections.alertForm.conditions.addConditionLabel": "添加:",
"xpack.triggersActionsUI.sections.alertForm.conditions.removeConditionLabel": "移除",
"xpack.triggersActionsUI.sections.alertForm.conditions.title": "条件:",
- "xpack.triggersActionsUI.sections.alertForm.connectorAddInline.actionIdLabel": "使用其他 {connectorInstance} 连接器",
- "xpack.triggersActionsUI.sections.alertForm.connectorAddInline.addNewConnectorEmptyButton": "添加连接器",
"xpack.triggersActionsUI.sections.alertForm.documentationLabel": "文档",
- "xpack.triggersActionsUI.sections.alertForm.emptyConnectorsLabel": "无 {actionTypeName} 连接器",
"xpack.triggersActionsUI.sections.alertForm.error.noAuthorizedRuleTypes": "为了{operation}规则,您需要获得相应的权限。",
"xpack.triggersActionsUI.sections.alertForm.error.noAuthorizedRuleTypesTitle": "您尚无权{operation}任何规则类型",
"xpack.triggersActionsUI.sections.alertForm.error.requiredActionConnector": "“{actionTypeId} 连接器的操作”必填。",
"xpack.triggersActionsUI.sections.alertForm.error.requiredIntervalText": "“检查时间间隔”必填。",
"xpack.triggersActionsUI.sections.alertForm.error.requiredNameText": "“名称”必填。",
"xpack.triggersActionsUI.sections.alertForm.error.requiredRuleTypeIdText": "“规则类型”必填。",
- "xpack.triggersActionsUI.sections.alertForm.existingAlertActionTypeEditTitle": "{actionConnectorName}",
- "xpack.triggersActionsUI.sections.alertForm.loadingConnectorsDescription": "正在加载连接器……",
- "xpack.triggersActionsUI.sections.alertForm.loadingConnectorTypesDescription": "正在加载连接器类型……",
"xpack.triggersActionsUI.sections.alertForm.loadingRuleTypeParamsDescription": "正在加载规则类型参数……",
"xpack.triggersActionsUI.sections.alertForm.loadingRuleTypesDescription": "正在加载规则类型……",
- "xpack.triggersActionsUI.sections.alertForm.newAlertActionTypeEditTitle": "{actionConnectorName}",
- "xpack.triggersActionsUI.sections.alertForm.preconfiguredTitleMessage": "预配置",
"xpack.triggersActionsUI.sections.alertForm.renotifyFieldLabel": "通知",
"xpack.triggersActionsUI.sections.alertForm.renotifyWithTooltip": "定义规则处于活动状态时重复操作的频率。",
"xpack.triggersActionsUI.sections.alertForm.ruleTypeSelectLabel": "选择规则类型",
"xpack.triggersActionsUI.sections.alertForm.searchPlaceholderTitle": "搜索",
- "xpack.triggersActionsUI.sections.alertForm.selectConnectorTypeTitle": "选择连接器类型",
"xpack.triggersActionsUI.sections.alertForm.solutionFilterLabel": "按用例筛选",
"xpack.triggersActionsUI.sections.alertForm.tagsFieldLabel": "标签 (可选) ",
- "xpack.triggersActionsUI.sections.alertForm.unableToAddAction": "无法添加操作,因为未定义默认操作组",
- "xpack.triggersActionsUI.sections.alertForm.unableToLoadActionsMessage": "无法加载连接器",
- "xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTitle": "无法加载连接器。",
- "xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTitle'": "无法加载连接器。",
- "xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTypesMessage": "无法加载连接器类型",
"xpack.triggersActionsUI.sections.alertForm.unableToLoadRuleTypesMessage": "无法加载规则类型",
- "xpack.triggersActionsUI.sections.alertForm.unauthorizedToCreateForEmptyConnectors": "只有获得授权的用户才能配置连接器。请联系您的管理员。",
"xpack.triggersActionsUI.sections.alertsList.actionTypeFilterLabel": "操作类型",
"xpack.triggersActionsUI.sections.alertsList.addRuleButtonLabel": "创建规则",
"xpack.triggersActionsUI.sections.alertsList.alertErrorReasonDecrypting": "解密规则时发生错误。",
diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md
index dc1da533af374..7d736218af2d9 100644
--- a/x-pack/plugins/triggers_actions_ui/README.md
+++ b/x-pack/plugins/triggers_actions_ui/README.md
@@ -206,13 +206,13 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent
+ <>
{hasExpressionErrors ? (
-
+ <>
-
+ >
) : null}
@@ -221,7 +221,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent
....
-
+ >
);
};
@@ -322,7 +322,7 @@ Fields of this object `AlertTypeModel` will be mapped properly in the UI below.
2. Define `alertParamsExpression` as `React.FunctionComponent` - this is the form for filling Alert params based on the current Alert type.
```
-import React, { Fragment, useState } from 'react';
+import React, { useState } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { WhenExpression, OfExpression } from '../../../../common/expression_items';
import { builtInAggregationTypes } from '../../../../common/constants';
@@ -340,7 +340,7 @@ export const ExampleExpression: React.FunctionComponent = ({
}) => {
const [aggType, setAggType] = useState('count');
return (
-
+ <>
= ({
) : null}
-
+ >
);
};
@@ -653,7 +653,7 @@ const ThresholdSpecifier = (
}) => {
if (!actionGroup) {
// render empty if no condition action group is specified
- return ;
+ return null;
}
return (
@@ -1073,7 +1073,7 @@ Action type model definition:
export function getActionType(): ActionTypeModel {
return {
id: '.pagerduty',
- iconClass: 'apps',
+ iconClass: lazy(() => import('./logo')),
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText',
{
@@ -1110,7 +1110,7 @@ and action params form available in Create Alert form:
Each action type should be defined as an `ActionTypeModel` object with the following properties:
```
id: string;
- iconClass: string;
+ iconClass: IconType;
selectMessage: string;
actionTypeTitle?: string;
validateConnector: (connector: any) => ValidationResult;
@@ -1121,7 +1121,7 @@ Each action type should be defined as an `ActionTypeModel` object with the follo
|Property|Description|
|---|---|
|id|Action type id. Should be the same as on server side.|
-|iconClass|Icon of action type, that will be displayed on the select card in UI.|
+|iconClass|Setting for icon to be displayed to the user. EUI supports any known EUI icon, SVG URL, or a lazy loaded React component, ReactElement.|
|selectMessage|Short description of action type responsibility, that will be displayed on the select card in UI.|
|validateConnector|Validation function for action connector.|
|validateParams|Validation function for action params.|
@@ -1157,7 +1157,7 @@ Below is a list of steps that should be done to build and register a new action
1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [ActionTypeModel]:
```
-import React, { Fragment, lazy } from 'react';
+import React, { lazy } from 'react';
import { i18n } from '@kbn/i18n';
import {
ActionTypeModel,
@@ -1230,7 +1230,7 @@ export function getActionType(): ActionTypeModel {
2. Define `actionConnectorFields` as `React.FunctionComponent` - this is the form for action connector.
```
-import React, { Fragment } from 'react';
+import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFieldText } from '@elastic/eui';
import { EuiTextArea } from '@elastic/eui';
@@ -1252,7 +1252,7 @@ const ExampleConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, errors }) => {
const { someConnectorField } = action.config;
return (
-
+ <>
0 && someConnectorField !== undefined}
@@ -1267,7 +1267,7 @@ const ExampleConnectorFields: React.FunctionComponent
-
+ >
);
};
@@ -1277,7 +1277,7 @@ export {ExampleConnectorFields as default};
3. Define action type params fields using the property of `ActionTypeModel` `actionParamsFields`:
```
-import React, { Fragment } from 'react';
+import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFieldText } from '@elastic/eui';
import { EuiTextArea } from '@elastic/eui';
@@ -1300,7 +1300,7 @@ const ExampleParamsFields: React.FunctionComponent {
const { message } = actionParams;
return (
-
+ <>
0 && message !== undefined}
@@ -1315,7 +1315,7 @@ const ExampleParamsFields: React.FunctionComponent
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts b/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts
index c1d7a3281b410..3c5c1b551028e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { lazy, Fragment } from 'react';
+import React, { lazy } from 'react';
import uuid from 'uuid';
import { ActionTypeModel, ActionTypeRegistryContract } from '../types';
@@ -21,7 +21,7 @@ const createActionTypeRegistryMock = () => {
const mockedActionParamsFields = lazy(async () => ({
default() {
- return React.createElement(Fragment);
+ return React.createElement(React.Fragment);
},
}));
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
index 2e0614e7ea963..5acb99c684a96 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
@@ -26,7 +26,7 @@ import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common
import { setSavedObjectsClient } from '../common/lib/data_apis';
import { KibanaContextProvider } from '../common/lib/kibana';
-const TriggersActionsUIHome = lazy(async () => import('./home'));
+const TriggersActionsUIHome = lazy(() => import('./home'));
const AlertDetailsRoute = lazy(
() => import('./sections/alert_details/components/alert_details_route')
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx
index 57b251fba0d45..2f220855f9474 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/add_message_variables.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { useState, Fragment } from 'react';
+import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiPopover,
@@ -62,7 +62,7 @@ export const AddMessageVariables: React.FunctionComponent = ({
);
if ((messageVariables?.length ?? 0) === 0) {
- return ;
+ return <>>;
}
return (
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx
index 2c25220598b03..e2d6237af85da 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useState, useEffect } from 'react';
+import React, { useState, useEffect } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiComboBox, EuiButtonEmpty, EuiFormRow } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -46,7 +46,7 @@ export const EmailParamsFields = ({
}, [defaultMessage]);
return (
-
+ <>
+ <>
{!addCC ? (
setAddCC(true)}>
@@ -77,7 +77,7 @@ export const EmailParamsFields = ({
) : null}
-
+ >
}
>
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx
index ba6a5fa2079dc..ff7fd026f8e31 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx
@@ -11,7 +11,6 @@ import {
ActionTypeModel,
ConnectorValidationResult,
} from '../../../../types';
-import logo from './logo.svg';
import { JiraActionConnector, JiraConfig, JiraSecrets, JiraActionParams } from './types';
import * as i18n from './translations';
import { isValidUrl } from '../../../lib/value_validators';
@@ -63,7 +62,7 @@ const validateConnector = (
export function getActionType(): ActionTypeModel {
return {
id: '.jira',
- iconClass: logo,
+ iconClass: lazy(() => import('./logo')),
selectMessage: i18n.JIRA_DESC,
actionTypeTitle: i18n.JIRA_TITLE,
validateConnector,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx
index cb2d637972cb8..11123a81440bb 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useCallback, useEffect, useMemo, useRef } from 'react';
+import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { i18n } from '@kbn/i18n';
import {
@@ -190,7 +190,7 @@ const JiraParamsFields: React.FunctionComponent
+ <>
<>
>
>
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.svg b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.svg
deleted file mode 100644
index 8560cf7e270c8..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx
new file mode 100644
index 0000000000000..2e8f1d5ef3bd7
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/logo.tsx
@@ -0,0 +1,35 @@
+/*
+ * 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 from 'react';
+
+const Logo = () => (
+
+);
+
+// eslint-disable-next-line import/no-default-export
+export { Logo as default };
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx
new file mode 100644
index 0000000000000..20db34351c6b1
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/logo.tsx
@@ -0,0 +1,37 @@
+/*
+ * 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 from 'react';
+
+const Logo = () => (
+
+);
+
+// eslint-disable-next-line import/no-default-export
+export { Logo as default };
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.svg b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.svg
deleted file mode 100644
index b11dcb3570c26..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx
index 7a60d79d33137..eae8690dbdd98 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx
@@ -25,7 +25,7 @@ beforeAll(() => {
describe('actionTypeRegistry.get() works', () => {
test('action type static data is as expected', () => {
expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID);
- expect(actionTypeModel.iconClass).toEqual('test-file-stub');
+ expect(actionTypeModel.actionTypeTitle).toEqual('Send to PagerDuty');
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx
index cae4221e5d7ce..310c5cae24566 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx
@@ -20,7 +20,6 @@ import {
PagerDutyActionParams,
EventActionOptions,
} from '.././types';
-import pagerDutySvg from './pagerduty.svg';
import { hasMustacheTokens } from '../../../lib/has_mustache_tokens';
export function getActionType(): ActionTypeModel<
@@ -30,7 +29,7 @@ export function getActionType(): ActionTypeModel<
> {
return {
id: '.pagerduty',
- iconClass: pagerDutySvg,
+ iconClass: lazy(() => import('./logo')),
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText',
{
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx
index 8c9f809b97447..7e9a5770c2158 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment } from 'react';
+import React from 'react';
import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -21,7 +21,7 @@ const PagerDutyActionConnectorFields: React.FunctionComponent<
const { apiUrl } = action.config;
const { routingKey } = action.secrets;
return (
-
+ <>
-
+ <>
{getEncryptedFieldNotifyLabel(
!action.id,
1,
@@ -94,9 +94,9 @@ const PagerDutyActionConnectorFields: React.FunctionComponent<
}
}}
/>
-
+ >
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
index 98dd9c6bf8431..4961a27fd0ac1 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment } from 'react';
+import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { isUndefined } from 'lodash';
@@ -102,7 +102,7 @@ const PagerDutyParamsFields: React.FunctionComponent
+ <>
>
) : null}
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.svg b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx
similarity index 77%
rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.svg
rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx
index 553c2c62b7191..325893756e2f4 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.svg
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.tsx
@@ -1,3 +1,20 @@
-
+/*
+ * 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 from 'react';
+
+const Logo = () => (
+
+);
+
+// eslint-disable-next-line import/no-default-export
+export { Logo as default };
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx
index a8fe5e8ae4b6a..e7074b7506e7a 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx
@@ -11,7 +11,6 @@ import {
ActionTypeModel,
ConnectorValidationResult,
} from '../../../../types';
-import logo from './logo.svg';
import {
ResilientActionConnector,
ResilientConfig,
@@ -72,7 +71,7 @@ export function getActionType(): ActionTypeModel<
> {
return {
id: '.resilient',
- iconClass: logo,
+ iconClass: lazy(() => import('./logo')),
selectMessage: i18n.DESC,
actionTypeTitle: i18n.TITLE,
validateConnector,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx
index 8444f5a2c5ca9..4642226d40222 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useCallback, useEffect, useMemo, useRef } from 'react';
+import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import {
EuiFormRow,
EuiComboBox,
@@ -165,7 +165,7 @@ const ResilientParamsFields: React.FunctionComponent
+ <>
Incident
@@ -251,7 +251,7 @@ const ResilientParamsFields: React.FunctionComponent
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx
index 2a6d21f31973b..6397ce7bc184e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useEffect, useState } from 'react';
+import React, { useEffect, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiSelect, EuiFormRow } from '@elastic/eui';
import { ActionParamsProps } from '../../../../types';
@@ -48,7 +48,7 @@ export const ServerLogParamsFields: React.FunctionComponent<
}, [defaultMessage]);
return (
-
+ <>
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.svg b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.svg
deleted file mode 100644
index dcd022a8dca18..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.tsx
new file mode 100644
index 0000000000000..f7f79d387c62c
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/logo.tsx
@@ -0,0 +1,33 @@
+/*
+ * 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 from 'react';
+
+function Logo() {
+ return (
+
+ );
+}
+
+// eslint-disable-next-line import/no-default-export
+export default Logo;
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx
index b1664656c0d14..a6cc116d3d7b4 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx
@@ -11,7 +11,6 @@ import {
ActionTypeModel,
ConnectorValidationResult,
} from '../../../../types';
-import logo from './logo.svg';
import {
ServiceNowActionConnector,
ServiceNowConfig,
@@ -68,7 +67,7 @@ export function getServiceNowITSMActionType(): ActionTypeModel<
> {
return {
id: '.servicenow',
- iconClass: logo,
+ iconClass: lazy(() => import('./logo')),
selectMessage: i18n.SERVICENOW_ITSM_DESC,
actionTypeTitle: i18n.SERVICENOW_ITSM_TITLE,
validateConnector,
@@ -103,7 +102,7 @@ export function getServiceNowSIRActionType(): ActionTypeModel<
> {
return {
id: '.servicenow-sir',
- iconClass: logo,
+ iconClass: lazy(() => import('./logo')),
selectMessage: i18n.SERVICENOW_SIR_DESC,
actionTypeTitle: i18n.SERVICENOW_SIR_TITLE,
validateConnector,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx
index 84326a7ae9be8..dbd6fec3dad19 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itsm_params.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
EuiFormRow,
EuiSelect,
@@ -146,7 +146,7 @@ const ServiceNowParamsFields: React.FunctionComponent<
}, [actionParams]);
return (
-
+ <>
{i18n.INCIDENT}
@@ -270,7 +270,7 @@ const ServiceNowParamsFields: React.FunctionComponent<
inputTargetValue={comments && comments.length > 0 ? comments[0].comment : undefined}
label={i18n.COMMENTS_LABEL}
/>
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx
index 95a17c205801c..be6756b1c1049 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
EuiFormRow,
EuiSelect,
@@ -142,7 +142,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent<
}, [actionParams]);
return (
-
+ <>
{i18n.INCIDENT}
@@ -276,7 +276,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent<
inputTargetValue={comments && comments.length > 0 ? comments[0].comment : undefined}
label={i18n.COMMENTS_LABEL}
/>
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx
index 677eb8d7d05f9..ce6cda1294adc 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment } from 'react';
+import React from 'react';
import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -21,7 +21,7 @@ const SlackActionFields: React.FunctionComponent<
const { webhookUrl } = action.secrets;
return (
-
+ <>
-
+ <>
{getEncryptedFieldNotifyLabel(
!action.id,
1,
@@ -68,9 +68,9 @@ const SlackActionFields: React.FunctionComponent<
}
}}
/>
-
+ >
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.svg b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/logo.tsx
similarity index 91%
rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.svg
rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/logo.tsx
index ab07be8f1ef0a..42b1fa2c2a0da 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.svg
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/logo.tsx
@@ -1,7 +1,32 @@
-
-
-
+MS0xMlQxOTo1Nzo0NSswMzowMIKUWWYAAAAASUVORK5CYII="
+ />
+
+);
+
+// eslint-disable-next-line import/no-default-export
+export { Logo as default };
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx
index 00d860fc54110..e8c7be7311c1c 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx
@@ -7,7 +7,6 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import teamsSvg from './teams.svg';
import {
ActionTypeModel,
GenericValidationResult,
@@ -19,7 +18,7 @@ import { isValidUrl } from '../../../lib/value_validators';
export function getActionType(): ActionTypeModel {
return {
id: '.teams',
- iconClass: teamsSvg,
+ iconClass: lazy(() => import('./logo')),
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText',
{
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx
index 9a93d29cfcb15..d3231f52b4d7b 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useEffect, useState } from 'react';
+import React, { useEffect, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@@ -110,7 +110,7 @@ const WebhookActionConnectorFields: React.FunctionComponent<
let headerControl;
if (hasHeaders) {
headerControl = (
-
+ <>
-
+ >
);
}
@@ -220,7 +220,7 @@ const WebhookActionConnectorFields: React.FunctionComponent<
});
return (
-
+ <>
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx
index d75ab102a8e0c..762526dfd7fa7 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment } from 'react';
+import React from 'react';
import { Option, none, some, fold } from 'fp-ts/lib/Option';
import { pipe } from 'fp-ts/lib/pipeable';
import { FormattedMessage } from '@kbn/i18n/react';
@@ -69,16 +69,16 @@ export const HealthCheck: React.FunctionComponent = ({
fold(
() =>
waitForCheck ? (
-
+ <>
-
+ >
) : (
- {children}
+ <>{children}>
),
(healthCheck) => {
return healthCheck?.isSufficientlySecure && healthCheck?.hasPermanentEncryptionKey ? (
- {children}
+ <>{children}>
) : !healthCheck.isAlertsAvailable ? (
) : !healthCheck.isSufficientlySecure && !healthCheck.hasPermanentEncryptionKey ? (
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.tsx
index e56fad409d98f..84ac46605905e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.tsx
@@ -6,7 +6,7 @@
*/
import { FormattedMessage } from '@kbn/i18n/react';
-import React, { Fragment } from 'react';
+import React from 'react';
import { EuiButton, EuiEmptyPrompt, EuiIcon, EuiSpacer, EuiTitle } from '@elastic/eui';
import './empty_connectors_prompt.scss';
@@ -14,7 +14,7 @@ export const EmptyConnectorsPrompt = ({ onCTAClicked }: { onCTAClicked: () => vo
+ <>
@@ -27,7 +27,7 @@ export const EmptyConnectorsPrompt = ({ onCTAClicked }: { onCTAClicked: () => vo
/>
-
+ >
}
body={
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
index b77593c990550..20aec6974d395 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { useEffect } from 'react';
+import React, { lazy, useEffect } from 'react';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@@ -26,11 +26,15 @@ import { getAlertingSectionBreadcrumb } from './lib/breadcrumb';
import { getCurrentDocTitle } from './lib/doc_title';
import { hasShowActionsCapability } from './lib/capabilities';
-import { ActionsConnectorsList } from './sections/actions_connectors_list/components/actions_connectors_list';
-import { AlertsList } from './sections/alerts_list/components/alerts_list';
import { HealthCheck } from './components/health_check';
import { HealthContextProvider } from './context/health_context';
import { useKibana } from '../common/lib/kibana';
+import { suspendedComponentWithProps } from './lib/suspended_component_with_props';
+
+const ActionsConnectorsList = lazy(
+ () => import('./sections/actions_connectors_list/components/actions_connectors_list')
+);
+const AlertsList = lazy(() => import('./sections/alerts_list/components/alerts_list'));
export interface MatchParams {
section: Section;
@@ -137,32 +141,24 @@ export const TriggersActionsUIHome: React.FunctionComponent
-
- {canShowActions && (
- (
-
-
-
-
-
+
+
+
+ {canShowActions && (
+
)}
- />
- )}
- (
-
-
-
-
-
- )}
- />
-
+
+
+
+
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx
index f9855cc9d7130..6b115abc590cc 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx
@@ -61,7 +61,7 @@ describe('checkActionTypeEnabled', () => {
>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.tsx
index e4e67002298ee..1c2a56f4cccaa 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.tsx
@@ -39,7 +39,7 @@ const getLicenseCheckResult = (actionType: ActionType) => {
{
// The "re-enable" terminology is used here because this message is used when an alert
// action was previously enabled and needs action to be re-enabled.
description={i18n.translate(
- 'xpack.triggersActionsUI.sections.alertForm.actionTypeDisabledByLicenseMessageDescription',
+ 'xpack.triggersActionsUI.licenseCheck.actionTypeDisabledByLicenseMessageDescription',
{ defaultMessage: 'To re-enable this action, please upgrade your license.' }
)}
className="actCheckActionTypeEnabled__disabledActionWarningCard"
@@ -58,7 +58,7 @@ const getLicenseCheckResult = (actionType: ActionType) => {
}
@@ -76,7 +76,7 @@ const configurationCheckResult = {
messageCard: (
+ <>
-
+ >
);
const FieldsComponent = actionTypeRegistered.actionConnectorFields;
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx
index 174407e7edec5..ad727be58280f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, lazy } from 'react';
+import React, { lazy } from 'react';
import { mountWithIntl, nextTick } from '@kbn/test/jest';
import { EuiAccordion } from '@elastic/eui';
import { coreMock } from '../../../../../../../src/core/public/mocks';
@@ -34,7 +34,7 @@ const setHasActionsWithBrokenConnector = jest.fn();
describe('action_form', () => {
const mockedActionParamsFields = lazy(async () => ({
default() {
- return ;
+ return <>>;
},
}));
@@ -45,7 +45,7 @@ describe('action_form', () => {
validate: (): ValidationResult => {
return { errors: {} };
},
- alertParamsExpression: () => ,
+ alertParamsExpression: () => <>>,
requiresAppContext: false,
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx
index 55ebbbc6f3edd..e9f79633ef520 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx
@@ -30,7 +30,6 @@ import {
ActionTypeRegistryContract,
} from '../../../types';
import { SectionLoading } from '../../components/section_loading';
-import { ConnectorAddModal } from './connector_add_modal';
import { ActionTypeForm, ActionTypeFormProps } from './action_type_form';
import { AddConnectorInline } from './connector_add_inline';
import { actionTypeCompare } from '../../lib/action_type_compare';
@@ -43,6 +42,8 @@ import {
import { ActionGroup, AlertActionParam } from '../../../../../alerting/common';
import { useKibana } from '../../../common/lib/kibana';
import { DefaultActionParamsGetter } from '../../lib/get_defaults_for_action_params';
+import { ConnectorAddModal } from '.';
+import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props';
export interface ActionGroupWithMessageVariables extends ActionGroup {
omitOptionalMessageVariables?: boolean;
@@ -124,7 +125,7 @@ export const ActionForm = ({
} catch (e) {
toasts.addDanger({
title: i18n.translate(
- 'xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTypesMessage',
+ 'xpack.triggersActionsUI.sections.actionForm.unableToLoadConnectorTypesMessage',
{ defaultMessage: 'Unable to load connector types' }
),
});
@@ -145,7 +146,7 @@ export const ActionForm = ({
} catch (e) {
toasts.addDanger({
title: i18n.translate(
- 'xpack.triggersActionsUI.sections.alertForm.unableToLoadActionsMessage',
+ 'xpack.triggersActionsUI.sections.actionForm.unableToLoadActionsMessage',
{
defaultMessage: 'Unable to load connectors',
}
@@ -193,7 +194,7 @@ export const ActionForm = ({
function addActionType(actionTypeModel: ActionTypeModel) {
if (!defaultActionGroupId) {
toasts!.addDanger({
- title: i18n.translate('xpack.triggersActionsUI.sections.alertForm.unableToAddAction', {
+ title: i18n.translate('xpack.triggersActionsUI.sections.actionForm.unableToAddAction', {
defaultMessage: 'Unable to add action, because default action group is not defined',
}),
});
@@ -271,7 +272,14 @@ export const ActionForm = ({
label={actionTypesIndex[item.id].name}
onClick={() => addActionType(item)}
>
-
+
);
@@ -291,17 +299,17 @@ export const ActionForm = ({
return isLoadingConnectors ? (
) : (
-
+ <>
@@ -354,54 +362,56 @@ export const ActionForm = ({
?.validateParams(actionItem.params);
return (
- {
- setActiveActionItem({ actionTypeId: actionItem.actionTypeId, indices: [index] });
- setAddModalVisibility(true);
- }}
- onConnectorSelected={(id: string) => {
- setActionIdByIndex(id, index);
- }}
- actionTypeRegistry={actionTypeRegistry}
- onDeleteAction={() => {
- const updatedActions = actions.filter(
- (_item: AlertAction, i: number) => i !== index
- );
- setActions(updatedActions);
- setIsAddActionPanelOpen(
- updatedActions.filter((item: AlertAction) => item.id !== actionItem.id).length ===
- 0
- );
- setActiveActionItem(undefined);
- }}
- />
+
+ {
+ setActiveActionItem({ actionTypeId: actionItem.actionTypeId, indices: [index] });
+ setAddModalVisibility(true);
+ }}
+ onConnectorSelected={(id: string) => {
+ setActionIdByIndex(id, index);
+ }}
+ actionTypeRegistry={actionTypeRegistry}
+ onDeleteAction={() => {
+ const updatedActions = actions.filter(
+ (_item: AlertAction, i: number) => i !== index
+ );
+ setActions(updatedActions);
+ setIsAddActionPanelOpen(
+ updatedActions.filter((item: AlertAction) => item.id !== actionItem.id)
+ .length === 0
+ );
+ setActiveActionItem(undefined);
+ }}
+ />
+
+
);
})}
{isAddActionPanelOpen ? (
-
+ <>
@@ -431,7 +441,7 @@ export const ActionForm = ({
{isLoadingActionTypes ? (
@@ -439,7 +449,7 @@ export const ActionForm = ({
actionTypeNodes
)}
-
+ >
) : (
@@ -449,7 +459,7 @@ export const ActionForm = ({
onClick={() => setIsAddActionPanelOpen(true)}
>
@@ -468,7 +478,7 @@ export const ActionForm = ({
actionTypeRegistry={actionTypeRegistry}
/>
) : null}
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx
index 48c6c1b42d7af..2690aeaffad32 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, Suspense, useEffect, useState } from 'react';
+import React, { Suspense, useEffect, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@@ -166,7 +166,7 @@ export const ActionTypeForm = ({
isActionGroupDisabledForActionType
? isActionGroupDisabledForActionType(actionGroupId, actionTypeId)
? i18n.translate(
- 'xpack.triggersActionsUI.sections.alertForm.addNewActionConnectorActionGroup.display',
+ 'xpack.triggersActionsUI.sections.actionTypeForm.addNewActionConnectorActionGroup.display',
{
defaultMessage: '{actionGroupName} (Not Currently Supported)',
values: { actionGroupName },
@@ -202,9 +202,9 @@ export const ActionTypeForm = ({
);
const accordionContent = checkEnabledResult.isEnabled ? (
-
+ <>
{actionGroups && selectedActionGroup && setActionGroupIdByIndex && (
-
+ <>
@@ -240,7 +240,7 @@ export const ActionTypeForm = ({
-
+ >
)}
@@ -248,7 +248,7 @@ export const ActionTypeForm = ({
fullWidth
label={
) : null
@@ -305,88 +305,86 @@ export const ActionTypeForm = ({
) : null}
-
+ >
) : (
checkEnabledResult.messageCard
);
return (
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {selectedActionGroup && !isOpen && (
-
+ {selectedActionGroup.name}
- {selectedActionGroup && !isOpen && (
-
- {selectedActionGroup.name}
-
+ )}
+
+ {checkEnabledResult.isEnabled === false && (
+ <>
+
+ >
)}
-
- {checkEnabledResult.isEnabled === false && (
-
-
-
- )}
-
-
-
-
-
-
- }
- extraAction={
-
- }
- >
- {accordionContent}
-
-
-
+
+
+
+
+
+
+ }
+ extraAction={
+
+ }
+ >
+ {accordionContent}
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx
index 6dc75b318a8f0..d3a6d662720ca 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { useCallback, useState, Fragment, useReducer } from 'react';
+import React, { useCallback, useState, useReducer } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiTitle,
@@ -29,8 +29,8 @@ import { ActionConnectorForm, getConnectorErrors } from './action_connector_form
import {
ActionType,
ActionConnector,
- ActionTypeRegistryContract,
UserConfiguredActionConnector,
+ ConnectorAddFlyoutProps,
} from '../../../types';
import { hasSaveActionsCapability } from '../../lib/capabilities';
import { createActionConnector } from '../../lib/action_connector_api';
@@ -39,15 +39,6 @@ import { useKibana } from '../../../common/lib/kibana';
import { createConnectorReducer, InitialConnector, ConnectorReducer } from './connector_reducer';
import { getConnectorWithInvalidatedFields } from '../../lib/value_validators';
-export interface ConnectorAddFlyoutProps {
- onClose: () => void;
- actionTypes?: ActionType[];
- onTestConnector?: (connector: ActionConnector) => void;
- reloadConnectors?: () => Promise;
- consumer?: string;
- actionTypeRegistry: ActionTypeRegistryContract;
-}
-
const ConnectorAddFlyout: React.FunctionComponent = ({
onClose,
actionTypes,
@@ -199,7 +190,7 @@ const ConnectorAddFlyout: React.FunctionComponent = ({
};
saveButton = (
-
+ <>
{onTestConnector && (
= ({
/>
-
+ >
);
}
@@ -251,7 +242,7 @@ const ConnectorAddFlyout: React.FunctionComponent = ({
) : null}
{actionTypeModel && actionType ? (
-
+ <>
= ({
{actionTypeModel.selectMessage}
-
+ >
) : (
@@ -285,7 +276,7 @@ const ConnectorAddFlyout: React.FunctionComponent = ({
!actionType && hasActionsUpgradeableByTrial ? (
) : (
-
+ <>>
)
}
>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx
index 9a9583313bcdb..0cdcf8bd44413 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_inline.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useEffect, useState } from 'react';
+import React, { useEffect, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@@ -68,7 +68,7 @@ export const AddConnectorInline = ({
const noConnectorsLabel = (
@@ -117,7 +117,7 @@ export const AddConnectorInline = ({
fullWidth
label={
}
@@ -162,8 +162,9 @@ export const AddConnectorInline = ({
);
return (
-
+ <>
}
/>
@@ -211,7 +212,7 @@ export const AddConnectorInline = ({
color="danger"
className="actAccordionActionForm__extraAction"
aria-label={i18n.translate(
- 'xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel',
+ 'xpack.triggersActionsUI.sections.connectorAddInline.accordion.deleteIconAriaLabel',
{
defaultMessage: 'Delete',
}
@@ -236,7 +237,7 @@ export const AddConnectorInline = ({
onClick={onAddConnector}
>
@@ -247,7 +248,7 @@ export const AddConnectorInline = ({
@@ -255,6 +256,9 @@ export const AddConnectorInline = ({
)}
-
+ >
);
};
+
+// eslint-disable-next-line import/no-default-export
+export { AddConnectorInline as default };
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx
index ccff4f5853b1b..c18f6955d1217 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.test.tsx
@@ -7,7 +7,7 @@
import * as React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
-import { ConnectorAddModal } from './connector_add_modal';
+import ConnectorAddModal from './connector_add_modal';
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import { ActionType, ConnectorValidationResult, GenericValidationResult } from '../../../types';
import { useKibana } from '../../../common/lib/kibana';
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx
index 8732727b9a77a..d01ee08df2394 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx
@@ -35,15 +35,16 @@ import {
import { useKibana } from '../../../common/lib/kibana';
import { getConnectorWithInvalidatedFields } from '../../lib/value_validators';
-interface ConnectorAddModalProps {
+// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
+type ConnectorAddModalProps = {
actionType: ActionType;
onClose: () => void;
postSaveEventHandler?: (savedAction: ActionConnector) => void;
consumer?: string;
actionTypeRegistry: ActionTypeRegistryContract;
-}
+};
-export const ConnectorAddModal = ({
+const ConnectorAddModal = ({
actionType,
onClose,
postSaveEventHandler,
@@ -216,3 +217,6 @@ export const ConnectorAddModal = ({
);
};
+
+// eslint-disable-next-line import/no-default-export
+export { ConnectorAddModal as default };
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx
index 6c08b0b0b1ac5..66a4dcc452c51 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { useCallback, useReducer, useState, Fragment } from 'react';
+import React, { useCallback, useReducer, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiTitle,
@@ -30,7 +30,8 @@ import { ActionConnectorForm, getConnectorErrors } from './action_connector_form
import { TestConnectorForm } from './test_connector_form';
import {
ActionConnector,
- ActionTypeRegistryContract,
+ ConnectorEditFlyoutProps,
+ EditConectorTabs,
UserConfiguredActionConnector,
} from '../../../types';
import { ConnectorReducer, createConnectorReducer } from './connector_reducer';
@@ -44,21 +45,7 @@ import './connector_edit_flyout.scss';
import { useKibana } from '../../../common/lib/kibana';
import { getConnectorWithInvalidatedFields } from '../../lib/value_validators';
-export interface ConnectorEditFlyoutProps {
- initialConnector: ActionConnector;
- onClose: () => void;
- tab?: EditConectorTabs;
- reloadConnectors?: () => Promise;
- consumer?: string;
- actionTypeRegistry: ActionTypeRegistryContract;
-}
-
-export enum EditConectorTabs {
- Configuration = 'configuration',
- Test = 'test',
-}
-
-export const ConnectorEditFlyout = ({
+const ConnectorEditFlyout = ({
initialConnector,
onClose,
tab = EditConectorTabs.Configuration,
@@ -173,7 +160,7 @@ export const ConnectorEditFlyout = ({
});
const flyoutTitle = connector.isPreconfigured ? (
-
+ <>
-
+ >
) : (
@@ -313,7 +300,7 @@ export const ConnectorEditFlyout = ({
consumer={consumer}
/>
) : (
-
+ <>
{i18n.translate(
'xpack.triggersActionsUI.sections.editConnectorForm.descriptionText',
@@ -328,7 +315,7 @@ export const ConnectorEditFlyout = ({
defaultMessage="Learn more about preconfigured connectors."
/>
-
+ >
)
) : (
{canSave && actionTypeModel && !connector.isPreconfigured ? (
-
+ <>
-
+ >
) : null}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts
index 6ff8b5ae1d500..75d29fd4b0c09 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/index.ts
@@ -15,3 +15,11 @@ export const ConnectorEditFlyout = suspendedComponentWithProps(
lazy(() => import('./connector_edit_flyout'))
);
export const ActionForm = suspendedComponentWithProps(lazy(() => import('./action_form')));
+
+export const ConnectorAddModal = suspendedComponentWithProps(
+ lazy(() => import('./connector_add_modal'))
+);
+
+export const AddConnectorInline = suspendedComponentWithProps(
+ lazy(() => import('./connector_add_inline'))
+);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/test_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/test_connector_form.tsx
index 8afa2d2b57529..92a17a2e4cfae 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/test_connector_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/test_connector_form.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, Suspense } from 'react';
+import React, { Suspense } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
@@ -102,9 +102,9 @@ export const TestConnectorForm = ({
defaultMessage: 'Run the test',
}),
children: (
-
+ <>
{executeEnabled ? null : (
-
+ <>
-
+ >
)}
-
+ >
),
},
{
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx
index 9102e73690cac..7b6453e705ec3 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx
@@ -8,7 +8,7 @@
import * as React from 'react';
import { mountWithIntl, nextTick } from '@kbn/test/jest';
-import { ActionsConnectorsList } from './actions_connectors_list';
+import ActionsConnectorsList from './actions_connectors_list';
import { coreMock } from '../../../../../../../../src/core/public/mocks';
import { ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';
@@ -154,7 +154,7 @@ describe('actions_connectors_list component with items', () => {
const mockedActionParamsFields = React.lazy(async () => ({
default() {
- return ;
+ return <>>;
},
}));
@@ -260,7 +260,8 @@ describe('actions_connectors_list component with items', () => {
await setup();
await wrapper.find('[data-test-subj="edit1"]').first().simulate('click');
- expect(wrapper.find('ConnectorEditFlyout')).toHaveLength(1);
+ const edit = await wrapper.find('ConnectorEditFlyout');
+ expect(edit).toHaveLength(1);
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
index a322460cde444..c237bbda48658 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx
@@ -25,10 +25,6 @@ import { i18n } from '@kbn/i18n';
import { omit } from 'lodash';
import { FormattedMessage } from '@kbn/i18n/react';
import { loadAllActions, loadActionTypes, deleteActions } from '../../../lib/action_connector_api';
-import ConnectorAddFlyout from '../../action_connector_form/connector_add_flyout';
-import ConnectorEditFlyout, {
- EditConectorTabs,
-} from '../../action_connector_form/connector_edit_flyout';
import {
hasDeleteActionsCapability,
hasSaveActionsCapability,
@@ -37,13 +33,20 @@ import {
import { DeleteModalConfirmation } from '../../../components/delete_modal_confirmation';
import { checkActionTypeEnabled } from '../../../lib/check_action_type_enabled';
import './actions_connectors_list.scss';
-import { ActionConnector, ActionConnectorTableItem, ActionTypeIndex } from '../../../../types';
+import {
+ ActionConnector,
+ ActionConnectorTableItem,
+ ActionTypeIndex,
+ EditConectorTabs,
+} from '../../../../types';
import { EmptyConnectorsPrompt } from '../../../components/prompts/empty_connectors_prompt';
import { useKibana } from '../../../../common/lib/kibana';
import { DEFAULT_HIDDEN_ACTION_TYPES } from '../../../../';
import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner';
+import ConnectorEditFlyout from '../../action_connector_form/connector_edit_flyout';
+import ConnectorAddFlyout from '../../action_connector_form/connector_add_flyout';
-export const ActionsConnectorsList: React.FunctionComponent = () => {
+const ActionsConnectorsList: React.FunctionComponent = () => {
const {
http,
notifications: { toasts },
@@ -440,6 +443,9 @@ export const ActionsConnectorsList: React.FunctionComponent = () => {
);
};
+// eslint-disable-next-line import/no-default-export
+export { ActionsConnectorsList as default };
+
function getActionsCountByActionType(actions: ActionConnector[], actionTypeId: string) {
return actions.filter((action) => action.actionTypeId === actionTypeId).length;
}
@@ -455,7 +461,7 @@ const DeleteOperation: React.FunctionComponent<{
= ({
{hasEditButton ? (
-
+ <>
{' '}
= ({
onSave={setAlert}
/>
)}
-
+ >
) : null}
@@ -201,7 +201,7 @@ export const AlertDetails: React.FunctionComponent = ({
{uniqueActions && uniqueActions.length ? (
-
+ <>
= ({
))}
-
+ >
) : null}
@@ -336,7 +336,7 @@ export const AlertDetails: React.FunctionComponent = ({
readOnly={!canSaveAlert}
/>
) : (
-
+ <>
= ({
/>
-
+ >
)}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
index 5ba4c466f6fad..29290af0d0285 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useState } from 'react';
+import React, { useState } from 'react';
import moment, { Duration } from 'moment';
import { i18n } from '@kbn/i18n';
import { EuiBasicTable, EuiHealth, EuiSpacer, EuiSwitch, EuiToolTip } from '@elastic/eui';
@@ -112,7 +112,7 @@ export const alertInstancesTableColumns = (
),
render: (alertInstance: AlertInstanceListItem) => {
return (
-
+ <>
onMuteAction(alertInstance)}
/>
-
+ >
);
},
sortable: false,
@@ -167,7 +167,7 @@ export function AlertInstances({
};
return (
-
+ <>
-
+ >
);
}
export const AlertInstancesWithApi = withBulkAlertOperations(AlertInstances);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
index a2463d785a3eb..cb43c168aa999 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx
@@ -12,11 +12,12 @@ import { act } from 'react-dom/test-utils';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiFormLabel } from '@elastic/eui';
import { coreMock } from '../../../../../../../src/core/public/mocks';
-import AlertAdd, { AlertAddProps } from './alert_add';
+import AlertAdd from './alert_add';
import { createAlert } from '../../lib/alert_api';
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import {
Alert,
+ AlertAddProps,
AlertFlyoutCloseReason,
ConnectorValidationResult,
GenericValidationResult,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx
index bcae77f896b71..a40f77998d6ee 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx
@@ -11,12 +11,11 @@ import { EuiTitle, EuiFlyoutHeader, EuiFlyout, EuiFlyoutBody, EuiPortal } from '
import { i18n } from '@kbn/i18n';
import { isEmpty } from 'lodash';
import {
- ActionTypeRegistryContract,
Alert,
- AlertTypeRegistryContract,
AlertTypeParams,
AlertUpdates,
AlertFlyoutCloseReason,
+ AlertAddProps,
} from '../../../types';
import { AlertForm, getAlertErrors, isValidAlert } from './alert_form';
import { alertReducer, InitialAlert, InitialAlertReducer } from './alert_reducer';
@@ -31,20 +30,6 @@ import { useKibana } from '../../../common/lib/kibana';
import { hasAlertChanged, haveAlertParamsChanged } from './has_alert_changed';
import { getAlertWithInvalidatedFields } from '../../lib/value_validators';
-export interface AlertAddProps> {
- consumer: string;
- alertTypeRegistry: AlertTypeRegistryContract;
- actionTypeRegistry: ActionTypeRegistryContract;
- onClose: (reason: AlertFlyoutCloseReason) => void;
- alertTypeId?: string;
- canChangeTrigger?: boolean;
- initialValues?: Partial;
- /** @deprecated use `onSave` as a callback after an alert is saved*/
- reloadAlerts?: () => Promise;
- onSave?: () => Promise;
- metadata?: MetaData;
-}
-
const AlertAdd = ({
consumer,
alertTypeRegistry,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx
index 6c2f5aecfcb7c..dd0a7df38eb62 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, PropsWithChildren } from 'react';
+import React, { PropsWithChildren } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFormRow, EuiButtonIcon, EuiTitle } from '@elastic/eui';
import { AlertConditionsProps, ActionGroupWithCondition } from './alert_conditions';
@@ -55,7 +55,7 @@ export const AlertConditionsGroup = ({
...otherProps,
})
) : (
-
+ <>>
)}
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx
index 0736e28f193b8..49dd92b67ee41 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx
@@ -98,7 +98,7 @@ describe('alert_edit', () => {
validate: (): ValidationResult => {
return { errors: {} };
},
- alertParamsExpression: () => ,
+ alertParamsExpression: () => <>>,
requiresAppContext: false,
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx
index d704111858e4f..f6569f32088ee 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useReducer, useState } from 'react';
+import React, { useReducer, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiTitle,
@@ -23,12 +23,7 @@ import {
} from '@elastic/eui';
import { cloneDeep } from 'lodash';
import { i18n } from '@kbn/i18n';
-import {
- ActionTypeRegistryContract,
- Alert,
- AlertFlyoutCloseReason,
- AlertTypeRegistryContract,
-} from '../../../types';
+import { Alert, AlertEditProps, AlertFlyoutCloseReason } from '../../../types';
import { AlertForm, getAlertErrors, isValidAlert } from './alert_form';
import { alertReducer, ConcreteAlertReducer } from './alert_reducer';
import { updateAlert } from '../../lib/alert_api';
@@ -39,17 +34,6 @@ import { ConfirmAlertClose } from './confirm_alert_close';
import { hasAlertChanged } from './has_alert_changed';
import { getAlertWithInvalidatedFields } from '../../lib/value_validators';
-export interface AlertEditProps> {
- initialAlert: Alert;
- alertTypeRegistry: AlertTypeRegistryContract;
- actionTypeRegistry: ActionTypeRegistryContract;
- onClose: (reason: AlertFlyoutCloseReason) => void;
- /** @deprecated use `onSave` as a callback after an alert is saved*/
- reloadAlerts?: () => Promise;
- onSave?: () => Promise;
- metadata?: MetaData;
-}
-
export const AlertEdit = ({
initialAlert,
onClose,
@@ -149,7 +133,7 @@ export const AlertEdit = ({
{hasActionsDisabled && (
-
+ <>
-
+ >
)}
{
validate: (): ValidationResult => {
return { errors: {} };
},
- alertParamsExpression: () => ,
+ alertParamsExpression: () => <>>,
requiresAppContext: false,
};
@@ -72,7 +72,7 @@ describe('alert_form', () => {
validate: (): ValidationResult => {
return { errors: {} };
},
- alertParamsExpression: () => ,
+ alertParamsExpression: () => <>>,
requiresAppContext: true,
};
@@ -84,7 +84,7 @@ describe('alert_form', () => {
validate: (): ValidationResult => {
return { errors: {} };
},
- alertParamsExpression: () => ,
+ alertParamsExpression: () => <>>,
requiresAppContext: false,
};
@@ -322,7 +322,7 @@ describe('alert_form', () => {
validate: (): ValidationResult => {
return { errors: {} };
},
- alertParamsExpression: () => ,
+ alertParamsExpression: () => <>>,
requiresAppContext: true,
},
{
@@ -333,7 +333,7 @@ describe('alert_form', () => {
validate: (): ValidationResult => {
return { errors: {} };
},
- alertParamsExpression: () => ,
+ alertParamsExpression: () => <>>,
requiresAppContext: false,
},
]);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
index 6bb485bc7fbb8..b4b6477fd5947 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx
@@ -470,35 +470,34 @@ export const AlertForm = ({
);
return (
-
-
- {alertTypeListItemHtml}
-
- )
+
+ {alertTypeListItemHtml}
+
+ )
+ }
+ isDisabled={!item.checkEnabledResult.isEnabled}
+ onClick={() => {
+ setAlertProperty('alertTypeId', item.id);
+ setActions([]);
+ setAlertTypeModel(item.alertTypeItem);
+ setAlertProperty('params', {});
+ if (alertTypesIndex && alertTypesIndex.has(item.id)) {
+ setDefaultActionGroupId(alertTypesIndex.get(item.id)!.defaultActionGroupId);
}
- isDisabled={!item.checkEnabledResult.isEnabled}
- onClick={() => {
- setAlertProperty('alertTypeId', item.id);
- setActions([]);
- setAlertTypeModel(item.alertTypeItem);
- setAlertProperty('params', {});
- if (alertTypesIndex && alertTypesIndex.has(item.id)) {
- setDefaultActionGroupId(alertTypesIndex.get(item.id)!.defaultActionGroupId);
- }
- }}
- />
-
+ }}
+ />
);
})}
@@ -507,7 +506,7 @@ export const AlertForm = ({
));
const alertTypeDetails = (
-
+ <>
@@ -605,11 +604,11 @@ export const AlertForm = ({
selectedAlertType ? (
<>
{errors.actionConnectors.length >= 1 ? (
-
+ <>
-
+ >
) : null}
>
) : null}
-
+ >
);
const labelForAlertChecked = (
@@ -793,9 +792,9 @@ export const AlertForm = ({
{alertTypeModel ? (
- {alertTypeDetails}
+ <>{alertTypeDetails}>
) : availableAlertTypes.length ? (
-
+ <>
{errors.alertTypeId.length >= 1 && alert.alertTypeId !== undefined ? (
-
+ <>
-
+ >
) : null}
{alertTypeNodes}
-
+ >
) : alertTypesIndex ? (
) : (
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.tsx
index b774fd702fadc..15b228467cf2d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { Fragment, useState, useEffect, useCallback } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@@ -39,7 +39,7 @@ const NOTIFY_WHEN_OPTIONS: Array> = [
),
'data-test-subj': 'onActionGroupChange',
dropdownDisplay: (
-
+ <>
> = [
/>
-
+ >
),
},
{
@@ -67,7 +67,7 @@ const NOTIFY_WHEN_OPTIONS: Array> = [
),
'data-test-subj': 'onActiveAlert',
dropdownDisplay: (
-
+ <>
> = [
/>
-
+ >
),
},
{
@@ -95,7 +95,7 @@ const NOTIFY_WHEN_OPTIONS: Array> = [
),
'data-test-subj': 'onThrottleInterval',
dropdownDisplay: (
-
+ <>
> = [
/>
-
+ >
),
},
];
@@ -173,7 +173,7 @@ export const AlertNotifyWhen = ({
);
return (
-
+ <>
@@ -184,7 +184,7 @@ export const AlertNotifyWhen = ({
onChange={onNotifyWhenValueChange}
/>
{showCustomThrottleOpts && (
-
+ <>
@@ -227,11 +227,11 @@ export const AlertNotifyWhen = ({
-
+ >
)}
-
+ >
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
index d43dd9f05344f..1fb688c4dd6bf 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx
@@ -10,7 +10,7 @@
import { i18n } from '@kbn/i18n';
import { capitalize, sortBy } from 'lodash';
import { FormattedMessage } from '@kbn/i18n/react';
-import React, { useEffect, useState, Fragment } from 'react';
+import React, { useEffect, useState } from 'react';
import {
EuiBasicTable,
EuiBadge,
@@ -479,7 +479,7 @@ export const AlertsList: React.FunctionComponent = () => {
: false;
const table = (
-
+ <>
{selectedIds.length > 0 && authorizedToModifySelectedAlerts && (
@@ -713,7 +713,7 @@ export const AlertsList: React.FunctionComponent = () => {
onCancel={() => setManageLicenseModalOpts(null)}
/>
)}
-
+ >
);
const loadedItems = convertAlertsToTableItems(
@@ -782,6 +782,9 @@ export const AlertsList: React.FunctionComponent = () => {
);
};
+// eslint-disable-next-line import/no-default-export
+export { AlertsList as default };
+
const noPermissionPrompt = (
{
>
{children &&
React.Children.map(children, (child) =>
- React.isValidElement(child) ? {React.cloneElement(child, {})} : child
+ React.isValidElement(child) ? <>{React.cloneElement(child, {})}> : child
)}
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx
index a6ec9d1b39665..1430c40340771 100644
--- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/group_by_over.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { useState, Fragment } from 'react';
+import React, { useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import {
@@ -151,7 +151,7 @@ export const GroupByExpression = ({
{groupByTypes[groupBy].sizeRequired ? (
-
+ <>
0} error={errors.termSize}>
-
+ >
) : null}
diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx
index d650162816f2b..5c44b6f29178b 100644
--- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { useEffect, useState, Fragment } from 'react';
+import React, { Fragment, useEffect, useState } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiExpression,
diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_add_alert_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_add_alert_flyout.tsx
index 42444f4b54e86..2698f4ee2e428 100644
--- a/x-pack/plugins/triggers_actions_ui/public/common/get_add_alert_flyout.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/common/get_add_alert_flyout.tsx
@@ -5,14 +5,10 @@
* 2.0.
*/
-import React, { lazy, Suspense } from 'react';
-import type { AlertAddProps } from '../application/sections/alert_form/alert_add';
+import React from 'react';
+import { AlertAdd } from '../application/sections/alert_form';
+import type { AlertAddProps } from '../types';
export const getAddAlertFlyoutLazy = (props: AlertAddProps) => {
- const AlertAddFlyoutLazy = lazy(() => import('../application/sections/alert_form/alert_add'));
- return (
-
-
-
- );
+ return ;
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_add_connector_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_add_connector_flyout.tsx
index 2c211572f2850..09261714cf8cf 100644
--- a/x-pack/plugins/triggers_actions_ui/public/common/get_add_connector_flyout.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/common/get_add_connector_flyout.tsx
@@ -5,16 +5,10 @@
* 2.0.
*/
-import React, { lazy, Suspense } from 'react';
-import type { ConnectorAddFlyoutProps } from '../application/sections/action_connector_form/connector_add_flyout';
+import React from 'react';
+import { ConnectorAddFlyout } from '../application/sections/action_connector_form';
+import type { ConnectorAddFlyoutProps } from '../types';
export const getAddConnectorFlyoutLazy = (props: ConnectorAddFlyoutProps) => {
- const ConnectorAddFlyoutLazy = lazy(
- () => import('../application/sections/action_connector_form/connector_add_flyout')
- );
- return (
-
-
-
- );
+ return ;
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_edit_alert_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_edit_alert_flyout.tsx
index 89b17f5bb1596..26cc1159e5afd 100644
--- a/x-pack/plugins/triggers_actions_ui/public/common/get_edit_alert_flyout.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/common/get_edit_alert_flyout.tsx
@@ -5,14 +5,10 @@
* 2.0.
*/
-import React, { lazy, Suspense } from 'react';
-import type { AlertEditProps } from '../application/sections/alert_form/alert_edit';
+import React from 'react';
+import { AlertEdit } from '../application/sections/alert_form';
+import type { AlertEditProps } from '../types';
export const getEditAlertFlyoutLazy = (props: AlertEditProps) => {
- const AlertEditFlyoutLazy = lazy(() => import('../application/sections/alert_form/alert_edit'));
- return (
-
-
-
- );
+ return ;
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/common/get_edit_connector_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/common/get_edit_connector_flyout.tsx
index 38002cfe14a15..90ecea56856f5 100644
--- a/x-pack/plugins/triggers_actions_ui/public/common/get_edit_connector_flyout.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/common/get_edit_connector_flyout.tsx
@@ -5,16 +5,10 @@
* 2.0.
*/
-import React, { lazy, Suspense } from 'react';
-import type { ConnectorEditFlyoutProps } from '../application/sections/action_connector_form/connector_edit_flyout';
+import React from 'react';
+import { ConnectorEditFlyout } from '../application/sections/action_connector_form';
+import type { ConnectorEditFlyoutProps } from '../types';
export const getEditConnectorFlyoutLazy = (props: ConnectorEditFlyoutProps) => {
- const ConnectorEditFlyoutLazy = lazy(
- () => import('../application/sections/action_connector_form/connector_edit_flyout')
- );
- return (
-
-
-
- );
+ return ;
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts
index b65086cd6f3e7..134627929e4a0 100644
--- a/x-pack/plugins/triggers_actions_ui/public/index.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/index.ts
@@ -7,14 +7,6 @@
import { Plugin } from './plugin';
-export { AlertAdd } from './application/sections/alert_form';
-export {
- AlertEdit,
- AlertConditions,
- AlertConditionsGroup,
- ActionGroupWithCondition,
-} from './application/sections';
-
export type {
AlertAction,
Alert,
@@ -37,7 +29,10 @@ export {
ConnectorEditFlyout,
} from './application/sections/action_connector_form';
-export { loadActionTypes } from './application/lib/action_connector_api';
+export type { ActionGroupWithCondition } from './application/sections';
+
+export { AlertConditions, AlertConditionsGroup } from './application/sections';
+
export * from './common';
export function plugin() {
@@ -47,6 +42,8 @@ export function plugin() {
export { Plugin };
export * from './plugin';
-export { TIME_UNITS } from './application/constants';
+export { loadActionTypes } from './application/lib/action_connector_api/connector_types';
+
+export type { TIME_UNITS } from './application/constants';
export { getTimeUnitLabel } from './common/lib/get_time_unit_label';
export type { TriggersAndActionsUiServices } from '../public/application/app';
diff --git a/x-pack/plugins/triggers_actions_ui/public/mocks.ts b/x-pack/plugins/triggers_actions_ui/public/mocks.ts
index 9666f8ab1b16b..dfc1cc88e15bc 100644
--- a/x-pack/plugins/triggers_actions_ui/public/mocks.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/mocks.ts
@@ -5,10 +5,6 @@
* 2.0.
*/
-import type { ConnectorAddFlyoutProps } from './application/sections/action_connector_form/connector_add_flyout';
-import type { ConnectorEditFlyoutProps } from './application/sections/action_connector_form/connector_edit_flyout';
-import type { AlertAddProps } from './application/sections/alert_form/alert_add';
-import type { AlertEditProps } from './application/sections/alert_form/alert_edit';
import type { TriggersAndActionsUIPublicPluginStart } from './plugin';
import { getAddConnectorFlyoutLazy } from './common/get_add_connector_flyout';
@@ -17,7 +13,14 @@ import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout';
import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout';
import { TypeRegistry } from './application/type_registry';
-import { ActionTypeModel, AlertTypeModel } from './types';
+import {
+ ActionTypeModel,
+ AlertAddProps,
+ AlertEditProps,
+ AlertTypeModel,
+ ConnectorAddFlyoutProps,
+ ConnectorEditFlyoutProps,
+} from './types';
function createStartMock(): TriggersAndActionsUIPublicPluginStart {
const actionTypeRegistry = new TypeRegistry();
diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts
index a027f25d15eb7..62daf2ad198f3 100644
--- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts
@@ -32,11 +32,14 @@ import { getEditConnectorFlyoutLazy } from './common/get_edit_connector_flyout';
import { getAddAlertFlyoutLazy } from './common/get_add_alert_flyout';
import { getEditAlertFlyoutLazy } from './common/get_edit_alert_flyout';
-import type { ActionTypeModel, AlertTypeModel } from './types';
-import type { ConnectorAddFlyoutProps } from './application/sections/action_connector_form/connector_add_flyout';
-import type { ConnectorEditFlyoutProps } from './application/sections/action_connector_form/connector_edit_flyout';
-import type { AlertAddProps } from './application/sections/alert_form/alert_add';
-import type { AlertEditProps } from './application/sections/alert_form/alert_edit';
+import type {
+ ActionTypeModel,
+ AlertAddProps,
+ AlertEditProps,
+ AlertTypeModel,
+ ConnectorAddFlyoutProps,
+ ConnectorEditFlyoutProps,
+} from './types';
export interface TriggersAndActionsUIPublicPluginSetup {
actionTypeRegistry: TypeRegistry;
diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts
index 6db5634be2221..0f2b961b1f2da 100644
--- a/x-pack/plugins/triggers_actions_ui/public/types.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/types.ts
@@ -10,6 +10,7 @@ import type { DocLinksStart } from 'kibana/public';
import { ComponentType } from 'react';
import { ChartsPluginSetup } from 'src/plugins/charts/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
+import { IconType } from '@elastic/eui';
import {
ActionType,
AlertHistoryEsIndexConnectorId,
@@ -103,7 +104,7 @@ export interface Sorting {
export interface ActionTypeModel {
id: string;
- iconClass: string;
+ iconClass: IconType;
selectMessage: string;
actionTypeTitle?: string;
validateConnector: (
@@ -247,3 +248,50 @@ export interface AlertTypeModel void;
+ actionTypes?: ActionType[];
+ onTestConnector?: (connector: ActionConnector) => void;
+ reloadConnectors?: () => Promise;
+ consumer?: string;
+ actionTypeRegistry: ActionTypeRegistryContract;
+}
+export enum EditConectorTabs {
+ Configuration = 'configuration',
+ Test = 'test',
+}
+
+export interface ConnectorEditFlyoutProps {
+ initialConnector: ActionConnector;
+ onClose: () => void;
+ tab?: EditConectorTabs;
+ reloadConnectors?: () => Promise;
+ consumer?: string;
+ actionTypeRegistry: ActionTypeRegistryContract;
+}
+
+export interface AlertEditProps> {
+ initialAlert: Alert;
+ alertTypeRegistry: AlertTypeRegistryContract;
+ actionTypeRegistry: ActionTypeRegistryContract;
+ onClose: (reason: AlertFlyoutCloseReason) => void;
+ /** @deprecated use `onSave` as a callback after an alert is saved*/
+ reloadAlerts?: () => Promise;
+ onSave?: () => Promise;
+ metadata?: MetaData;
+}
+
+export interface AlertAddProps> {
+ consumer: string;
+ alertTypeRegistry: AlertTypeRegistryContract;
+ actionTypeRegistry: ActionTypeRegistryContract;
+ onClose: (reason: AlertFlyoutCloseReason) => void;
+ alertTypeId?: string;
+ canChangeTrigger?: boolean;
+ initialValues?: Partial;
+ /** @deprecated use `onSave` as a callback after an alert is saved*/
+ reloadAlerts?: () => Promise;
+ onSave?: () => Promise;
+ metadata?: MetaData;
+}
From 656ff1ca272bbf4e11262fefbbb255a057dfee60 Mon Sep 17 00:00:00 2001
From: Spencer
Date: Tue, 25 May 2021 20:12:12 -0600
Subject: [PATCH 05/17] [ftr] migrate "filterBar" service to FtrService class
(#100601)
Co-authored-by: spalger
---
test/functional/services/filter_bar.ts | 350 ++++++++++++-------------
test/functional/services/index.ts | 4 +-
2 files changed, 175 insertions(+), 179 deletions(-)
diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts
index 1ffa5c94b5fc4..5f20d3d4f8b7b 100644
--- a/test/functional/services/filter_bar.ts
+++ b/test/functional/services/filter_bar.ts
@@ -7,200 +7,196 @@
*/
import classNames from 'classnames';
-import { FtrProviderContext } from '../ftr_provider_context';
-
-export function FilterBarProvider({ getService, getPageObjects }: FtrProviderContext) {
- const comboBox = getService('comboBox');
- const testSubjects = getService('testSubjects');
- const PageObjects = getPageObjects(['common', 'header']);
-
- class FilterBar {
- /**
- * Checks if specified filter exists
- *
- * @param key field name
- * @param value filter value
- * @param enabled filter status
- * @param pinned filter pinned status
- * @param negated filter including or excluding value
- */
- public async hasFilter(
- key: string,
- value: string,
- enabled: boolean = true,
- pinned: boolean = false,
- negated: boolean = false
- ): Promise {
- const filterActivationState = enabled ? 'enabled' : 'disabled';
- const filterPinnedState = pinned ? 'pinned' : 'unpinned';
- const filterNegatedState = negated ? 'filter-negated' : '';
- return testSubjects.exists(
- classNames(
- 'filter',
- `filter-${filterActivationState}`,
- key !== '' && `filter-key-${key}`,
- value !== '' && `filter-value-${value}`,
- `filter-${filterPinnedState}`,
- filterNegatedState
- ),
- {
- allowHidden: true,
- }
- );
- }
+import { FtrService } from '../ftr_provider_context';
+
+export class FilterBarService extends FtrService {
+ private readonly comboBox = this.ctx.getService('comboBox');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly PageObjects = this.ctx.getPageObjects(['common', 'header']);
+
+ /**
+ * Checks if specified filter exists
+ *
+ * @param key field name
+ * @param value filter value
+ * @param enabled filter status
+ * @param pinned filter pinned status
+ * @param negated filter including or excluding value
+ */
+ public async hasFilter(
+ key: string,
+ value: string,
+ enabled: boolean = true,
+ pinned: boolean = false,
+ negated: boolean = false
+ ): Promise {
+ const filterActivationState = enabled ? 'enabled' : 'disabled';
+ const filterPinnedState = pinned ? 'pinned' : 'unpinned';
+ const filterNegatedState = negated ? 'filter-negated' : '';
+ return this.testSubjects.exists(
+ classNames(
+ 'filter',
+ `filter-${filterActivationState}`,
+ key !== '' && `filter-key-${key}`,
+ value !== '' && `filter-value-${value}`,
+ `filter-${filterPinnedState}`,
+ filterNegatedState
+ ),
+ {
+ allowHidden: true,
+ }
+ );
+ }
- /**
- * Removes specified filter
- *
- * @param key field name
- */
- public async removeFilter(key: string): Promise {
- await testSubjects.click(`~filter & ~filter-key-${key}`);
- await testSubjects.click(`deleteFilter`);
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
- }
+ /**
+ * Removes specified filter
+ *
+ * @param key field name
+ */
+ public async removeFilter(key: string): Promise {
+ await this.testSubjects.click(`~filter & ~filter-key-${key}`);
+ await this.testSubjects.click(`deleteFilter`);
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
- /**
- * Removes all filters
- */
- public async removeAllFilters(): Promise {
- await testSubjects.click('showFilterActions');
- await testSubjects.click('removeAllFilters');
- await PageObjects.header.waitUntilLoadingHasFinished();
- await PageObjects.common.waitUntilUrlIncludes('filters:!()');
- }
+ /**
+ * Removes all filters
+ */
+ public async removeAllFilters(): Promise {
+ await this.testSubjects.click('showFilterActions');
+ await this.testSubjects.click('removeAllFilters');
+ await this.PageObjects.header.waitUntilLoadingHasFinished();
+ await this.PageObjects.common.waitUntilUrlIncludes('filters:!()');
+ }
- /**
- * Changes filter active status
- *
- * @param key field name
- */
- public async toggleFilterEnabled(key: string): Promise {
- await testSubjects.click(`~filter & ~filter-key-${key}`);
- await testSubjects.click(`disableFilter`);
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
- }
+ /**
+ * Changes filter active status
+ *
+ * @param key field name
+ */
+ public async toggleFilterEnabled(key: string): Promise {
+ await this.testSubjects.click(`~filter & ~filter-key-${key}`);
+ await this.testSubjects.click(`disableFilter`);
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
- public async toggleFilterPinned(key: string): Promise {
- await testSubjects.click(`~filter & ~filter-key-${key}`);
- await testSubjects.click(`pinFilter`);
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
- }
+ public async toggleFilterPinned(key: string): Promise {
+ await this.testSubjects.click(`~filter & ~filter-key-${key}`);
+ await this.testSubjects.click(`pinFilter`);
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
- public async isFilterPinned(key: string): Promise {
- const filter = await testSubjects.find(`~filter & ~filter-key-${key}`);
- return (await filter.getAttribute('data-test-subj')).includes('filter-pinned');
- }
+ public async isFilterPinned(key: string): Promise {
+ const filter = await this.testSubjects.find(`~filter & ~filter-key-${key}`);
+ return (await filter.getAttribute('data-test-subj')).includes('filter-pinned');
+ }
- public async getFilterCount(): Promise {
- const filters = await testSubjects.findAll('~filter');
- return filters.length;
- }
+ public async getFilterCount(): Promise {
+ const filters = await this.testSubjects.findAll('~filter');
+ return filters.length;
+ }
- /**
- * Adds a filter to the filter bar.
- *
- * @param {string} field The name of the field the filter should be applied for.
- * @param {string} operator A valid operator for that fields, e.g. "is one of", "is", "exists", etc.
- * @param {string[]|string} values The remaining parameters are the values passed into the individual
- * value input fields, i.e. the third parameter into the first input field, the fourth into the second, etc.
- * Each value itself can be an array, in case you want to enter multiple values into one field (e.g. for "is one of"):
- * @example
- * // Add a plain single value
- * filterBar.addFilter('country', 'is', 'NL');
- * // Add an exists filter
- * filterBar.addFilter('country', 'exists');
- * // Add a range filter for a numeric field
- * filterBar.addFilter('bytes', 'is between', '500', '1000');
- * // Add a filter containing multiple values
- * filterBar.addFilter('extension', 'is one of', ['jpg', 'png']);
- */
- public async addFilter(field: string, operator: string, ...values: any): Promise {
- await testSubjects.click('addFilter');
- await comboBox.set('filterFieldSuggestionList', field);
- await comboBox.set('filterOperatorList', operator);
- const params = await testSubjects.find('filterParams');
- const paramsComboBoxes = await params.findAllByCssSelector(
- '[data-test-subj~="filterParamsComboBox"]',
- 1000
- );
- const paramFields = await params.findAllByTagName('input', 1000);
- for (let i = 0; i < values.length; i++) {
- let fieldValues = values[i];
- if (!Array.isArray(fieldValues)) {
- fieldValues = [fieldValues];
- }
+ /**
+ * Adds a filter to the filter bar.
+ *
+ * @param {string} field The name of the field the filter should be applied for.
+ * @param {string} operator A valid operator for that fields, e.g. "is one of", "is", "exists", etc.
+ * @param {string[]|string} values The remaining parameters are the values passed into the individual
+ * value input fields, i.e. the third parameter into the first input field, the fourth into the second, etc.
+ * Each value itself can be an array, in case you want to enter multiple values into one field (e.g. for "is one of"):
+ * @example
+ * // Add a plain single value
+ * filterBar.addFilter('country', 'is', 'NL');
+ * // Add an exists filter
+ * filterBar.addFilter('country', 'exists');
+ * // Add a range filter for a numeric field
+ * filterBar.addFilter('bytes', 'is between', '500', '1000');
+ * // Add a filter containing multiple values
+ * filterBar.addFilter('extension', 'is one of', ['jpg', 'png']);
+ */
+ public async addFilter(field: string, operator: string, ...values: any): Promise {
+ await this.testSubjects.click('addFilter');
+ await this.comboBox.set('filterFieldSuggestionList', field);
+ await this.comboBox.set('filterOperatorList', operator);
+ const params = await this.testSubjects.find('filterParams');
+ const paramsComboBoxes = await params.findAllByCssSelector(
+ '[data-test-subj~="filterParamsComboBox"]',
+ 1000
+ );
+ const paramFields = await params.findAllByTagName('input', 1000);
+ for (let i = 0; i < values.length; i++) {
+ let fieldValues = values[i];
+ if (!Array.isArray(fieldValues)) {
+ fieldValues = [fieldValues];
+ }
- if (paramsComboBoxes && paramsComboBoxes.length > 0) {
- for (let j = 0; j < fieldValues.length; j++) {
- await comboBox.setElement(paramsComboBoxes[i], fieldValues[j]);
- }
- } else if (paramFields && paramFields.length > 0) {
- for (let j = 0; j < fieldValues.length; j++) {
- await paramFields[i].type(fieldValues[j]);
- }
+ if (paramsComboBoxes && paramsComboBoxes.length > 0) {
+ for (let j = 0; j < fieldValues.length; j++) {
+ await this.comboBox.setElement(paramsComboBoxes[i], fieldValues[j]);
+ }
+ } else if (paramFields && paramFields.length > 0) {
+ for (let j = 0; j < fieldValues.length; j++) {
+ await paramFields[i].type(fieldValues[j]);
}
}
- await testSubjects.click('saveFilter');
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
- }
-
- /**
- * Activates filter editing
- * @param key field name
- * @param value field value
- */
- public async clickEditFilter(key: string, value: string): Promise {
- await testSubjects.click(`~filter & ~filter-key-${key} & ~filter-value-${value}`);
- await testSubjects.click(`editFilter`);
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
}
+ await this.testSubjects.click('saveFilter');
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
- /**
- * Returns available phrases in the filter
- */
- public async getFilterEditorSelectedPhrases(): Promise {
- return await comboBox.getComboBoxSelectedOptions('~filterParamsComboBox');
- }
+ /**
+ * Activates filter editing
+ * @param key field name
+ * @param value field value
+ */
+ public async clickEditFilter(key: string, value: string): Promise {
+ await this.testSubjects.click(`~filter & ~filter-key-${key} & ~filter-value-${value}`);
+ await this.testSubjects.click(`editFilter`);
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
- /**
- * Returns available fields in the filter
- */
- public async getFilterEditorFields(): Promise {
- const optionsString = await comboBox.getOptionsList('filterFieldSuggestionList');
- return optionsString.split('\n');
- }
+ /**
+ * Returns available phrases in the filter
+ */
+ public async getFilterEditorSelectedPhrases(): Promise {
+ return await this.comboBox.getComboBoxSelectedOptions('~filterParamsComboBox');
+ }
- /**
- * Closes field editor modal window
- */
- public async ensureFieldEditorModalIsClosed(): Promise {
- const cancelSaveFilterModalButtonExists = await testSubjects.exists('cancelSaveFilter');
- if (cancelSaveFilterModalButtonExists) {
- await testSubjects.click('cancelSaveFilter');
- }
- await testSubjects.waitForDeleted('cancelSaveFilter');
- }
+ /**
+ * Returns available fields in the filter
+ */
+ public async getFilterEditorFields(): Promise {
+ const optionsString = await this.comboBox.getOptionsList('filterFieldSuggestionList');
+ return optionsString.split('\n');
+ }
- /**
- * Returns comma-separated list of index patterns
- */
- public async getIndexPatterns(): Promise {
- await testSubjects.click('addFilter');
- const indexPatterns = await comboBox.getOptionsList('filterIndexPatternsSelect');
- await this.ensureFieldEditorModalIsClosed();
- return indexPatterns.trim().split('\n').join(',');
+ /**
+ * Closes field editor modal window
+ */
+ public async ensureFieldEditorModalIsClosed(): Promise {
+ const cancelSaveFilterModalButtonExists = await this.testSubjects.exists('cancelSaveFilter');
+ if (cancelSaveFilterModalButtonExists) {
+ await this.testSubjects.click('cancelSaveFilter');
}
+ await this.testSubjects.waitForDeleted('cancelSaveFilter');
+ }
- /**
- * Adds new index pattern filter
- * @param indexPatternTitle
- */
- public async selectIndexPattern(indexPatternTitle: string): Promise {
- await testSubjects.click('addFilter');
- await comboBox.set('filterIndexPatternsSelect', indexPatternTitle);
- }
+ /**
+ * Returns comma-separated list of index patterns
+ */
+ public async getIndexPatterns(): Promise {
+ await this.testSubjects.click('addFilter');
+ const indexPatterns = await this.comboBox.getOptionsList('filterIndexPatternsSelect');
+ await this.ensureFieldEditorModalIsClosed();
+ return indexPatterns.trim().split('\n').join(',');
}
- return new FilterBar();
+ /**
+ * Adds new index pattern filter
+ * @param indexPatternTitle
+ */
+ public async selectIndexPattern(indexPatternTitle: string): Promise {
+ await this.testSubjects.click('addFilter');
+ await this.comboBox.set('filterIndexPatternsSelect', indexPatternTitle);
+ }
}
diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts
index 0dd7f20debcbd..b6887bc38b93e 100644
--- a/test/functional/services/index.ts
+++ b/test/functional/services/index.ts
@@ -27,7 +27,7 @@ import {
} from './dashboard';
import { DocTableProvider } from './doc_table';
import { EmbeddingProvider } from './embedding';
-import { FilterBarProvider } from './filter_bar';
+import { FilterBarService } from './filter_bar';
import { FlyoutProvider } from './flyout';
import { GlobalNavProvider } from './global_nav';
import { InspectorProvider } from './inspector';
@@ -53,7 +53,7 @@ export const services = {
...commonServiceProviders,
__webdriver__: RemoteProvider,
- filterBar: FilterBarProvider,
+ filterBar: FilterBarService,
queryBar: QueryBarProvider,
find: FindProvider,
testSubjects: TestSubjectsProvider,
From 090e0beb65e42b601b54b8f89afc701a3e1ec33d Mon Sep 17 00:00:00 2001
From: Spencer
Date: Tue, 25 May 2021 20:14:00 -0600
Subject: [PATCH 06/17] [ftr] migrate "fieldEditor" to FtrService class
(#100597)
Co-authored-by: spalger
---
test/functional/services/field_editor.ts | 78 +++++++++++-------------
test/functional/services/index.ts | 4 +-
2 files changed, 39 insertions(+), 43 deletions(-)
diff --git a/test/functional/services/field_editor.ts b/test/functional/services/field_editor.ts
index 342e2afec28d3..c74c229cd11c7 100644
--- a/test/functional/services/field_editor.ts
+++ b/test/functional/services/field_editor.ts
@@ -6,51 +6,47 @@
* Side Public License, v 1.
*/
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
-export function FieldEditorProvider({ getService }: FtrProviderContext) {
- const browser = getService('browser');
- const testSubjects = getService('testSubjects');
+export class FieldEditorService extends FtrService {
+ private readonly browser = this.ctx.getService('browser');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
- class FieldEditor {
- public async setName(name: string) {
- await testSubjects.setValue('nameField > input', name);
- }
- public async enableCustomLabel() {
- await testSubjects.setEuiSwitch('customLabelRow > toggle', 'check');
- }
- public async setCustomLabel(name: string) {
- await testSubjects.setValue('customLabelRow > input', name);
- }
- public async enableValue() {
- await testSubjects.setEuiSwitch('valueRow > toggle', 'check');
- }
- public async disableValue() {
- await testSubjects.setEuiSwitch('valueRow > toggle', 'uncheck');
- }
- public async typeScript(script: string) {
- const editor = await (await testSubjects.find('valueRow')).findByClassName(
- 'react-monaco-editor-container'
- );
- const textarea = await editor.findByClassName('monaco-mouse-cursor-text');
-
- await textarea.click();
- await browser.pressKeys(script);
- }
- public async save() {
- await testSubjects.click('fieldSaveButton');
- }
+ public async setName(name: string) {
+ await this.testSubjects.setValue('nameField > input', name);
+ }
+ public async enableCustomLabel() {
+ await this.testSubjects.setEuiSwitch('customLabelRow > toggle', 'check');
+ }
+ public async setCustomLabel(name: string) {
+ await this.testSubjects.setValue('customLabelRow > input', name);
+ }
+ public async enableValue() {
+ await this.testSubjects.setEuiSwitch('valueRow > toggle', 'check');
+ }
+ public async disableValue() {
+ await this.testSubjects.setEuiSwitch('valueRow > toggle', 'uncheck');
+ }
+ public async typeScript(script: string) {
+ const editor = await (await this.testSubjects.find('valueRow')).findByClassName(
+ 'react-monaco-editor-container'
+ );
+ const textarea = await editor.findByClassName('monaco-mouse-cursor-text');
- public async confirmSave() {
- await testSubjects.setValue('saveModalConfirmText', 'change');
- await testSubjects.click('confirmModalConfirmButton');
- }
+ await textarea.click();
+ await this.browser.pressKeys(script);
+ }
+ public async save() {
+ await this.testSubjects.click('fieldSaveButton');
+ }
- public async confirmDelete() {
- await testSubjects.setValue('deleteModalConfirmText', 'remove');
- await testSubjects.click('confirmModalConfirmButton');
- }
+ public async confirmSave() {
+ await this.testSubjects.setValue('saveModalConfirmText', 'change');
+ await this.testSubjects.click('confirmModalConfirmButton');
}
- return new FieldEditor();
+ public async confirmDelete() {
+ await this.testSubjects.setValue('deleteModalConfirmText', 'remove');
+ await this.testSubjects.click('confirmModalConfirmButton');
+ }
}
diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts
index b6887bc38b93e..f37b0b544fd66 100644
--- a/test/functional/services/index.ts
+++ b/test/functional/services/index.ts
@@ -31,7 +31,7 @@ import { FilterBarService } from './filter_bar';
import { FlyoutProvider } from './flyout';
import { GlobalNavProvider } from './global_nav';
import { InspectorProvider } from './inspector';
-import { FieldEditorProvider } from './field_editor';
+import { FieldEditorService } from './field_editor';
import { ManagementMenuProvider } from './management';
import { QueryBarProvider } from './query_bar';
import { RemoteProvider } from './remote';
@@ -75,7 +75,7 @@ export const services = {
browser: BrowserProvider,
pieChart: PieChartProvider,
inspector: InspectorProvider,
- fieldEditor: FieldEditorProvider,
+ fieldEditor: FieldEditorService,
vegaDebugInspector: VegaDebugInspectorViewProvider,
appsMenu: AppsMenuProvider,
globalNav: GlobalNavProvider,
From 29b7d1d448ba5d15f0f3ebfb9773f1758b8205ee Mon Sep 17 00:00:00 2001
From: Spencer
Date: Wed, 26 May 2021 01:58:05 -0600
Subject: [PATCH 07/17] [ftr] migrate "dataGrid" service to FtrService class
(#100593)
Co-authored-by: spalger
---
test/functional/services/data_grid.ts | 443 +++++++++++++-------------
test/functional/services/index.ts | 4 +-
2 files changed, 220 insertions(+), 227 deletions(-)
diff --git a/test/functional/services/data_grid.ts b/test/functional/services/data_grid.ts
index ee50185db8c68..a00587c978977 100644
--- a/test/functional/services/data_grid.ts
+++ b/test/functional/services/data_grid.ts
@@ -7,7 +7,7 @@
*/
import { chunk } from 'lodash';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
import { WebElementWrapper } from './lib/web_element_wrapper';
interface TabbedGridData {
@@ -19,261 +19,254 @@ interface SelectOptions {
rowIndex: number;
}
-export function DataGridProvider({ getService, getPageObjects }: FtrProviderContext) {
- const find = getService('find');
- const testSubjects = getService('testSubjects');
- const PageObjects = getPageObjects(['common', 'header']);
- const retry = getService('retry');
-
- class DataGrid {
- async getDataGridTableData(): Promise {
- const table = await find.byCssSelector('.euiDataGrid');
- const $ = await table.parseDomContent();
-
- const columns = $('.euiDataGridHeaderCell__content')
- .toArray()
- .map((cell) => $(cell).text());
- const cells = $.findTestSubjects('dataGridRowCell')
- .toArray()
- .map((cell) => $(cell).text());
-
- const rows = chunk(cells, columns.length);
-
- return {
- columns,
- rows,
- };
- }
-
- /**
- * Converts the data grid data into nested array
- * [ [cell1_in_row1, cell2_in_row1], [cell1_in_row2, cell2_in_row2] ]
- * @param element table
- */
- public async getDataFromElement(
- element: WebElementWrapper,
- cellDataTestSubj: string
- ): Promise {
- const $ = await element.parseDomContent();
- const columnNumber = $('.euiDataGridHeaderCell__content').length;
- const cells = $.findTestSubjects('dataGridRowCell')
- .toArray()
- .map((cell) =>
- $(cell)
- .findTestSubject(cellDataTestSubj)
- .text()
- .replace(/ /g, '')
- .trim()
- );
-
- return chunk(cells, columnNumber);
- }
-
- /**
- * Returns an array of data grid headers names
- */
- public async getHeaders() {
- const header = await testSubjects.find('dataGridWrapper > dataGridHeader');
- const $ = await header.parseDomContent();
- return $('.euiDataGridHeaderCell__content')
- .toArray()
- .map((cell) => $(cell).text());
- }
+export class DataGridService extends FtrService {
+ private readonly find = this.ctx.getService('find');
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly PageObjects = this.ctx.getPageObjects(['common', 'header']);
+ private readonly retry = this.ctx.getService('retry');
+
+ async getDataGridTableData(): Promise {
+ const table = await this.find.byCssSelector('.euiDataGrid');
+ const $ = await table.parseDomContent();
+
+ const columns = $('.euiDataGridHeaderCell__content')
+ .toArray()
+ .map((cell) => $(cell).text());
+ const cells = $.findTestSubjects('dataGridRowCell')
+ .toArray()
+ .map((cell) => $(cell).text());
+
+ const rows = chunk(cells, columns.length);
+
+ return {
+ columns,
+ rows,
+ };
+ }
- /**
- * Returns a grid cell element by row & column indexes.
- * The row offset equals 1 since the first row of data grid is the header row.
- * @param rowIndex data row index starting from 1 (1 means 1st row)
- * @param columnIndex column index starting from 1 (1 means 1st column)
- */
- public async getCellElement(rowIndex: number, columnIndex: number) {
- const table = await find.byCssSelector('.euiDataGrid');
- const $ = await table.parseDomContent();
- const columnNumber = $('.euiDataGridHeaderCell__content').length;
- return await find.byCssSelector(
- `[data-test-subj="dataGridWrapper"] [data-test-subj="dataGridRowCell"]:nth-of-type(${
- columnNumber * (rowIndex - 1) + columnIndex + 1
- })`
+ /**
+ * Converts the data grid data into nested array
+ * [ [cell1_in_row1, cell2_in_row1], [cell1_in_row2, cell2_in_row2] ]
+ * @param element table
+ */
+ public async getDataFromElement(
+ element: WebElementWrapper,
+ cellDataTestSubj: string
+ ): Promise {
+ const $ = await element.parseDomContent();
+ const columnNumber = $('.euiDataGridHeaderCell__content').length;
+ const cells = $.findTestSubjects('dataGridRowCell')
+ .toArray()
+ .map((cell) =>
+ $(cell)
+ .findTestSubject(cellDataTestSubj)
+ .text()
+ .replace(/ /g, '')
+ .trim()
);
- }
- public async getDocCount(): Promise {
- const grid = await find.byCssSelector('[data-document-number]');
- return Number(await grid.getAttribute('data-document-number'));
- }
+ return chunk(cells, columnNumber);
+ }
- public async getFields() {
- const cells = await find.allByCssSelector('.euiDataGridRowCell');
-
- const rows: string[][] = [];
- let rowIdx = -1;
- for (const cell of cells) {
- if (await cell.elementHasClass('euiDataGridRowCell--firstColumn')) {
- // first column contains expand icon
- rowIdx++;
- rows[rowIdx] = [];
- }
- if (!(await cell.elementHasClass('euiDataGridRowCell--controlColumn'))) {
- rows[rowIdx].push(await cell.getVisibleText());
- }
- }
- return rows;
- }
+ /**
+ * Returns an array of data grid headers names
+ */
+ public async getHeaders() {
+ const header = await this.testSubjects.find('dataGridWrapper > dataGridHeader');
+ const $ = await header.parseDomContent();
+ return $('.euiDataGridHeaderCell__content')
+ .toArray()
+ .map((cell) => $(cell).text());
+ }
- public async getTable(selector: string = 'docTable') {
- return await testSubjects.find(selector);
- }
+ /**
+ * Returns a grid cell element by row & column indexes.
+ * The row offset equals 1 since the first row of data grid is the header row.
+ * @param rowIndex data row index starting from 1 (1 means 1st row)
+ * @param columnIndex column index starting from 1 (1 means 1st column)
+ */
+ public async getCellElement(rowIndex: number, columnIndex: number) {
+ const table = await this.find.byCssSelector('.euiDataGrid');
+ const $ = await table.parseDomContent();
+ const columnNumber = $('.euiDataGridHeaderCell__content').length;
+ return await this.find.byCssSelector(
+ `[data-test-subj="dataGridWrapper"] [data-test-subj="dataGridRowCell"]:nth-of-type(${
+ columnNumber * (rowIndex - 1) + columnIndex + 1
+ })`
+ );
+ }
- public async getBodyRows(): Promise {
- return this.getDocTableRows();
- }
+ public async getDocCount(): Promise {
+ const grid = await this.find.byCssSelector('[data-document-number]');
+ return Number(await grid.getAttribute('data-document-number'));
+ }
- /**
- * Returns an array of rows (which are array of cells)
- */
- public async getDocTableRows() {
- const table = await this.getTable();
- if (!table) {
- return [];
+ public async getFields() {
+ const cells = await this.find.allByCssSelector('.euiDataGridRowCell');
+
+ const rows: string[][] = [];
+ let rowIdx = -1;
+ for (const cell of cells) {
+ if (await cell.elementHasClass('euiDataGridRowCell--firstColumn')) {
+ // first column contains expand icon
+ rowIdx++;
+ rows[rowIdx] = [];
}
- const cells = await table.findAllByCssSelector('.euiDataGridRowCell');
-
- const rows: WebElementWrapper[][] = [];
- let rowIdx = -1;
- for (const cell of cells) {
- if (await cell.elementHasClass('euiDataGridRowCell--firstColumn')) {
- rowIdx++;
- rows[rowIdx] = [];
- }
- rows[rowIdx].push(cell);
+ if (!(await cell.elementHasClass('euiDataGridRowCell--controlColumn'))) {
+ rows[rowIdx].push(await cell.getVisibleText());
}
- return rows;
- }
-
- /**
- * Returns an array of cells for that row
- */
- public async getRow(options: SelectOptions): Promise {
- return (await this.getBodyRows())[options.rowIndex];
}
+ return rows;
+ }
- public async clickRowToggle(
- options: SelectOptions = { isAnchorRow: false, rowIndex: 0 }
- ): Promise {
- const row = await this.getRow(options);
- const toggle = await row[0];
- await toggle.click();
- }
+ public async getTable(selector: string = 'docTable') {
+ return await this.testSubjects.find(selector);
+ }
- public async getDetailsRows(): Promise {
- return await testSubjects.findAll('docTableDetailsFlyout');
- }
+ public async getBodyRows(): Promise {
+ return this.getDocTableRows();
+ }
- public async closeFlyout() {
- await testSubjects.click('euiFlyoutCloseButton');
+ /**
+ * Returns an array of rows (which are array of cells)
+ */
+ public async getDocTableRows() {
+ const table = await this.getTable();
+ if (!table) {
+ return [];
}
-
- public async getHeaderFields(): Promise {
- const result = await find.allByCssSelector('.euiDataGridHeaderCell__content');
- const textArr = [];
- let idx = 0;
- for (const cell of result) {
- if (idx > 1) {
- textArr.push(await cell.getVisibleText());
- }
- idx++;
+ const cells = await table.findAllByCssSelector('.euiDataGridRowCell');
+
+ const rows: WebElementWrapper[][] = [];
+ let rowIdx = -1;
+ for (const cell of cells) {
+ if (await cell.elementHasClass('euiDataGridRowCell--firstColumn')) {
+ rowIdx++;
+ rows[rowIdx] = [];
}
- return Promise.resolve(textArr);
+ rows[rowIdx].push(cell);
}
+ return rows;
+ }
- public async getRowActions(
- options: SelectOptions = { isAnchorRow: false, rowIndex: 0 }
- ): Promise {
- const detailsRow = (await this.getDetailsRows())[options.rowIndex];
- return await detailsRow.findAllByTestSubject('~docTableRowAction');
- }
+ /**
+ * Returns an array of cells for that row
+ */
+ public async getRow(options: SelectOptions): Promise {
+ return (await this.getBodyRows())[options.rowIndex];
+ }
- public async openColMenuByField(field: string) {
- await retry.waitFor('header cell action being displayed', async () => {
- // to prevent flakiness
- await testSubjects.click(`dataGridHeaderCell-${field}`);
- return await testSubjects.exists(`dataGridHeaderCellActionGroup-${field}`);
- });
- }
+ public async clickRowToggle(
+ options: SelectOptions = { isAnchorRow: false, rowIndex: 0 }
+ ): Promise {
+ const row = await this.getRow(options);
+ const toggle = await row[0];
+ await toggle.click();
+ }
- public async clickDocSortAsc(field?: string, sortText = 'Sort New-Old') {
- if (field) {
- await this.openColMenuByField(field);
- } else {
- await find.clickByCssSelector('.euiDataGridHeaderCell__button');
- }
- await find.clickByButtonText(sortText);
- }
+ public async getDetailsRows(): Promise {
+ return await this.testSubjects.findAll('docTableDetailsFlyout');
+ }
- public async clickDocSortDesc(field?: string, sortText = 'Sort Old-New') {
- if (field) {
- await this.openColMenuByField(field);
- } else {
- await find.clickByCssSelector('.euiDataGridHeaderCell__button');
- }
- await find.clickByButtonText(sortText);
- }
+ public async closeFlyout() {
+ await this.testSubjects.click('euiFlyoutCloseButton');
+ }
- public async clickRemoveColumn(field?: string) {
- if (field) {
- await this.openColMenuByField(field);
- } else {
- await find.clickByCssSelector('.euiDataGridHeaderCell__button');
+ public async getHeaderFields(): Promise {
+ const result = await this.find.allByCssSelector('.euiDataGridHeaderCell__content');
+ const textArr = [];
+ let idx = 0;
+ for (const cell of result) {
+ if (idx > 1) {
+ textArr.push(await cell.getVisibleText());
}
- await find.clickByButtonText('Remove column');
- }
- public async getDetailsRow(): Promise {
- const detailRows = await this.getDetailsRows();
- return detailRows[0];
- }
- public async addInclusiveFilter(
- detailsRow: WebElementWrapper,
- fieldName: string
- ): Promise {
- const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
- const addInclusiveFilterButton = await this.getAddInclusiveFilterButton(tableDocViewRow);
- await addInclusiveFilterButton.click();
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ idx++;
}
+ return Promise.resolve(textArr);
+ }
- public async getAddInclusiveFilterButton(
- tableDocViewRow: WebElementWrapper
- ): Promise {
- return await tableDocViewRow.findByTestSubject(`~addInclusiveFilterButton`);
- }
+ public async getRowActions(
+ options: SelectOptions = { isAnchorRow: false, rowIndex: 0 }
+ ): Promise {
+ const detailsRow = (await this.getDetailsRows())[options.rowIndex];
+ return await detailsRow.findAllByTestSubject('~docTableRowAction');
+ }
- public async getTableDocViewRow(
- detailsRow: WebElementWrapper,
- fieldName: string
- ): Promise {
- return await detailsRow.findByTestSubject(`~tableDocViewRow-${fieldName}`);
- }
+ public async openColMenuByField(field: string) {
+ await this.retry.waitFor('header cell action being displayed', async () => {
+ // to prevent flakiness
+ await this.testSubjects.click(`dataGridHeaderCell-${field}`);
+ return await this.testSubjects.exists(`dataGridHeaderCellActionGroup-${field}`);
+ });
+ }
- public async getRemoveInclusiveFilterButton(
- tableDocViewRow: WebElementWrapper
- ): Promise {
- return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`);
+ public async clickDocSortAsc(field?: string, sortText = 'Sort New-Old') {
+ if (field) {
+ await this.openColMenuByField(field);
+ } else {
+ await this.find.clickByCssSelector('.euiDataGridHeaderCell__button');
}
+ await this.find.clickByButtonText(sortText);
+ }
- public async removeInclusiveFilter(
- detailsRow: WebElementWrapper,
- fieldName: string
- ): Promise {
- const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
- const addInclusiveFilterButton = await this.getRemoveInclusiveFilterButton(tableDocViewRow);
- await addInclusiveFilterButton.click();
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ public async clickDocSortDesc(field?: string, sortText = 'Sort Old-New') {
+ if (field) {
+ await this.openColMenuByField(field);
+ } else {
+ await this.find.clickByCssSelector('.euiDataGridHeaderCell__button');
}
+ await this.find.clickByButtonText(sortText);
+ }
- public async hasNoResults() {
- return await find.existsByCssSelector('.euiDataGrid__noResults');
+ public async clickRemoveColumn(field?: string) {
+ if (field) {
+ await this.openColMenuByField(field);
+ } else {
+ await this.find.clickByCssSelector('.euiDataGridHeaderCell__button');
}
+ await this.find.clickByButtonText('Remove column');
+ }
+ public async getDetailsRow(): Promise {
+ const detailRows = await this.getDetailsRows();
+ return detailRows[0];
+ }
+ public async addInclusiveFilter(detailsRow: WebElementWrapper, fieldName: string): Promise {
+ const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
+ const addInclusiveFilterButton = await this.getAddInclusiveFilterButton(tableDocViewRow);
+ await addInclusiveFilterButton.click();
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
+
+ public async getAddInclusiveFilterButton(
+ tableDocViewRow: WebElementWrapper
+ ): Promise {
+ return await tableDocViewRow.findByTestSubject(`~addInclusiveFilterButton`);
}
- return new DataGrid();
+ public async getTableDocViewRow(
+ detailsRow: WebElementWrapper,
+ fieldName: string
+ ): Promise {
+ return await detailsRow.findByTestSubject(`~tableDocViewRow-${fieldName}`);
+ }
+
+ public async getRemoveInclusiveFilterButton(
+ tableDocViewRow: WebElementWrapper
+ ): Promise {
+ return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`);
+ }
+
+ public async removeInclusiveFilter(
+ detailsRow: WebElementWrapper,
+ fieldName: string
+ ): Promise {
+ const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
+ const addInclusiveFilterButton = await this.getRemoveInclusiveFilterButton(tableDocViewRow);
+ await addInclusiveFilterButton.click();
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
+
+ public async hasNoResults() {
+ return await this.find.existsByCssSelector('.euiDataGrid__noResults');
+ }
}
diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts
index f37b0b544fd66..f5415e34c29e8 100644
--- a/test/functional/services/index.ts
+++ b/test/functional/services/index.ts
@@ -37,7 +37,7 @@ import { QueryBarProvider } from './query_bar';
import { RemoteProvider } from './remote';
import { RenderableProvider } from './renderable';
import { ToastsProvider } from './toasts';
-import { DataGridProvider } from './data_grid';
+import { DataGridService } from './data_grid';
import {
PieChartProvider,
ElasticChartProvider,
@@ -69,7 +69,7 @@ export const services = {
dashboardPanelActions: DashboardPanelActionsProvider,
flyout: FlyoutProvider,
comboBox: ComboBoxProvider,
- dataGrid: DataGridProvider,
+ dataGrid: DataGridService,
embedding: EmbeddingProvider,
renderable: RenderableProvider,
browser: BrowserProvider,
From c42f6c3063d41b626e9711645a7ea4f7b2ba6e4a Mon Sep 17 00:00:00 2001
From: Yaroslav Kuznietsov
Date: Wed, 26 May 2021 11:34:08 +0300
Subject: [PATCH 08/17] Fixed comparing real value with formatted according to
mode. (#100456)
Before this part of code was comparing clean data, which came from dataset, with X/Y values. They were true according to normal mode, but in percentage mode, for example, it was comparing absolute value with percentage value. To avoid it, need to compare datum (feature #822 from elastic/elastic-charts) of geometry with clean value from row info.
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../charts/public/static/utils/transform_click_event.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/plugins/charts/public/static/utils/transform_click_event.ts b/src/plugins/charts/public/static/utils/transform_click_event.ts
index 0c303b92bf1a1..844e2c3b301fa 100644
--- a/src/plugins/charts/public/static/utils/transform_click_event.ts
+++ b/src/plugins/charts/public/static/utils/transform_click_event.ts
@@ -152,9 +152,9 @@ const rowFindPredicate = (
) => (row: Datatable['rows'][number]): boolean =>
(geometry === null ||
(xAccessor !== null &&
- getAccessorValue(row, xAccessor) === geometry.x &&
+ getAccessorValue(row, xAccessor) === getAccessorValue(geometry.datum, xAccessor) &&
yAccessor !== null &&
- getAccessorValue(row, yAccessor) === geometry.y &&
+ getAccessorValue(row, yAccessor) === getAccessorValue(geometry.datum, yAccessor) &&
(splitChartAccessor === undefined ||
(splitChartValue !== undefined &&
getAccessorValue(row, splitChartAccessor) === splitChartValue)))) &&
From 749c69b93601f6daab6a0fd162136ae1a9136bf0 Mon Sep 17 00:00:00 2001
From: Spencer
Date: Wed, 26 May 2021 01:37:34 -0700
Subject: [PATCH 09/17] [ftr] migrate "listingTable" service to FtrService
class (#100606)
Co-authored-by: spalger
---
test/functional/services/index.ts | 4 +-
test/functional/services/listing_table.ts | 339 +++++++++++-----------
2 files changed, 170 insertions(+), 173 deletions(-)
diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts
index f5415e34c29e8..b0ac8f50624a3 100644
--- a/test/functional/services/index.ts
+++ b/test/functional/services/index.ts
@@ -43,7 +43,7 @@ import {
ElasticChartProvider,
VegaDebugInspectorViewProvider,
} from './visualizations';
-import { ListingTableProvider } from './listing_table';
+import { ListingTableService } from './listing_table';
import { SavedQueryManagementComponentProvider } from './saved_query_management_component';
import { KibanaSupertestProvider } from './supertest';
import { MenuToggleProvider } from './menu_toggle';
@@ -63,7 +63,7 @@ export const services = {
dashboardVisualizations: DashboardVisualizationProvider,
dashboardExpect: DashboardExpectProvider,
failureDebugging: FailureDebuggingProvider,
- listingTable: ListingTableProvider,
+ listingTable: ListingTableService,
dashboardAddPanel: DashboardAddPanelProvider,
dashboardReplacePanel: DashboardReplacePanelProvider,
dashboardPanelActions: DashboardPanelActionsProvider,
diff --git a/test/functional/services/listing_table.ts b/test/functional/services/listing_table.ts
index 0e1fa4a7b2117..79678cf7a812b 100644
--- a/test/functional/services/listing_table.ts
+++ b/test/functional/services/listing_table.ts
@@ -7,202 +7,199 @@
*/
import expect from '@kbn/expect';
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
-type AppName = 'visualize' | 'dashboard' | 'map';
+type AppName = keyof typeof PREFIX_MAP;
+const PREFIX_MAP = { visualize: 'vis', dashboard: 'dashboard', map: 'map' };
-export function ListingTableProvider({ getService, getPageObjects }: FtrProviderContext) {
- const testSubjects = getService('testSubjects');
- const find = getService('find');
- const log = getService('log');
- const retry = getService('retry');
- const { common, header } = getPageObjects(['common', 'header']);
- const prefixMap = { visualize: 'vis', dashboard: 'dashboard', map: 'map' };
+export class ListingTableService extends FtrService {
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly find = this.ctx.getService('find');
+ private readonly log = this.ctx.getService('log');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly common = this.ctx.getPageObjects(['common']).common;
+ private readonly header = this.ctx.getPageObjects(['header']).header;
- class ListingTable {
- private async getSearchFilter() {
- return await testSubjects.find('tableListSearchBox');
- }
+ private async getSearchFilter() {
+ return await this.testSubjects.find('tableListSearchBox');
+ }
- /**
- * Returns search input value on landing page
- */
- public async getSearchFilterValue() {
- const searchFilter = await this.getSearchFilter();
- return await searchFilter.getAttribute('value');
- }
+ /**
+ * Returns search input value on landing page
+ */
+ public async getSearchFilterValue() {
+ const searchFilter = await this.getSearchFilter();
+ return await searchFilter.getAttribute('value');
+ }
- /**
- * Clears search input on landing page
- */
- public async clearSearchFilter() {
- const searchFilter = await this.getSearchFilter();
- await searchFilter.clearValue();
- await searchFilter.click();
- }
+ /**
+ * Clears search input on landing page
+ */
+ public async clearSearchFilter() {
+ const searchFilter = await this.getSearchFilter();
+ await searchFilter.clearValue();
+ await searchFilter.click();
+ }
- private async getAllItemsNamesOnCurrentPage(): Promise {
- const visualizationNames = [];
- const links = await find.allByCssSelector('.euiTableRow .euiLink');
- for (let i = 0; i < links.length; i++) {
- visualizationNames.push(await links[i].getVisibleText());
- }
- log.debug(`Found ${visualizationNames.length} visualizations on current page`);
- return visualizationNames;
+ private async getAllItemsNamesOnCurrentPage(): Promise {
+ const visualizationNames = [];
+ const links = await this.find.allByCssSelector('.euiTableRow .euiLink');
+ for (let i = 0; i < links.length; i++) {
+ visualizationNames.push(await links[i].getVisibleText());
}
+ this.log.debug(`Found ${visualizationNames.length} visualizations on current page`);
+ return visualizationNames;
+ }
- public async waitUntilTableIsLoaded() {
- return retry.try(async () => {
- const isLoaded = await find.existsByDisplayedByCssSelector(
- '[data-test-subj="itemsInMemTable"]:not(.euiBasicTable-loading)'
- );
-
- if (isLoaded) {
- return true;
- } else {
- throw new Error('Waiting');
- }
- });
- }
+ public async waitUntilTableIsLoaded() {
+ return this.retry.try(async () => {
+ const isLoaded = await this.find.existsByDisplayedByCssSelector(
+ '[data-test-subj="itemsInMemTable"]:not(.euiBasicTable-loading)'
+ );
- /**
- * Navigates through all pages on Landing page and returns array of items names
- */
- public async getAllItemsNames(): Promise {
- log.debug('ListingTable.getAllItemsNames');
- let morePages = true;
- let visualizationNames: string[] = [];
- while (morePages) {
- visualizationNames = visualizationNames.concat(await this.getAllItemsNamesOnCurrentPage());
- morePages = !(
- (await testSubjects.getAttribute('pagination-button-next', 'disabled')) === 'true'
- );
- if (morePages) {
- await testSubjects.click('pagerNextButton');
- await header.waitUntilLoadingHasFinished();
- }
+ if (isLoaded) {
+ return true;
+ } else {
+ throw new Error('Waiting');
}
- return visualizationNames;
- }
+ });
+ }
- /**
- * Returns items count on landing page
- */
- public async expectItemsCount(appName: AppName, count: number) {
- await retry.try(async () => {
- const elements = await find.allByCssSelector(
- `[data-test-subj^="${prefixMap[appName]}ListingTitleLink"]`
- );
- expect(elements.length).to.equal(count);
- });
+ /**
+ * Navigates through all pages on Landing page and returns array of items names
+ */
+ public async getAllItemsNames(): Promise {
+ this.log.debug('ListingTable.getAllItemsNames');
+ let morePages = true;
+ let visualizationNames: string[] = [];
+ while (morePages) {
+ visualizationNames = visualizationNames.concat(await this.getAllItemsNamesOnCurrentPage());
+ morePages = !(
+ (await this.testSubjects.getAttribute('pagination-button-next', 'disabled')) === 'true'
+ );
+ if (morePages) {
+ await this.testSubjects.click('pagerNextButton');
+ await this.header.waitUntilLoadingHasFinished();
+ }
}
+ return visualizationNames;
+ }
- /**
- * Types name into search field on Landing page and waits till search completed
- * @param name item name
- */
- public async searchForItemWithName(name: string, { escape = true }: { escape?: boolean } = {}) {
- log.debug(`searchForItemWithName: ${name}`);
-
- await retry.try(async () => {
- const searchFilter = await this.getSearchFilter();
- await searchFilter.clearValue();
- await searchFilter.click();
-
- if (escape) {
- name = name
- // Note: this replacement of - to space is to preserve original logic but I'm not sure why or if it's needed.
- .replace('-', ' ')
- // Remove `[*]` from search as it is not supported by EUI Query's syntax.
- .replace(/ *\[[^)]*\] */g, '');
- }
-
- await searchFilter.type(name);
- await common.pressEnterKey();
- });
+ /**
+ * Returns items count on landing page
+ */
+ public async expectItemsCount(appName: AppName, count: number) {
+ await this.retry.try(async () => {
+ const elements = await this.find.allByCssSelector(
+ `[data-test-subj^="${PREFIX_MAP[appName]}ListingTitleLink"]`
+ );
+ expect(elements.length).to.equal(count);
+ });
+ }
- await header.waitUntilLoadingHasFinished();
- }
+ /**
+ * Types name into search field on Landing page and waits till search completed
+ * @param name item name
+ */
+ public async searchForItemWithName(name: string, { escape = true }: { escape?: boolean } = {}) {
+ this.log.debug(`searchForItemWithName: ${name}`);
- /**
- * Searches for item on Landing page and retruns items count that match `ListingTitleLink-${name}` pattern
- */
- public async searchAndExpectItemsCount(appName: AppName, name: string, count: number) {
- await this.searchForItemWithName(name);
- await retry.try(async () => {
- const links = await testSubjects.findAll(
- `${prefixMap[appName]}ListingTitleLink-${name.replace(/ /g, '-')}`
- );
- expect(links.length).to.equal(count);
- });
- }
+ await this.retry.try(async () => {
+ const searchFilter = await this.getSearchFilter();
+ await searchFilter.clearValue();
+ await searchFilter.click();
- public async clickDeleteSelected() {
- await testSubjects.click('deleteSelectedItems');
- }
+ if (escape) {
+ name = name
+ // Note: this replacement of - to space is to preserve original logic but I'm not sure why or if it's needed.
+ .replace('-', ' ')
+ // Remove `[*]` from search as it is not supported by EUI Query's syntax.
+ .replace(/ *\[[^)]*\] */g, '');
+ }
- public async clickItemCheckbox(id: string) {
- await testSubjects.click(`checkboxSelectRow-${id}`);
- }
+ await searchFilter.type(name);
+ await this.common.pressEnterKey();
+ });
- /**
- * Searches for item by name, selects checbox and deletes it
- * @param name item name
- * @param id row id
- */
- public async deleteItem(name: string, id: string) {
- await this.searchForItemWithName(name);
- await this.clickItemCheckbox(id);
- await this.clickDeleteSelected();
- await common.clickConfirmOnModal();
- }
+ await this.header.waitUntilLoadingHasFinished();
+ }
- /**
- * Clicks item on Landing page by link name if it is present
- */
- public async clickItemLink(appName: AppName, name: string) {
- await testSubjects.click(
- `${prefixMap[appName]}ListingTitleLink-${name.split(' ').join('-')}`
+ /**
+ * Searches for item on Landing page and retruns items count that match `ListingTitleLink-${name}` pattern
+ */
+ public async searchAndExpectItemsCount(appName: AppName, name: string, count: number) {
+ await this.searchForItemWithName(name);
+ await this.retry.try(async () => {
+ const links = await this.testSubjects.findAll(
+ `${PREFIX_MAP[appName]}ListingTitleLink-${name.replace(/ /g, '-')}`
);
- }
+ expect(links.length).to.equal(count);
+ });
+ }
- /**
- * Checks 'SelectAll' checkbox on
- */
- public async checkListingSelectAllCheckbox() {
- const element = await testSubjects.find('checkboxSelectAll');
- const isSelected = await element.isSelected();
- if (!isSelected) {
- log.debug(`checking checkbox "checkboxSelectAll"`);
- await testSubjects.click('checkboxSelectAll');
- }
- }
+ public async clickDeleteSelected() {
+ await this.testSubjects.click('deleteSelectedItems');
+ }
- /**
- * Clicks NewItem button on Landing page
- * @param promptBtnTestSubj testSubj locator for Prompt button
- */
- public async clickNewButton(promptBtnTestSubj: string): Promise {
- await retry.tryForTime(20000, async () => {
- // newItemButton button is only visible when there are items in the listing table is displayed.
- const isnNewItemButtonPresent = await testSubjects.exists('newItemButton', {
- timeout: 10000,
- });
- if (isnNewItemButtonPresent) {
- await testSubjects.click('newItemButton');
- } else {
- // no items exist, click createPromptButton to create new dashboard/visualization
- await testSubjects.click(promptBtnTestSubj);
- }
- });
+ public async clickItemCheckbox(id: string) {
+ await this.testSubjects.click(`checkboxSelectRow-${id}`);
+ }
+
+ /**
+ * Searches for item by name, selects checbox and deletes it
+ * @param name item name
+ * @param id row id
+ */
+ public async deleteItem(name: string, id: string) {
+ await this.searchForItemWithName(name);
+ await this.clickItemCheckbox(id);
+ await this.clickDeleteSelected();
+ await this.common.clickConfirmOnModal();
+ }
+
+ /**
+ * Clicks item on Landing page by link name if it is present
+ */
+ public async clickItemLink(appName: AppName, name: string) {
+ await this.testSubjects.click(
+ `${PREFIX_MAP[appName]}ListingTitleLink-${name.split(' ').join('-')}`
+ );
+ }
+
+ /**
+ * Checks 'SelectAll' checkbox on
+ */
+ public async checkListingSelectAllCheckbox() {
+ const element = await this.testSubjects.find('checkboxSelectAll');
+ const isSelected = await element.isSelected();
+ if (!isSelected) {
+ this.log.debug(`checking checkbox "checkboxSelectAll"`);
+ await this.testSubjects.click('checkboxSelectAll');
}
+ }
- public async onListingPage(appName: AppName) {
- return await testSubjects.exists(`${appName}LandingPage`, {
- timeout: 5000,
+ /**
+ * Clicks NewItem button on Landing page
+ * @param promptBtnTestSubj testSubj locator for Prompt button
+ */
+ public async clickNewButton(promptBtnTestSubj: string): Promise {
+ await this.retry.tryForTime(20000, async () => {
+ // newItemButton button is only visible when there are items in the listing table is displayed.
+ const isnNewItemButtonPresent = await this.testSubjects.exists('newItemButton', {
+ timeout: 10000,
});
- }
+ if (isnNewItemButtonPresent) {
+ await this.testSubjects.click('newItemButton');
+ } else {
+ // no items exist, click createPromptButton to create new dashboard/visualization
+ await this.testSubjects.click(promptBtnTestSubj);
+ }
+ });
}
- return new ListingTable();
+ public async onListingPage(appName: AppName) {
+ return await this.testSubjects.exists(`${appName}LandingPage`, {
+ timeout: 5000,
+ });
+ }
}
From 987c7369578fbc7382979ce80713bfa327bc5e3b Mon Sep 17 00:00:00 2001
From: Spencer
Date: Wed, 26 May 2021 01:42:45 -0700
Subject: [PATCH 10/17] [ftr] migrate "docTable" service to FtrService class
(#100595)
Co-authored-by: spalger
---
test/functional/services/doc_table.ts | 323 +++++++++++++-------------
test/functional/services/index.ts | 4 +-
2 files changed, 160 insertions(+), 167 deletions(-)
diff --git a/test/functional/services/doc_table.ts b/test/functional/services/doc_table.ts
index 35c3531c70c41..6c73faec16b1a 100644
--- a/test/functional/services/doc_table.ts
+++ b/test/functional/services/doc_table.ts
@@ -6,177 +6,170 @@
* Side Public License, v 1.
*/
-import { FtrProviderContext } from '../ftr_provider_context';
+import { FtrService } from '../ftr_provider_context';
import { WebElementWrapper } from './lib/web_element_wrapper';
-export function DocTableProvider({ getService, getPageObjects }: FtrProviderContext) {
- const testSubjects = getService('testSubjects');
- const retry = getService('retry');
- const PageObjects = getPageObjects(['common', 'header']);
+interface SelectOptions {
+ isAnchorRow?: boolean;
+ rowIndex?: number;
+}
+
+export class DocTableService extends FtrService {
+ private readonly testSubjects = this.ctx.getService('testSubjects');
+ private readonly retry = this.ctx.getService('retry');
+ private readonly PageObjects = this.ctx.getPageObjects(['common', 'header']);
- interface SelectOptions {
- isAnchorRow?: boolean;
- rowIndex?: number;
+ public async getTable(selector?: string) {
+ return await this.testSubjects.find(selector ? selector : 'docTable');
}
- class DocTable {
- public async getTable(selector?: string) {
- return await testSubjects.find(selector ? selector : 'docTable');
- }
+ public async getRowsText() {
+ const table = await this.getTable();
+ const $ = await table.parseDomContent();
+ return $.findTestSubjects('~docTableRow')
+ .toArray()
+ .map((row: any) => $(row).text().trim());
+ }
- public async getRowsText() {
- const table = await this.getTable();
- const $ = await table.parseDomContent();
- return $.findTestSubjects('~docTableRow')
- .toArray()
- .map((row: any) => $(row).text().trim());
- }
-
- public async getBodyRows(): Promise {
- const table = await this.getTable();
- return await table.findAllByTestSubject('~docTableRow');
- }
-
- public async getAnchorRow(): Promise {
- const table = await this.getTable();
- return await table.findByTestSubject('~docTableAnchorRow');
- }
-
- public async getRow({
- isAnchorRow = false,
- rowIndex = 0,
- }: SelectOptions = {}): Promise {
- return isAnchorRow ? await this.getAnchorRow() : (await this.getBodyRows())[rowIndex];
- }
-
- public async getDetailsRow(): Promise {
- const table = await this.getTable();
- return await table.findByCssSelector('[data-test-subj~="docTableDetailsRow"]');
- }
-
- public async getAnchorDetailsRow(): Promise {
- const table = await this.getTable();
- return await table.findByCssSelector(
- '[data-test-subj~="docTableAnchorRow"] + [data-test-subj~="docTableDetailsRow"]'
- );
- }
-
- public async clickRowToggle(
- options: SelectOptions = { isAnchorRow: false, rowIndex: 0 }
- ): Promise {
- const row = await this.getRow(options);
- const toggle = await row.findByTestSubject('~docTableExpandToggleColumn');
- await toggle.click();
- }
-
- public async getDetailsRows(): Promise {
- const table = await this.getTable();
- return await table.findAllByCssSelector(
- '[data-test-subj~="docTableRow"] + [data-test-subj~="docTableDetailsRow"]'
- );
- }
-
- public async getRowActions({ isAnchorRow = false, rowIndex = 0 }: SelectOptions = {}): Promise<
- WebElementWrapper[]
- > {
- const detailsRow = isAnchorRow
- ? await this.getAnchorDetailsRow()
- : (await this.getDetailsRows())[rowIndex];
- return await detailsRow.findAllByTestSubject('~docTableRowAction');
- }
-
- public async getFields(options: { isAnchorRow: boolean } = { isAnchorRow: false }) {
- const table = await this.getTable();
- const $ = await table.parseDomContent();
- const rowLocator = options.isAnchorRow ? '~docTableAnchorRow' : '~docTableRow';
- const rows = $.findTestSubjects(rowLocator).toArray();
- return rows.map((row: any) =>
- $(row)
- .find('[data-test-subj~="docTableField"]')
- .toArray()
- .map((field: any) => $(field).text())
- );
- }
+ public async getBodyRows(): Promise {
+ const table = await this.getTable();
+ return await table.findAllByTestSubject('~docTableRow');
+ }
+
+ public async getAnchorRow(): Promise {
+ const table = await this.getTable();
+ return await table.findByTestSubject('~docTableAnchorRow');
+ }
+
+ public async getRow({
+ isAnchorRow = false,
+ rowIndex = 0,
+ }: SelectOptions = {}): Promise {
+ return isAnchorRow ? await this.getAnchorRow() : (await this.getBodyRows())[rowIndex];
+ }
+
+ public async getDetailsRow(): Promise {
+ const table = await this.getTable();
+ return await table.findByCssSelector('[data-test-subj~="docTableDetailsRow"]');
+ }
+
+ public async getAnchorDetailsRow(): Promise {
+ const table = await this.getTable();
+ return await table.findByCssSelector(
+ '[data-test-subj~="docTableAnchorRow"] + [data-test-subj~="docTableDetailsRow"]'
+ );
+ }
+
+ public async clickRowToggle(
+ options: SelectOptions = { isAnchorRow: false, rowIndex: 0 }
+ ): Promise {
+ const row = await this.getRow(options);
+ const toggle = await row.findByTestSubject('~docTableExpandToggleColumn');
+ await toggle.click();
+ }
+
+ public async getDetailsRows(): Promise {
+ const table = await this.getTable();
+ return await table.findAllByCssSelector(
+ '[data-test-subj~="docTableRow"] + [data-test-subj~="docTableDetailsRow"]'
+ );
+ }
+
+ public async getRowActions({ isAnchorRow = false, rowIndex = 0 }: SelectOptions = {}): Promise<
+ WebElementWrapper[]
+ > {
+ const detailsRow = isAnchorRow
+ ? await this.getAnchorDetailsRow()
+ : (await this.getDetailsRows())[rowIndex];
+ return await detailsRow.findAllByTestSubject('~docTableRowAction');
+ }
- public async getHeaderFields(selector?: string): Promise {
- const table = await this.getTable(selector);
- const $ = await table.parseDomContent();
- return $.findTestSubjects('~docTableHeaderField')
+ public async getFields(options: { isAnchorRow: boolean } = { isAnchorRow: false }) {
+ const table = await this.getTable();
+ const $ = await table.parseDomContent();
+ const rowLocator = options.isAnchorRow ? '~docTableAnchorRow' : '~docTableRow';
+ const rows = $.findTestSubjects(rowLocator).toArray();
+ return rows.map((row: any) =>
+ $(row)
+ .find('[data-test-subj~="docTableField"]')
.toArray()
- .map((field: any) => $(field).text().trim());
- }
-
- public async getHeaders(selector?: string): Promise {
- return this.getHeaderFields(selector);
- }
-
- public async getTableDocViewRow(
- detailsRow: WebElementWrapper,
- fieldName: string
- ): Promise {
- return await detailsRow.findByTestSubject(`~tableDocViewRow-${fieldName}`);
- }
-
- public async getAddInclusiveFilterButton(
- tableDocViewRow: WebElementWrapper
- ): Promise {
- return await tableDocViewRow.findByTestSubject(`~addInclusiveFilterButton`);
- }
-
- public async addInclusiveFilter(
- detailsRow: WebElementWrapper,
- fieldName: string
- ): Promise {
- const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
- const addInclusiveFilterButton = await this.getAddInclusiveFilterButton(tableDocViewRow);
- await addInclusiveFilterButton.click();
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
- }
-
- public async getRemoveInclusiveFilterButton(
- tableDocViewRow: WebElementWrapper
- ): Promise {
- return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`);
- }
-
- public async removeInclusiveFilter(
- detailsRow: WebElementWrapper,
- fieldName: string
- ): Promise {
- const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
- const addInclusiveFilterButton = await this.getRemoveInclusiveFilterButton(tableDocViewRow);
- await addInclusiveFilterButton.click();
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
- }
-
- public async getAddExistsFilterButton(
- tableDocViewRow: WebElementWrapper
- ): Promise {
- return await tableDocViewRow.findByTestSubject(`~addExistsFilterButton`);
- }
-
- public async addExistsFilter(detailsRow: WebElementWrapper, fieldName: string): Promise {
- const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
- const addInclusiveFilterButton = await this.getAddExistsFilterButton(tableDocViewRow);
- await addInclusiveFilterButton.click();
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
- }
-
- public async toggleRowExpanded({
- isAnchorRow = false,
- rowIndex = 0,
- }: SelectOptions = {}): Promise {
- await this.clickRowToggle({ isAnchorRow, rowIndex });
- await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
- return await retry.try(async () => {
- const row = isAnchorRow ? await this.getAnchorRow() : (await this.getBodyRows())[rowIndex];
- const detailsRow = await row.findByXpath(
- './following-sibling::*[@data-test-subj="docTableDetailsRow"]'
- );
- return detailsRow.findByTestSubject('~docViewer');
- });
- }
- }
-
- return new DocTable();
+ .map((field: any) => $(field).text())
+ );
+ }
+
+ public async getHeaderFields(selector?: string): Promise {
+ const table = await this.getTable(selector);
+ const $ = await table.parseDomContent();
+ return $.findTestSubjects('~docTableHeaderField')
+ .toArray()
+ .map((field: any) => $(field).text().trim());
+ }
+
+ public async getHeaders(selector?: string): Promise {
+ return this.getHeaderFields(selector);
+ }
+
+ public async getTableDocViewRow(
+ detailsRow: WebElementWrapper,
+ fieldName: string
+ ): Promise {
+ return await detailsRow.findByTestSubject(`~tableDocViewRow-${fieldName}`);
+ }
+
+ public async getAddInclusiveFilterButton(
+ tableDocViewRow: WebElementWrapper
+ ): Promise {
+ return await tableDocViewRow.findByTestSubject(`~addInclusiveFilterButton`);
+ }
+
+ public async addInclusiveFilter(detailsRow: WebElementWrapper, fieldName: string): Promise {
+ const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
+ const addInclusiveFilterButton = await this.getAddInclusiveFilterButton(tableDocViewRow);
+ await addInclusiveFilterButton.click();
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
+
+ public async getRemoveInclusiveFilterButton(
+ tableDocViewRow: WebElementWrapper
+ ): Promise {
+ return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`);
+ }
+
+ public async removeInclusiveFilter(
+ detailsRow: WebElementWrapper,
+ fieldName: string
+ ): Promise {
+ const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
+ const addInclusiveFilterButton = await this.getRemoveInclusiveFilterButton(tableDocViewRow);
+ await addInclusiveFilterButton.click();
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
+
+ public async getAddExistsFilterButton(
+ tableDocViewRow: WebElementWrapper
+ ): Promise {
+ return await tableDocViewRow.findByTestSubject(`~addExistsFilterButton`);
+ }
+
+ public async addExistsFilter(detailsRow: WebElementWrapper, fieldName: string): Promise {
+ const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName);
+ const addInclusiveFilterButton = await this.getAddExistsFilterButton(tableDocViewRow);
+ await addInclusiveFilterButton.click();
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ }
+
+ public async toggleRowExpanded({
+ isAnchorRow = false,
+ rowIndex = 0,
+ }: SelectOptions = {}): Promise {
+ await this.clickRowToggle({ isAnchorRow, rowIndex });
+ await this.PageObjects.header.awaitGlobalLoadingIndicatorHidden();
+ return await this.retry.try(async () => {
+ const row = isAnchorRow ? await this.getAnchorRow() : (await this.getBodyRows())[rowIndex];
+ const detailsRow = await row.findByXpath(
+ './following-sibling::*[@data-test-subj="docTableDetailsRow"]'
+ );
+ return detailsRow.findByTestSubject('~docViewer');
+ });
+ }
}
diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts
index b0ac8f50624a3..43f891ee4644f 100644
--- a/test/functional/services/index.ts
+++ b/test/functional/services/index.ts
@@ -25,7 +25,7 @@ import {
DashboardPanelActionsProvider,
DashboardVisualizationProvider,
} from './dashboard';
-import { DocTableProvider } from './doc_table';
+import { DocTableService } from './doc_table';
import { EmbeddingProvider } from './embedding';
import { FilterBarService } from './filter_bar';
import { FlyoutProvider } from './flyout';
@@ -57,7 +57,7 @@ export const services = {
queryBar: QueryBarProvider,
find: FindProvider,
testSubjects: TestSubjectsProvider,
- docTable: DocTableProvider,
+ docTable: DocTableService,
screenshots: ScreenshotsProvider,
snapshots: SnapshotsProvider,
dashboardVisualizations: DashboardVisualizationProvider,
From f915b6fe73f21eeb82e00c60641fa46c9a474acc Mon Sep 17 00:00:00 2001
From: Mikhail Shustov
Date: Wed, 26 May 2021 10:57:01 +0200
Subject: [PATCH 11/17] [telemetry] report config deprecations (#99887)
* return the list of changes config keys during deprecation
* gather changed config keys in the core
* adjust Security plugin deprecations tests
* update docs
* update interface
* update telemetry schema
* update spaces tests
* update tests in other x-pack plugins
* remove testing instruction
* improve tests. get rid of snapshots
---
.../kbn-config/src/config_service.mock.ts | 2 +
.../src/config_service.test.mocks.ts | 11 +-
.../kbn-config/src/config_service.test.ts | 25 +-
packages/kbn-config/src/config_service.ts | 12 +-
.../deprecation/apply_deprecations.test.ts | 29 ++-
.../src/deprecation/apply_deprecations.ts | 19 +-
packages/kbn-config/src/deprecation/index.ts | 1 +
packages/kbn-config/src/deprecation/types.ts | 10 +
packages/kbn-config/src/index.ts | 1 +
src/core/server/config/test_utils.ts | 2 +-
.../core_usage_data_service.mock.ts | 4 +
.../core_usage_data_service.test.ts | 225 ++++++++----------
.../core_usage_data_service.ts | 14 +-
src/core/server/core_usage_data/types.ts | 5 +
src/core/server/server.api.md | 5 +
src/core/server/server.ts | 2 +
.../collectors/core/core_usage_collector.ts | 17 ++
src/plugins/telemetry/schema/oss_plugins.json | 22 ++
.../reporting/server/config/index.test.ts | 2 +-
.../server/config_deprecations.test.ts | 2 +-
x-pack/plugins/spaces/server/config.test.ts | 2 +-
.../plugins/task_manager/server/index.test.ts | 2 +-
22 files changed, 269 insertions(+), 145 deletions(-)
diff --git a/packages/kbn-config/src/config_service.mock.ts b/packages/kbn-config/src/config_service.mock.ts
index 83fbf20b5c0b3..68adbba7c0ed7 100644
--- a/packages/kbn-config/src/config_service.mock.ts
+++ b/packages/kbn-config/src/config_service.mock.ts
@@ -26,11 +26,13 @@ const createConfigServiceMock = ({
addDeprecationProvider: jest.fn(),
validate: jest.fn(),
getHandledDeprecatedConfigs: jest.fn(),
+ getDeprecatedConfigPath$: jest.fn(),
};
mocked.atPath.mockReturnValue(new BehaviorSubject(atPath));
mocked.atPathSync.mockReturnValue(atPath);
mocked.getConfig$.mockReturnValue(new BehaviorSubject(new ObjectToConfigAdapter(getConfig$)));
+ mocked.getDeprecatedConfigPath$.mockReturnValue(new BehaviorSubject({ set: [], unset: [] }));
mocked.getUsedPaths.mockResolvedValue([]);
mocked.getUnusedPaths.mockResolvedValue([]);
mocked.isEnabledAtPath.mockResolvedValue(true);
diff --git a/packages/kbn-config/src/config_service.test.mocks.ts b/packages/kbn-config/src/config_service.test.mocks.ts
index d8da2852b9251..39aa551ae85f9 100644
--- a/packages/kbn-config/src/config_service.test.mocks.ts
+++ b/packages/kbn-config/src/config_service.test.mocks.ts
@@ -11,10 +11,17 @@ import type { applyDeprecations } from './deprecation/apply_deprecations';
jest.mock('../../../package.json', () => mockPackage);
+const changedPaths = {
+ set: ['foo'],
+ unset: ['bar.baz'],
+};
+
+export { changedPaths as mockedChangedPaths };
+
export const mockApplyDeprecations = jest.fn<
- Record,
+ ReturnType,
Parameters
->((config, deprecations, createAddDeprecation) => config);
+>((config, deprecations, createAddDeprecation) => ({ config, changedPaths }));
jest.mock('./deprecation/apply_deprecations', () => ({
applyDeprecations: mockApplyDeprecations,
diff --git a/packages/kbn-config/src/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts
index 64404341bc64d..c2d4f15b6d915 100644
--- a/packages/kbn-config/src/config_service.test.ts
+++ b/packages/kbn-config/src/config_service.test.ts
@@ -9,7 +9,7 @@
import { BehaviorSubject, Observable } from 'rxjs';
import { first, take } from 'rxjs/operators';
-import { mockApplyDeprecations } from './config_service.test.mocks';
+import { mockApplyDeprecations, mockedChangedPaths } from './config_service.test.mocks';
import { rawConfigServiceMock } from './raw/raw_config_service.mock';
import { schema } from '@kbn/config-schema';
@@ -420,7 +420,7 @@ test('logs deprecation warning during validation', async () => {
const addDeprecation = createAddDeprecation!('');
addDeprecation({ message: 'some deprecation message' });
addDeprecation({ message: 'another deprecation message' });
- return config;
+ return { config, changedPaths: mockedChangedPaths };
});
loggerMock.clear(logger);
@@ -446,12 +446,12 @@ test('does not log warnings for silent deprecations during validation', async ()
const addDeprecation = createAddDeprecation!('');
addDeprecation({ message: 'some deprecation message', silent: true });
addDeprecation({ message: 'another deprecation message' });
- return config;
+ return { config, changedPaths: mockedChangedPaths };
})
.mockImplementationOnce((config, deprecations, createAddDeprecation) => {
const addDeprecation = createAddDeprecation!('');
addDeprecation({ message: 'I am silent', silent: true });
- return config;
+ return { config, changedPaths: mockedChangedPaths };
});
loggerMock.clear(logger);
@@ -521,7 +521,7 @@ describe('getHandledDeprecatedConfigs', () => {
const addDeprecation = createAddDeprecation!(deprecation.path);
addDeprecation({ message: `some deprecation message`, documentationUrl: 'some-url' });
});
- return config;
+ return { config, changedPaths: mockedChangedPaths };
});
await configService.validate();
@@ -541,3 +541,18 @@ describe('getHandledDeprecatedConfigs', () => {
`);
});
});
+
+describe('getDeprecatedConfigPath$', () => {
+ it('returns all config paths changes during deprecation', async () => {
+ const rawConfig$ = new BehaviorSubject>({ key: 'value' });
+ const rawConfigProvider = rawConfigServiceMock.create({ rawConfig$ });
+
+ const configService = new ConfigService(rawConfigProvider, defaultEnv, logger);
+ await configService.setSchema('key', schema.string());
+ await configService.validate();
+
+ const deprecatedConfigPath$ = configService.getDeprecatedConfigPath$();
+ const deprecatedConfigPath = await deprecatedConfigPath$.pipe(first()).toPromise();
+ expect(deprecatedConfigPath).toEqual(mockedChangedPaths);
+ });
+});
diff --git a/packages/kbn-config/src/config_service.ts b/packages/kbn-config/src/config_service.ts
index 91927b4c7b5c9..a80680bd46dfc 100644
--- a/packages/kbn-config/src/config_service.ts
+++ b/packages/kbn-config/src/config_service.ts
@@ -22,6 +22,7 @@ import {
ConfigDeprecationProvider,
configDeprecationFactory,
DeprecatedConfigDetails,
+ ChangedDeprecatedPaths,
} from './deprecation';
import { LegacyObjectToConfigAdapter } from './legacy';
@@ -36,6 +37,10 @@ export class ConfigService {
private validated = false;
private readonly config$: Observable;
private lastConfig?: Config;
+ private readonly deprecatedConfigPaths = new BehaviorSubject({
+ set: [],
+ unset: [],
+ });
/**
* Whenever a config if read at a path, we mark that path as 'handled'. We can
@@ -57,7 +62,8 @@ export class ConfigService {
this.config$ = combineLatest([this.rawConfigProvider.getConfig$(), this.deprecations]).pipe(
map(([rawConfig, deprecations]) => {
const migrated = applyDeprecations(rawConfig, deprecations);
- return new LegacyObjectToConfigAdapter(migrated);
+ this.deprecatedConfigPaths.next(migrated.changedPaths);
+ return new LegacyObjectToConfigAdapter(migrated.config);
}),
tap((config) => {
this.lastConfig = config;
@@ -191,6 +197,10 @@ export class ConfigService {
return config.getFlattenedPaths().filter((path) => isPathHandled(path, handledPaths));
}
+ public getDeprecatedConfigPath$() {
+ return this.deprecatedConfigPaths.asObservable();
+ }
+
private async logDeprecation() {
const rawConfig = await this.rawConfigProvider.getConfig$().pipe(take(1)).toPromise();
const deprecations = await this.deprecations.pipe(take(1)).toPromise();
diff --git a/packages/kbn-config/src/deprecation/apply_deprecations.test.ts b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts
index 47746967bbe5f..8ad1491c19c9b 100644
--- a/packages/kbn-config/src/deprecation/apply_deprecations.test.ts
+++ b/packages/kbn-config/src/deprecation/apply_deprecations.test.ts
@@ -82,7 +82,7 @@ describe('applyDeprecations', () => {
it('returns the migrated config', () => {
const initialConfig = { foo: 'bar', deprecated: 'deprecated', renamed: 'renamed' };
- const migrated = applyDeprecations(initialConfig, [
+ const { config: migrated } = applyDeprecations(initialConfig, [
wrapHandler(deprecations.unused('deprecated')),
wrapHandler(deprecations.rename('renamed', 'newname')),
]);
@@ -93,7 +93,7 @@ describe('applyDeprecations', () => {
it('does not alter the initial config', () => {
const initialConfig = { foo: 'bar', deprecated: 'deprecated' };
- const migrated = applyDeprecations(initialConfig, [
+ const { config: migrated } = applyDeprecations(initialConfig, [
wrapHandler(deprecations.unused('deprecated')),
]);
@@ -110,7 +110,7 @@ describe('applyDeprecations', () => {
return { unset: [{ path: 'unknown' }] };
});
- const migrated = applyDeprecations(
+ const { config: migrated } = applyDeprecations(
initialConfig,
[wrapHandler(handler, 'pathA')],
createAddDeprecation
@@ -128,7 +128,7 @@ describe('applyDeprecations', () => {
return { rewrite: [{ path: 'foo' }] };
});
- const migrated = applyDeprecations(
+ const { config: migrated } = applyDeprecations(
initialConfig,
[wrapHandler(handler, 'pathA')],
createAddDeprecation
@@ -136,4 +136,25 @@ describe('applyDeprecations', () => {
expect(migrated).toEqual(initialConfig);
});
+
+ it('returns a list of changes config paths', () => {
+ const addDeprecation = jest.fn();
+ const createAddDeprecation = jest.fn().mockReturnValue(addDeprecation);
+ const initialConfig = { foo: 'bar', deprecated: 'deprecated' };
+
+ const handler = jest.fn().mockImplementation((config) => {
+ return { set: [{ path: 'foo', value: 'bar' }], unset: [{ path: 'baz' }] };
+ });
+
+ const { changedPaths } = applyDeprecations(
+ initialConfig,
+ [wrapHandler(handler, 'pathA')],
+ createAddDeprecation
+ );
+
+ expect(changedPaths).toEqual({
+ set: ['foo'],
+ unset: ['baz'],
+ });
+ });
});
diff --git a/packages/kbn-config/src/deprecation/apply_deprecations.ts b/packages/kbn-config/src/deprecation/apply_deprecations.ts
index 092a5ced28371..d38ae98835831 100644
--- a/packages/kbn-config/src/deprecation/apply_deprecations.ts
+++ b/packages/kbn-config/src/deprecation/apply_deprecations.ts
@@ -8,7 +8,11 @@
import { cloneDeep, unset } from 'lodash';
import { set } from '@elastic/safer-lodash-set';
-import { ConfigDeprecationWithContext, AddConfigDeprecation } from './types';
+import type {
+ AddConfigDeprecation,
+ ChangedDeprecatedPaths,
+ ConfigDeprecationWithContext,
+} from './types';
const noopAddDeprecationFactory: () => AddConfigDeprecation = () => () => undefined;
/**
@@ -22,22 +26,31 @@ export const applyDeprecations = (
config: Record,
deprecations: ConfigDeprecationWithContext[],
createAddDeprecation: (pluginId: string) => AddConfigDeprecation = noopAddDeprecationFactory
-) => {
+): { config: Record; changedPaths: ChangedDeprecatedPaths } => {
const result = cloneDeep(config);
+ const changedPaths: ChangedDeprecatedPaths = {
+ set: [],
+ unset: [],
+ };
deprecations.forEach(({ deprecation, path }) => {
const commands = deprecation(result, path, createAddDeprecation(path));
if (commands) {
if (commands.set) {
+ changedPaths.set.push(...commands.set.map((c) => c.path));
commands.set.forEach(function ({ path: commandPath, value }) {
set(result, commandPath, value);
});
}
if (commands.unset) {
+ changedPaths.unset.push(...commands.unset.map((c) => c.path));
commands.unset.forEach(function ({ path: commandPath }) {
unset(result, commandPath);
});
}
}
});
- return result;
+ return {
+ config: result,
+ changedPaths,
+ };
};
diff --git a/packages/kbn-config/src/deprecation/index.ts b/packages/kbn-config/src/deprecation/index.ts
index 48576e6d830be..ce10bafd9c575 100644
--- a/packages/kbn-config/src/deprecation/index.ts
+++ b/packages/kbn-config/src/deprecation/index.ts
@@ -14,6 +14,7 @@ export type {
AddConfigDeprecation,
ConfigDeprecationProvider,
DeprecatedConfigDetails,
+ ChangedDeprecatedPaths,
} from './types';
export { configDeprecationFactory } from './deprecation_factory';
export { applyDeprecations } from './apply_deprecations';
diff --git a/packages/kbn-config/src/deprecation/types.ts b/packages/kbn-config/src/deprecation/types.ts
index 6944f45c1e1d2..0522365ad76c1 100644
--- a/packages/kbn-config/src/deprecation/types.ts
+++ b/packages/kbn-config/src/deprecation/types.ts
@@ -55,6 +55,16 @@ export type ConfigDeprecation = (
addDeprecation: AddConfigDeprecation
) => void | ConfigDeprecationCommand;
+/**
+ * List of config paths changed during deprecation.
+ *
+ * @public
+ */
+export interface ChangedDeprecatedPaths {
+ set: string[];
+ unset: string[];
+}
+
/**
* Outcome of deprecation operation. Allows mutating config values in a declarative way.
*
diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts
index cf875d3daa4a2..294caba4e7048 100644
--- a/packages/kbn-config/src/index.ts
+++ b/packages/kbn-config/src/index.ts
@@ -13,6 +13,7 @@ export type {
ConfigDeprecationWithContext,
ConfigDeprecation,
ConfigDeprecationCommand,
+ ChangedDeprecatedPaths,
} from './deprecation';
export { applyDeprecations, configDeprecationFactory } from './deprecation';
diff --git a/src/core/server/config/test_utils.ts b/src/core/server/config/test_utils.ts
index 8e20e87e6f7d8..ab06ff50012b7 100644
--- a/src/core/server/config/test_utils.ts
+++ b/src/core/server/config/test_utils.ts
@@ -16,7 +16,7 @@ function collectDeprecations(
) {
const deprecations = provider(configDeprecationFactory);
const deprecationMessages: string[] = [];
- const migrated = applyDeprecations(
+ const { config: migrated } = applyDeprecations(
settings,
deprecations.map((deprecation) => ({
deprecation,
diff --git a/src/core/server/core_usage_data/core_usage_data_service.mock.ts b/src/core/server/core_usage_data/core_usage_data_service.mock.ts
index e09f595747c30..5fa67fecb2a8a 100644
--- a/src/core/server/core_usage_data/core_usage_data_service.mock.ts
+++ b/src/core/server/core_usage_data/core_usage_data_service.mock.ts
@@ -116,6 +116,10 @@ const createStartContractMock = () => {
maxImportExportSize: 10000,
maxImportPayloadBytes: 26214400,
},
+ deprecatedKeys: {
+ set: ['path.to.a.prop'],
+ unset: [],
+ },
},
environment: {
memory: {
diff --git a/src/core/server/core_usage_data/core_usage_data_service.test.ts b/src/core/server/core_usage_data/core_usage_data_service.test.ts
index dc74b65c8dcfc..95dd392016c17 100644
--- a/src/core/server/core_usage_data/core_usage_data_service.test.ts
+++ b/src/core/server/core_usage_data/core_usage_data_service.test.ts
@@ -91,7 +91,8 @@ describe('CoreUsageDataService', () => {
const savedObjectsStartPromise = Promise.resolve(
savedObjectsServiceMock.createStartContract()
);
- service.setup({ http, metrics, savedObjectsStartPromise });
+ const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$();
+ service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ });
const savedObjects = await savedObjectsStartPromise;
expect(savedObjects.createInternalRepository).toHaveBeenCalledTimes(1);
@@ -105,7 +106,13 @@ describe('CoreUsageDataService', () => {
const savedObjectsStartPromise = Promise.resolve(
savedObjectsServiceMock.createStartContract()
);
- const coreUsageData = service.setup({ http, metrics, savedObjectsStartPromise });
+ const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$();
+ const coreUsageData = service.setup({
+ http,
+ metrics,
+ savedObjectsStartPromise,
+ changedDeprecatedConfigPath$,
+ });
const typeRegistry = typeRegistryMock.create();
coreUsageData.registerType(typeRegistry);
@@ -126,7 +133,13 @@ describe('CoreUsageDataService', () => {
const savedObjectsStartPromise = Promise.resolve(
savedObjectsServiceMock.createStartContract()
);
- const coreUsageData = service.setup({ http, metrics, savedObjectsStartPromise });
+ const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$();
+ const coreUsageData = service.setup({
+ http,
+ metrics,
+ savedObjectsStartPromise,
+ changedDeprecatedConfigPath$,
+ });
const usageStatsClient = coreUsageData.getClient();
expect(usageStatsClient).toBeInstanceOf(CoreUsageStatsClient);
@@ -142,7 +155,11 @@ describe('CoreUsageDataService', () => {
const savedObjectsStartPromise = Promise.resolve(
savedObjectsServiceMock.createStartContract()
);
- service.setup({ http, metrics, savedObjectsStartPromise });
+ const changedDeprecatedConfigPath$ = new BehaviorSubject({
+ set: ['new.path'],
+ unset: ['deprecated.path'],
+ });
+ service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ });
const elasticsearch = elasticsearchServiceMock.createStart();
elasticsearch.client.asInternalUser.cat.indices.mockResolvedValueOnce({
body: [
@@ -180,6 +197,14 @@ describe('CoreUsageDataService', () => {
expect(getCoreUsageData()).resolves.toMatchInlineSnapshot(`
Object {
"config": Object {
+ "deprecatedKeys": Object {
+ "set": Array [
+ "new.path",
+ ],
+ "unset": Array [
+ "deprecated.path",
+ ],
+ },
"elasticsearch": Object {
"apiVersion": "master",
"customHeadersConfigured": false,
@@ -381,12 +406,10 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginA.enabled": "[redacted]",
- "pluginAB.enabled": "[redacted]",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginA.enabled': '[redacted]',
+ 'pluginAB.enabled': '[redacted]',
+ });
});
it('returns an object of plugin config usage', async () => {
@@ -418,23 +441,21 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "elasticsearch.password": "[redacted]",
- "elasticsearch.username": "[redacted]",
- "logging.json": false,
- "pluginA.arrayOfNumbers": "[redacted]",
- "pluginA.enabled": true,
- "pluginA.objectConfig.debug": true,
- "pluginA.objectConfig.username": "[redacted]",
- "pluginAB.enabled": false,
- "pluginB.arrayOfObjects": "[redacted]",
- "plugins.paths": "[redacted]",
- "server.basePath": "/zvt",
- "server.port": 5603,
- "server.rewriteBasePath": true,
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'elasticsearch.password': '[redacted]',
+ 'elasticsearch.username': '[redacted]',
+ 'logging.json': false,
+ 'pluginA.arrayOfNumbers': '[redacted]',
+ 'pluginA.enabled': true,
+ 'pluginA.objectConfig.debug': true,
+ 'pluginA.objectConfig.username': '[redacted]',
+ 'pluginAB.enabled': false,
+ 'pluginB.arrayOfObjects': '[redacted]',
+ 'plugins.paths': '[redacted]',
+ 'server.basePath': '/zvt',
+ 'server.port': 5603,
+ 'server.rewriteBasePath': true,
+ });
});
describe('config explicitly exposed to usage', () => {
@@ -457,12 +478,10 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginA.objectConfig.debug": "[redacted]",
- "server.basePath": "[redacted]",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginA.objectConfig.debug': '[redacted]',
+ 'server.basePath': '[redacted]',
+ });
});
it('returns config value on safe complete match', async () => {
@@ -478,11 +497,9 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "server.basePath": "/zvt",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'server.basePath': '/zvt',
+ });
});
it('returns [redacted] on unsafe parent match', async () => {
@@ -501,12 +518,10 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginA.objectConfig.debug": "[redacted]",
- "pluginA.objectConfig.username": "[redacted]",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginA.objectConfig.debug': '[redacted]',
+ 'pluginA.objectConfig.username': '[redacted]',
+ });
});
it('returns config value on safe parent match', async () => {
@@ -525,12 +540,10 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginA.objectConfig.debug": true,
- "pluginA.objectConfig.username": "some_user",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginA.objectConfig.debug': true,
+ 'pluginA.objectConfig.username': 'some_user',
+ });
});
it('returns [redacted] on explicitly marked as safe array of objects', async () => {
@@ -546,11 +559,9 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginB.arrayOfObjects": "[redacted]",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginB.arrayOfObjects': '[redacted]',
+ });
});
it('returns values on explicitly marked as safe array of numbers', async () => {
@@ -566,15 +577,9 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginA.arrayOfNumbers": Array [
- 1,
- 2,
- 3,
- ],
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginA.arrayOfNumbers': [1, 2, 3],
+ });
});
it('returns values on explicitly marked as safe array of strings', async () => {
@@ -590,15 +595,9 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "plugins.paths": Array [
- "pluginA",
- "pluginAB",
- "pluginB",
- ],
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'plugins.paths': ['pluginA', 'pluginAB', 'pluginB'],
+ });
});
});
@@ -619,12 +618,10 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginA.objectConfig.debug": "[redacted]",
- "pluginA.objectConfig.username": "[redacted]",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginA.objectConfig.debug': '[redacted]',
+ 'pluginA.objectConfig.username': '[redacted]',
+ });
});
it('returns config value on safe parent match', async () => {
@@ -640,13 +637,11 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "elasticsearch.password": "[redacted]",
- "elasticsearch.username": "[redacted]",
- "pluginA.objectConfig.username": "[redacted]",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'elasticsearch.password': '[redacted]',
+ 'elasticsearch.username': '[redacted]',
+ 'pluginA.objectConfig.username': '[redacted]',
+ });
});
it('returns [redacted] on implicit array of objects', async () => {
@@ -658,11 +653,9 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginB.arrayOfObjects": "[redacted]",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginB.arrayOfObjects': '[redacted]',
+ });
});
it('returns values on implicit array of numbers', async () => {
@@ -674,16 +667,11 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "pluginA.arrayOfNumbers": Array [
- 1,
- 2,
- 3,
- ],
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'pluginA.arrayOfNumbers': [1, 2, 3],
+ });
});
+
it('returns [redacted] on implicit array of strings', async () => {
configService.getUsedPaths.mockResolvedValue(['plugins.paths']);
@@ -693,11 +681,9 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "plugins.paths": "[redacted]",
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'plugins.paths': '[redacted]',
+ });
});
it('returns config value for numbers', async () => {
@@ -709,11 +695,9 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "server.port": 5603,
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'server.port': 5603,
+ });
});
it('returns config value for booleans', async () => {
@@ -728,12 +712,10 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "logging.json": false,
- "pluginA.objectConfig.debug": true,
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'logging.json': false,
+ 'pluginA.objectConfig.debug': true,
+ });
});
it('ignores exposed to usage configs but not used', async () => {
@@ -749,11 +731,9 @@ describe('CoreUsageDataService', () => {
elasticsearch,
});
- await expect(getConfigsUsageData()).resolves.toMatchInlineSnapshot(`
- Object {
- "logging.json": false,
- }
- `);
+ await expect(getConfigsUsageData()).resolves.toEqual({
+ 'logging.json': false,
+ });
});
});
});
@@ -779,7 +759,8 @@ describe('CoreUsageDataService', () => {
savedObjectsServiceMock.createStartContract()
);
- service.setup({ http, metrics, savedObjectsStartPromise });
+ const changedDeprecatedConfigPath$ = configServiceMock.create().getDeprecatedConfigPath$();
+ service.setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ });
// Use the stopTimer$ to delay calling stop() until the third frame
const stopTimer$ = cold('---a|');
diff --git a/src/core/server/core_usage_data/core_usage_data_service.ts b/src/core/server/core_usage_data/core_usage_data_service.ts
index 85abdca9ea5dc..dc24f889cd8dd 100644
--- a/src/core/server/core_usage_data/core_usage_data_service.ts
+++ b/src/core/server/core_usage_data/core_usage_data_service.ts
@@ -6,10 +6,10 @@
* Side Public License, v 1.
*/
-import { Subject } from 'rxjs';
+import { Subject, Observable } from 'rxjs';
import { takeUntil, first } from 'rxjs/operators';
import { get } from 'lodash';
-import { hasConfigPathIntersection } from '@kbn/config';
+import { hasConfigPathIntersection, ChangedDeprecatedPaths } from '@kbn/config';
import { CoreService } from 'src/core/types';
import { Logger, SavedObjectsServiceStart, SavedObjectTypeRegistry } from 'src/core/server';
@@ -39,6 +39,7 @@ export interface SetupDeps {
http: InternalHttpServiceSetup;
metrics: MetricsServiceSetup;
savedObjectsStartPromise: Promise;
+ changedDeprecatedConfigPath$: Observable;
}
export interface StartDeps {
@@ -89,6 +90,7 @@ export class CoreUsageDataService implements CoreService);
}
- setup({ http, metrics, savedObjectsStartPromise }: SetupDeps) {
+ setup({ http, metrics, savedObjectsStartPromise, changedDeprecatedConfigPath$ }: SetupDeps) {
metrics
.getOpsMetrics$()
.pipe(takeUntil(this.stop$))
@@ -417,6 +421,10 @@ export class CoreUsageDataService implements CoreService (this.deprecatedConfigPaths = deprecatedConfigPaths));
+
const internalRepositoryPromise = savedObjectsStartPromise.then((savedObjects) =>
savedObjects.createInternalRepository([CORE_USAGE_STATS_TYPE])
);
diff --git a/src/core/server/core_usage_data/types.ts b/src/core/server/core_usage_data/types.ts
index 1d5ef6d893f53..affd3d5c66ab7 100644
--- a/src/core/server/core_usage_data/types.ts
+++ b/src/core/server/core_usage_data/types.ts
@@ -254,6 +254,11 @@ export interface CoreConfigUsageData {
// uiSettings: {
// overridesCount: number;
// };
+
+ deprecatedKeys: {
+ set: string[];
+ unset: string[];
+ };
}
/** @internal */
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index d9ad24a4a2c0c..7f108dbeb0086 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -399,6 +399,11 @@ export interface ContextSetup {
// @internal
export interface CoreConfigUsageData {
+ // (undocumented)
+ deprecatedKeys: {
+ set: string[];
+ unset: string[];
+ };
// (undocumented)
elasticsearch: {
sniffOnStart: boolean;
diff --git a/src/core/server/server.ts b/src/core/server/server.ts
index fcfca3a5e0e2f..4d99368f9bf70 100644
--- a/src/core/server/server.ts
+++ b/src/core/server/server.ts
@@ -153,6 +153,7 @@ export class Server {
http: httpSetup,
metrics: metricsSetup,
savedObjectsStartPromise: this.savedObjectsStartPromise,
+ changedDeprecatedConfigPath$: this.configService.getDeprecatedConfigPath$(),
});
const savedObjectsSetup = await this.savedObjects.setup({
@@ -265,6 +266,7 @@ export class Server {
await this.http.start();
startTransaction?.end();
+
return this.coreStart;
}
diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts
index 3f39b5563ebc0..bf51e21bb9bf4 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts
@@ -308,6 +308,23 @@ export function getCoreUsageCollector(
},
},
},
+
+ deprecatedKeys: {
+ set: {
+ type: 'array',
+ items: {
+ type: 'keyword',
+ _meta: { description: 'Config path added during config deprecation.' },
+ },
+ },
+ unset: {
+ type: 'array',
+ items: {
+ type: 'keyword',
+ _meta: { description: 'Config path removed during config deprecation.' },
+ },
+ },
+ },
},
environment: {
memory: {
diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json
index 230d2052f089e..693957057f108 100644
--- a/src/plugins/telemetry/schema/oss_plugins.json
+++ b/src/plugins/telemetry/schema/oss_plugins.json
@@ -6950,6 +6950,28 @@
}
}
}
+ },
+ "deprecatedKeys": {
+ "properties": {
+ "set": {
+ "type": "array",
+ "items": {
+ "type": "keyword",
+ "_meta": {
+ "description": "Config path added during config deprecation."
+ }
+ }
+ },
+ "unset": {
+ "type": "array",
+ "items": {
+ "type": "keyword",
+ "_meta": {
+ "description": "Config path removed during config deprecation."
+ }
+ }
+ }
+ }
}
}
},
diff --git a/x-pack/plugins/reporting/server/config/index.test.ts b/x-pack/plugins/reporting/server/config/index.test.ts
index cba64500575aa..8f13fe8b53810 100644
--- a/x-pack/plugins/reporting/server/config/index.test.ts
+++ b/x-pack/plugins/reporting/server/config/index.test.ts
@@ -15,7 +15,7 @@ const applyReportingDeprecations = (settings: Record = {}) => {
const deprecationMessages: string[] = [];
const _config: any = {};
_config[CONFIG_PATH] = settings;
- const migrated = applyDeprecations(
+ const { config: migrated } = applyDeprecations(
_config,
deprecations.map((deprecation) => ({
deprecation,
diff --git a/x-pack/plugins/security/server/config_deprecations.test.ts b/x-pack/plugins/security/server/config_deprecations.test.ts
index 80173dd42a49e..a233d760359e5 100644
--- a/x-pack/plugins/security/server/config_deprecations.test.ts
+++ b/x-pack/plugins/security/server/config_deprecations.test.ts
@@ -14,7 +14,7 @@ import { securityConfigDeprecationProvider } from './config_deprecations';
const applyConfigDeprecations = (settings: Record = {}) => {
const deprecations = securityConfigDeprecationProvider(configDeprecationFactory);
const deprecationMessages: string[] = [];
- const migrated = applyDeprecations(
+ const { config: migrated } = applyDeprecations(
settings,
deprecations.map((deprecation) => ({
deprecation,
diff --git a/x-pack/plugins/spaces/server/config.test.ts b/x-pack/plugins/spaces/server/config.test.ts
index 1e60c1b635320..a6f8c37b293ef 100644
--- a/x-pack/plugins/spaces/server/config.test.ts
+++ b/x-pack/plugins/spaces/server/config.test.ts
@@ -13,7 +13,7 @@ import { spacesConfigDeprecationProvider } from './config';
const applyConfigDeprecations = (settings: Record = {}) => {
const deprecations = spacesConfigDeprecationProvider(configDeprecationFactory);
const deprecationMessages: string[] = [];
- const migrated = applyDeprecations(
+ const { config: migrated } = applyDeprecations(
settings,
deprecations.map((deprecation) => ({
deprecation,
diff --git a/x-pack/plugins/task_manager/server/index.test.ts b/x-pack/plugins/task_manager/server/index.test.ts
index 3fce5f7bdfdf4..8eb98c39a2ccd 100644
--- a/x-pack/plugins/task_manager/server/index.test.ts
+++ b/x-pack/plugins/task_manager/server/index.test.ts
@@ -16,7 +16,7 @@ const applyTaskManagerDeprecations = (settings: Record = {}) =>
const _config = {
[CONFIG_PATH]: settings,
};
- const migrated = applyDeprecations(
+ const { config: migrated } = applyDeprecations(
_config,
deprecations.map((deprecation) => ({
deprecation,
From 28d2343fce11892a21f332b6fd0298f1e7bb52e2 Mon Sep 17 00:00:00 2001
From: Spencer
Date: Wed, 26 May 2021 02:00:24 -0700
Subject: [PATCH 12/17] [ftr] migrate "find" service to FtrService class
(#100509)
Co-authored-by: spalger
---
test/functional/services/common/find.ts | 863 ++++++++++++------------
1 file changed, 413 insertions(+), 450 deletions(-)
diff --git a/test/functional/services/common/find.ts b/test/functional/services/common/find.ts
index 0cd4c14683f6e..8d037e2df2109 100644
--- a/test/functional/services/common/find.ts
+++ b/test/functional/services/common/find.ts
@@ -7,511 +7,474 @@
*/
import { WebDriver, WebElement, By, until } from 'selenium-webdriver';
-import { FtrProviderContext } from '../../ftr_provider_context';
-import { WebElementWrapper } from '../lib/web_element_wrapper';
-export async function FindProvider({ getService }: FtrProviderContext) {
- const log = getService('log');
- const config = getService('config');
- const { driver, browserType } = await getService('__webdriver__').init();
- const retry = getService('retry');
+import { Browsers } from '../remote/browsers';
+import { FtrService, FtrProviderContext } from '../../ftr_provider_context';
+import { WebElementWrapper } from '../lib/web_element_wrapper';
- const WAIT_FOR_EXISTS_TIME = config.get('timeouts.waitForExists');
- const POLLING_TIME = 500;
- const defaultFindTimeout = config.get('timeouts.find');
- const fixedHeaderHeight = config.get('layout.fixedHeaderHeight');
+export class FindService extends FtrService {
+ private readonly log = this.ctx.getService('log');
+ private readonly config = this.ctx.getService('config');
+ private readonly retry = this.ctx.getService('retry');
- const wrap = (webElement: WebElement | WebElementWrapper, locator: By | null = null) =>
- WebElementWrapper.create(
- webElement,
- locator,
- driver,
- defaultFindTimeout,
- fixedHeaderHeight,
- log,
- browserType
- );
+ private readonly WAIT_FOR_EXISTS_TIME = this.config.get('timeouts.waitForExists');
+ private readonly POLLING_TIME = 500;
+ private readonly defaultFindTimeout = this.config.get('timeouts.find');
+ private readonly fixedHeaderHeight = this.config.get('layout.fixedHeaderHeight');
- const wrapAll = (webElements: Array) =>
- webElements.map((e) => wrap(e));
+ public currentWait = this.defaultFindTimeout;
- const findAndWrap = async (locator: By, timeout: number): Promise => {
- const webElement = await driver.wait(until.elementLocated(locator), timeout);
- return wrap(webElement, locator);
- };
+ constructor(
+ ctx: FtrProviderContext,
+ private readonly browserType: Browsers,
+ private readonly driver: WebDriver
+ ) {
+ super(ctx);
+ }
- class Find {
- public currentWait = defaultFindTimeout;
+ public async byName(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.byName('${selector}') with timeout=${timeout}`);
+ return await this.findAndWrap(By.name(selector), timeout);
+ }
- public async byName(
- selector: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.byName('${selector}') with timeout=${timeout}`);
- return await findAndWrap(By.name(selector), timeout);
- }
+ public async byCssSelector(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.findByCssSelector('${selector}') with timeout=${timeout}`);
+ return this.findAndWrap(By.css(selector), timeout);
+ }
- public async byCssSelector(
- selector: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.findByCssSelector('${selector}') with timeout=${timeout}`);
- return findAndWrap(By.css(selector), timeout);
- }
+ public async byXPath(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.byXPath('${selector}') with timeout=${timeout}`);
+ return this.findAndWrap(By.xpath(selector), timeout);
+ }
- public async byXPath(
- selector: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.byXPath('${selector}') with timeout=${timeout}`);
- return findAndWrap(By.xpath(selector), timeout);
- }
+ public async byClassName(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.findByClassName('${selector}') with timeout=${timeout}`);
+ return this.findAndWrap(By.className(selector), timeout);
+ }
- public async byClassName(
- selector: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.findByClassName('${selector}') with timeout=${timeout}`);
- return findAndWrap(By.className(selector), timeout);
- }
+ public async activeElement(): Promise {
+ return this.wrap(await this.driver.switchTo().activeElement());
+ }
- public async activeElement(): Promise {
- return wrap(await driver.switchTo().activeElement());
- }
+ public async setValue(selector: string, text: string, topOffset?: number): Promise {
+ this.log.debug(`Find.setValue('${selector}', '${text}')`);
+ return await this.retry.try(async () => {
+ const element = await this.byCssSelector(selector);
+ await element.click(topOffset);
+
+ // in case the input element is actually a child of the testSubject, we
+ // call clearValue() and type() on the element that is focused after
+ // clicking on the testSubject
+ const input = await this.activeElement();
+ if (input) {
+ await input.clearValue();
+ await input.type(text);
+ } else {
+ await element.clearValue();
+ await element.type(text);
+ }
+ });
+ }
- public async setValue(selector: string, text: string, topOffset?: number): Promise {
- log.debug(`Find.setValue('${selector}', '${text}')`);
- return await retry.try(async () => {
- const element = await this.byCssSelector(selector);
- await element.click(topOffset);
+ public async selectValue(selector: string, value: string): Promise {
+ this.log.debug(`Find.selectValue('${selector}', option[value="${value}"]')`);
+ const combobox = await this.byCssSelector(selector);
+ const $ = await combobox.parseDomContent();
+ const text = $(`option[value="${value}"]`).text();
+ await combobox.type(text);
+ }
- // in case the input element is actually a child of the testSubject, we
- // call clearValue() and type() on the element that is focused after
- // clicking on the testSubject
- const input = await this.activeElement();
- if (input) {
- await input.clearValue();
- await input.type(text);
- } else {
- await element.clearValue();
- await element.type(text);
+ public async filterElementIsDisplayed(elements: WebElementWrapper[]) {
+ if (elements.length === 0) {
+ return [];
+ } else {
+ const displayed = [];
+ // tslint:disable-next-line:prefer-for-of
+ for (let i = 0; i < elements.length; i++) {
+ const isDisplayed = await elements[i].isDisplayed();
+ if (isDisplayed) {
+ displayed.push(elements[i]);
}
- });
+ }
+ return displayed;
}
+ }
- public async selectValue(selector: string, value: string): Promise {
- log.debug(`Find.selectValue('${selector}', option[value="${value}"]')`);
- const combobox = await this.byCssSelector(selector);
- const $ = await combobox.parseDomContent();
- const text = $(`option[value="${value}"]`).text();
- await combobox.type(text);
- }
+ public async allByCssSelector(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.allByCssSelector('${selector}') with timeout=${timeout}`);
+ await this._withTimeout(timeout);
+ const elements = await this.driver.findElements(By.css(selector));
+ await this._withTimeout(this.defaultFindTimeout);
+ return this.wrapAll(elements);
+ }
- public async filterElementIsDisplayed(elements: WebElementWrapper[]) {
- if (elements.length === 0) {
- return [];
- } else {
- const displayed = [];
- // tslint:disable-next-line:prefer-for-of
- for (let i = 0; i < elements.length; i++) {
- const isDisplayed = await elements[i].isDisplayed();
- if (isDisplayed) {
- displayed.push(elements[i]);
- }
- }
- return displayed;
- }
- }
+ public async descendantExistsByCssSelector(
+ selector: string,
+ parentElement: WebElementWrapper,
+ timeout: number = this.WAIT_FOR_EXISTS_TIME
+ ): Promise {
+ this.log.debug(`Find.descendantExistsByCssSelector('${selector}') with timeout=${timeout}`);
+ const els = await parentElement._webElement.findElements(By.css(selector));
+ return await this.exists(async () => this.wrapAll(els), timeout);
+ }
- public async allByCustom(
- findAllFunction: (drive: WebDriver) => WebElementWrapper[],
- timeout = defaultFindTimeout
- ): Promise {
- await this._withTimeout(timeout);
- return await retry.try(async () => {
- let elements = await findAllFunction(driver);
- if (!elements) {
- elements = [];
- }
- // Force isStale checks for all the retrieved elements.
- await Promise.all(elements.map(async (element) => await element.isEnabled()));
- await this._withTimeout(defaultFindTimeout);
- return elements;
- });
+ public async descendantDisplayedByCssSelector(
+ selector: string,
+ parentElement: WebElementWrapper
+ ): Promise {
+ this.log.debug(`Find.descendantDisplayedByCssSelector('${selector}')`);
+ const element = await parentElement._webElement.findElement(By.css(selector));
+ const descendant = this.wrap(element, By.css(selector));
+ const isDisplayed = await descendant.isDisplayed();
+ if (isDisplayed) {
+ return descendant;
+ } else {
+ throw new Error(`Element "${selector}" is not displayed`);
}
+ }
- public async allByLinkText(
- selector: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.allByLinkText('${selector}') with timeout=${timeout}`);
- await this._withTimeout(timeout);
- const elements = await driver.findElements(By.linkText(selector));
- await this._withTimeout(defaultFindTimeout);
- return wrapAll(elements);
- }
+ public async allDescendantDisplayedByCssSelector(
+ selector: string,
+ parentElement: WebElementWrapper
+ ): Promise {
+ this.log.debug(`Find.allDescendantDisplayedByCssSelector('${selector}')`);
+ const allElements = await this.wrapAll(
+ await parentElement._webElement.findElements(By.css(selector))
+ );
+ return await this.filterElementIsDisplayed(allElements);
+ }
- public async allByButtonText(
- buttonText: string,
- element: WebDriver | WebElement | WebElementWrapper = driver,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.byButtonText('${buttonText}') with timeout=${timeout}`);
- return await retry.tryForTime(timeout, async () => {
- // tslint:disable-next-line:variable-name
- const _element = element instanceof WebElementWrapper ? element._webElement : element;
- await this._withTimeout(0);
- const allButtons = wrapAll(await _element.findElements(By.tagName('button')));
- await this._withTimeout(defaultFindTimeout);
- const buttonTexts = await Promise.all(
- allButtons.map(async (el) => {
- return el.getVisibleText();
- })
- );
- return buttonTexts.filter((text) => text.trim() === buttonText.trim());
- });
- }
+ public async allDescendantDisplayedByTagName(
+ tagName: string,
+ parentElement: WebElementWrapper
+ ): Promise {
+ this.log.debug(`Find.allDescendantDisplayedByTagName('${tagName}')`);
+ const allElements = await this.wrapAll(
+ await parentElement._webElement.findElements(By.tagName(tagName))
+ );
+ return await this.filterElementIsDisplayed(allElements);
+ }
- public async allByCssSelector(
- selector: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.allByCssSelector('${selector}') with timeout=${timeout}`);
- await this._withTimeout(timeout);
- const elements = await driver.findElements(By.css(selector));
- await this._withTimeout(defaultFindTimeout);
- return wrapAll(elements);
- }
+ public async displayedByLinkText(
+ linkText: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.displayedByLinkText('${linkText}') with timeout=${timeout}`);
+ const element = await this.byLinkText(linkText, timeout);
+ this.log.debug(`Wait for element become visible: ${linkText} with timeout=${timeout}`);
+ await this.driver.wait(until.elementIsVisible(element._webElement), timeout);
+ return this.wrap(element, By.linkText(linkText));
+ }
- public async descendantExistsByCssSelector(
- selector: string,
- parentElement: WebElementWrapper,
- timeout: number = WAIT_FOR_EXISTS_TIME
- ): Promise {
- log.debug(`Find.descendantExistsByCssSelector('${selector}') with timeout=${timeout}`);
- const els = await parentElement._webElement.findElements(By.css(selector));
- return await this.exists(async () => wrapAll(els), timeout);
- }
+ public async displayedByCssSelector(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.displayedByCssSelector(${selector})`);
+ const element = await this.byCssSelector(selector, timeout);
+ this.log.debug(`Wait for element become visible: ${selector} with timeout=${timeout}`);
+ await this.driver.wait(until.elementIsVisible(element._webElement), timeout);
+ return this.wrap(element, By.css(selector));
+ }
+
+ public async byLinkText(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.byLinkText('${selector}') with timeout=${timeout}`);
+ return this.findAndWrap(By.linkText(selector), timeout);
+ }
- public async descendantDisplayedByCssSelector(
- selector: string,
- parentElement: WebElementWrapper
- ): Promise {
- log.debug(`Find.descendantDisplayedByCssSelector('${selector}')`);
- const element = await parentElement._webElement.findElement(By.css(selector));
- const descendant = wrap(element, By.css(selector));
- const isDisplayed = await descendant.isDisplayed();
- if (isDisplayed) {
- return descendant;
+ public async byPartialLinkText(
+ partialLinkText: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.byPartialLinkText('${partialLinkText}') with timeout=${timeout}`);
+ return this.findAndWrap(By.partialLinkText(partialLinkText), timeout);
+ }
+
+ public async exists(
+ findFunction: (
+ el: WebDriver
+ ) =>
+ | WebElementWrapper
+ | WebElementWrapper[]
+ | Promise
+ | Promise,
+ timeout: number = this.WAIT_FOR_EXISTS_TIME
+ ): Promise {
+ await this._withTimeout(timeout);
+ try {
+ const found = await findFunction(this.driver);
+ await this._withTimeout(this.defaultFindTimeout);
+ if (Array.isArray(found)) {
+ return found.length > 0;
} else {
- throw new Error(`Element "${selector}" is not displayed`);
+ return found instanceof WebElementWrapper;
}
+ } catch (err) {
+ await this._withTimeout(this.defaultFindTimeout);
+ return false;
}
+ }
- public async allDescendantDisplayedByCssSelector(
- selector: string,
- parentElement: WebElementWrapper
- ): Promise {
- log.debug(`Find.allDescendantDisplayedByCssSelector('${selector}')`);
- const allElements = await wrapAll(
- await parentElement._webElement.findElements(By.css(selector))
- );
- return await this.filterElementIsDisplayed(allElements);
- }
-
- public async allDescendantDisplayedByTagName(
- tagName: string,
- parentElement: WebElementWrapper
- ): Promise {
- log.debug(`Find.allDescendantDisplayedByTagName('${tagName}')`);
- const allElements = await wrapAll(
- await parentElement._webElement.findElements(By.tagName(tagName))
- );
- return await this.filterElementIsDisplayed(allElements);
- }
+ public async existsByLinkText(
+ linkText: string,
+ timeout: number = this.WAIT_FOR_EXISTS_TIME
+ ): Promise {
+ this.log.debug(`Find.existsByLinkText('${linkText}') with timeout=${timeout}`);
+ return await this.exists(
+ async (drive) => this.wrapAll(await drive.findElements(By.linkText(linkText))),
+ timeout
+ );
+ }
- public async displayedByLinkText(
- linkText: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.displayedByLinkText('${linkText}') with timeout=${timeout}`);
- const element = await this.byLinkText(linkText, timeout);
- log.debug(`Wait for element become visible: ${linkText} with timeout=${timeout}`);
- await driver.wait(until.elementIsVisible(element._webElement), timeout);
- return wrap(element, By.linkText(linkText));
+ public async existsByDisplayedByCssSelector(
+ selector: string,
+ timeout: number = this.WAIT_FOR_EXISTS_TIME
+ ): Promise {
+ this.log.debug(`Find.existsByDisplayedByCssSelector('${selector}') with timeout=${timeout}`);
+ try {
+ await this.retry.tryForTime(timeout, async () => {
+ // make sure that the find timeout is not longer than the retry timeout
+ await this._withTimeout(Math.min(timeout, this.WAIT_FOR_EXISTS_TIME));
+ const elements = await this.driver.findElements(By.css(selector));
+ await this._withTimeout(this.defaultFindTimeout);
+ const displayed = await this.filterElementIsDisplayed(this.wrapAll(elements));
+ if (displayed.length === 0) {
+ throw new Error(`${selector} is not displayed`);
+ }
+ });
+ } catch (err) {
+ await this._withTimeout(this.defaultFindTimeout);
+ return false;
}
+ return true;
+ }
- public async displayedByCssSelector(
- selector: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.displayedByCssSelector(${selector})`);
- const element = await this.byCssSelector(selector, timeout);
- log.debug(`Wait for element become visible: ${selector} with timeout=${timeout}`);
- await driver.wait(until.elementIsVisible(element._webElement), timeout);
- return wrap(element, By.css(selector));
- }
+ public async existsByCssSelector(
+ selector: string,
+ timeout: number = this.WAIT_FOR_EXISTS_TIME
+ ): Promise {
+ this.log.debug(`Find.existsByCssSelector('${selector}') with timeout=${timeout}`);
+ return await this.exists(async (drive) => {
+ return this.wrapAll(await drive.findElements(By.css(selector)));
+ }, timeout);
+ }
- public async byLinkText(
- selector: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.byLinkText('${selector}') with timeout=${timeout}`);
- return findAndWrap(By.linkText(selector), timeout);
- }
+ public async clickByCssSelectorWhenNotDisabled(
+ selector: string,
+ { timeout } = { timeout: this.defaultFindTimeout }
+ ): Promise {
+ this.log.debug(`Find.clickByCssSelectorWhenNotDisabled('${selector}') with timeout=${timeout}`);
+
+ // Don't wrap this code in a retry, or stale element checks may get caught here and the element
+ // will never be re-grabbed. Let errors bubble, but continue checking for disabled property until
+ // it's gone.
+ const element = await this.byCssSelector(selector, timeout);
+ await element.moveMouseTo();
+ await this.driver.wait(until.elementIsEnabled(element._webElement), timeout);
+ await element.click();
+ }
- public async byPartialLinkText(
- partialLinkText: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.byPartialLinkText('${partialLinkText}') with timeout=${timeout}`);
- return findAndWrap(By.partialLinkText(partialLinkText), timeout);
- }
+ public async clickByPartialLinkText(
+ linkText: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.clickByPartialLinkText('${linkText}') with timeout=${timeout}`);
+ await this.retry.try(async () => {
+ const element = await this.byPartialLinkText(linkText, timeout);
+ await element.moveMouseTo();
+ await element.click();
+ });
+ }
- public async exists(
- findFunction: (
- el: WebDriver
- ) =>
- | WebElementWrapper
- | WebElementWrapper[]
- | Promise
- | Promise,
- timeout: number = WAIT_FOR_EXISTS_TIME
- ): Promise {
- await this._withTimeout(timeout);
- try {
- const found = await findFunction(driver);
- await this._withTimeout(defaultFindTimeout);
- if (Array.isArray(found)) {
- return found.length > 0;
- } else {
- return found instanceof WebElementWrapper;
- }
- } catch (err) {
- await this._withTimeout(defaultFindTimeout);
- return false;
- }
- }
+ public async clickByLinkText(
+ linkText: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.clickByLinkText('${linkText}') with timeout=${timeout}`);
+ await this.retry.try(async () => {
+ const element = await this.byLinkText(linkText, timeout);
+ await element.moveMouseTo();
+ await element.click();
+ });
+ }
- public async existsByLinkText(
- linkText: string,
- timeout: number = WAIT_FOR_EXISTS_TIME
- ): Promise {
- log.debug(`Find.existsByLinkText('${linkText}') with timeout=${timeout}`);
- return await this.exists(
- async (drive) => wrapAll(await drive.findElements(By.linkText(linkText))),
- timeout
+ public async byButtonText(
+ buttonText: string,
+ element: WebDriver | WebElement | WebElementWrapper = this.driver,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.byButtonText('${buttonText}') with timeout=${timeout}`);
+ return await this.retry.tryForTime(timeout, async () => {
+ // tslint:disable-next-line:variable-name
+ const _element = element instanceof WebElementWrapper ? element._webElement : element;
+ const allButtons = this.wrapAll(await _element.findElements(By.tagName('button')));
+ const buttonTexts = await Promise.all(
+ allButtons.map(async (el) => {
+ return el.getVisibleText();
+ })
);
- }
-
- public async existsByDisplayedByCssSelector(
- selector: string,
- timeout: number = WAIT_FOR_EXISTS_TIME
- ): Promise {
- log.debug(`Find.existsByDisplayedByCssSelector('${selector}') with timeout=${timeout}`);
- try {
- await retry.tryForTime(timeout, async () => {
- // make sure that the find timeout is not longer than the retry timeout
- await this._withTimeout(Math.min(timeout, WAIT_FOR_EXISTS_TIME));
- const elements = await driver.findElements(By.css(selector));
- await this._withTimeout(defaultFindTimeout);
- const displayed = await this.filterElementIsDisplayed(wrapAll(elements));
- if (displayed.length === 0) {
- throw new Error(`${selector} is not displayed`);
- }
- });
- } catch (err) {
- await this._withTimeout(defaultFindTimeout);
- return false;
+ const index = buttonTexts.findIndex((text) => text.trim() === buttonText.trim());
+ if (index === -1) {
+ throw new Error('Button not found');
}
- return true;
- }
-
- public async existsByCssSelector(
- selector: string,
- timeout: number = WAIT_FOR_EXISTS_TIME
- ): Promise {
- log.debug(`Find.existsByCssSelector('${selector}') with timeout=${timeout}`);
- return await this.exists(async (drive) => {
- return wrapAll(await drive.findElements(By.css(selector)));
- }, timeout);
- }
+ return allButtons[index];
+ });
+ }
- public async clickByCssSelectorWhenNotDisabled(
- selector: string,
- { timeout } = { timeout: defaultFindTimeout }
- ): Promise {
- log.debug(`Find.clickByCssSelectorWhenNotDisabled('${selector}') with timeout=${timeout}`);
+ public async clickByButtonText(
+ buttonText: string,
+ element: WebDriver | WebElement | WebElementWrapper = this.driver,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.clickByButtonText('${buttonText}') with timeout=${timeout}`);
+ await this.retry.try(async () => {
+ const button = await this.byButtonText(buttonText, element, timeout);
+ await button.click();
+ });
+ }
- // Don't wrap this code in a retry, or stale element checks may get caught here and the element
- // will never be re-grabbed. Let errors bubble, but continue checking for disabled property until
- // it's gone.
+ public async clickByCssSelector(
+ selector: string,
+ timeout: number = this.defaultFindTimeout,
+ topOffset?: number
+ ): Promise {
+ this.log.debug(`Find.clickByCssSelector('${selector}') with timeout=${timeout}`);
+ await this.retry.try(async () => {
const element = await this.byCssSelector(selector, timeout);
- await element.moveMouseTo();
- await driver.wait(until.elementIsEnabled(element._webElement), timeout);
- await element.click();
- }
+ if (element) {
+ // await element.moveMouseTo();
+ await element.click(topOffset);
+ } else {
+ throw new Error(`Element with css='${selector}' is not found`);
+ }
+ });
+ }
- public async clickByPartialLinkText(
- linkText: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.clickByPartialLinkText('${linkText}') with timeout=${timeout}`);
- await retry.try(async () => {
- const element = await this.byPartialLinkText(linkText, timeout);
+ public async clickByDisplayedLinkText(
+ linkText: string,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.clickByDisplayedLinkText('${linkText}') with timeout=${timeout}`);
+ await this.retry.try(async () => {
+ const element = await this.displayedByLinkText(linkText, timeout);
+ if (element) {
await element.moveMouseTo();
await element.click();
- });
- }
+ } else {
+ throw new Error(`Element with linkText='${linkText}' is not found`);
+ }
+ });
+ }
- public async clickByLinkText(
- linkText: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.clickByLinkText('${linkText}') with timeout=${timeout}`);
- await retry.try(async () => {
- const element = await this.byLinkText(linkText, timeout);
+ public async clickDisplayedByCssSelector(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ) {
+ this.log.debug(`Find.clickDisplayedByCssSelector('${selector}') with timeout=${timeout}`);
+ await this.retry.try(async () => {
+ const element = await this.displayedByCssSelector(selector, timeout);
+ if (element) {
await element.moveMouseTo();
await element.click();
- });
- }
-
- public async byButtonText(
- buttonText: string,
- element: WebDriver | WebElement | WebElementWrapper = driver,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.byButtonText('${buttonText}') with timeout=${timeout}`);
- return await retry.tryForTime(timeout, async () => {
- // tslint:disable-next-line:variable-name
- const _element = element instanceof WebElementWrapper ? element._webElement : element;
- const allButtons = wrapAll(await _element.findElements(By.tagName('button')));
- const buttonTexts = await Promise.all(
- allButtons.map(async (el) => {
- return el.getVisibleText();
- })
- );
- const index = buttonTexts.findIndex((text) => text.trim() === buttonText.trim());
- if (index === -1) {
- throw new Error('Button not found');
- }
- return allButtons[index];
- });
- }
-
- public async clickByButtonText(
- buttonText: string,
- element: WebDriver | WebElement | WebElementWrapper = driver,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.clickByButtonText('${buttonText}') with timeout=${timeout}`);
- await retry.try(async () => {
- const button = await this.byButtonText(buttonText, element, timeout);
- await button.click();
- });
- }
+ } else {
+ throw new Error(`Element with css='${selector}' is not found`);
+ }
+ });
+ }
- public async clickByCssSelector(
- selector: string,
- timeout: number = defaultFindTimeout,
- topOffset?: number
- ): Promise {
- log.debug(`Find.clickByCssSelector('${selector}') with timeout=${timeout}`);
- await retry.try(async () => {
- const element = await this.byCssSelector(selector, timeout);
- if (element) {
- // await element.moveMouseTo();
- await element.click(topOffset);
- } else {
- throw new Error(`Element with css='${selector}' is not found`);
- }
- });
- }
+ public async waitForDeletedByCssSelector(
+ selector: string,
+ timeout: number = this.defaultFindTimeout
+ ) {
+ this.log.debug(`Find.waitForDeletedByCssSelector('${selector}') with timeout=${timeout}`);
+ await this._withTimeout(this.POLLING_TIME);
+ await this.driver.wait(
+ async () => {
+ const found = await this.driver.findElements(By.css(selector));
+ return found.length === 0;
+ },
+ timeout,
+ `The element ${selector} was still present when it should have disappeared.`
+ );
+ await this._withTimeout(this.defaultFindTimeout);
+ }
- public async clickByDisplayedLinkText(
- linkText: string,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.clickByDisplayedLinkText('${linkText}') with timeout=${timeout}`);
- await retry.try(async () => {
- const element = await this.displayedByLinkText(linkText, timeout);
- if (element) {
- await element.moveMouseTo();
- await element.click();
- } else {
- throw new Error(`Element with linkText='${linkText}' is not found`);
- }
- });
- }
+ public async waitForAttributeToChange(
+ selector: string,
+ attribute: string,
+ value: string
+ ): Promise {
+ this.log.debug(`Find.waitForAttributeToChange('${selector}', '${attribute}', '${value}')`);
+ await this.retry.waitFor(`${attribute} to equal "${value}"`, async () => {
+ const el = await this.byCssSelector(selector);
+ return value === (await el.getAttribute(attribute));
+ });
+ }
- public async clickDisplayedByCssSelector(
- selector: string,
- timeout: number = defaultFindTimeout
- ) {
- log.debug(`Find.clickDisplayedByCssSelector('${selector}') with timeout=${timeout}`);
- await retry.try(async () => {
- const element = await this.displayedByCssSelector(selector, timeout);
- if (element) {
- await element.moveMouseTo();
- await element.click();
- } else {
- throw new Error(`Element with css='${selector}' is not found`);
- }
- });
- }
+ public async waitForElementStale(
+ element: WebElementWrapper,
+ timeout: number = this.defaultFindTimeout
+ ): Promise {
+ this.log.debug(`Find.waitForElementStale with timeout=${timeout}`);
+ await this.driver.wait(until.stalenessOf(element._webElement), timeout);
+ }
- public async waitForDeletedByCssSelector(
- selector: string,
- timeout: number = defaultFindTimeout
- ) {
- log.debug(`Find.waitForDeletedByCssSelector('${selector}') with timeout=${timeout}`);
- await this._withTimeout(POLLING_TIME);
- await driver.wait(
- async () => {
- const found = await driver.findElements(By.css(selector));
- return found.length === 0;
- },
- timeout,
- `The element ${selector} was still present when it should have disappeared.`
- );
- await this._withTimeout(defaultFindTimeout);
- }
+ public async waitForElementHidden(
+ element: WebElementWrapper,
+ timeout: number = this.defaultFindTimeout
+ ) {
+ this.log.debug(`Find.waitForElementHidden with timeout=${timeout}`);
+ await this.driver.wait(until.elementIsNotVisible(element._webElement), timeout);
+ }
- public async waitForAttributeToChange(
- selector: string,
- attribute: string,
- value: string
- ): Promise {
- log.debug(`Find.waitForAttributeToChange('${selector}', '${attribute}', '${value}')`);
- await retry.waitFor(`${attribute} to equal "${value}"`, async () => {
- const el = await this.byCssSelector(selector);
- return value === (await el.getAttribute(attribute));
- });
+ private async _withTimeout(timeout: number) {
+ if (timeout !== this.currentWait) {
+ this.currentWait = timeout;
+ await this.driver.manage().setTimeouts({ implicit: timeout });
}
+ }
- public async waitForElementStale(
- element: WebElementWrapper,
- timeout: number = defaultFindTimeout
- ): Promise {
- log.debug(`Find.waitForElementStale with timeout=${timeout}`);
- await driver.wait(until.stalenessOf(element._webElement), timeout);
- }
+ private wrap(webElement: WebElement | WebElementWrapper, locator: By | null = null) {
+ return WebElementWrapper.create(
+ webElement,
+ locator,
+ this.driver,
+ this.defaultFindTimeout,
+ this.fixedHeaderHeight,
+ this.log,
+ this.browserType
+ );
+ }
- public async waitForElementHidden(
- element: WebElementWrapper,
- timeout: number = defaultFindTimeout
- ) {
- log.debug(`Find.waitForElementHidden with timeout=${timeout}`);
- await driver.wait(until.elementIsNotVisible(element._webElement), timeout);
- }
+ private wrapAll(webElements: Array) {
+ return webElements.map((e) => this.wrap(e));
+ }
- private async _withTimeout(timeout: number) {
- if (timeout !== this.currentWait) {
- this.currentWait = timeout;
- await driver.manage().setTimeouts({ implicit: timeout });
- }
- }
+ private async findAndWrap(locator: By, timeout: number): Promise {
+ const webElement = await this.driver.wait(until.elementLocated(locator), timeout);
+ return this.wrap(webElement, locator);
}
+}
- return new Find();
+export async function FindProvider(ctx: FtrProviderContext) {
+ const { browserType, driver } = await ctx.getService('__webdriver__').init();
+ return new FindService(ctx, browserType, driver);
}
From 1f02c48d3cf57dd921cfe64186a695f8375e3954 Mon Sep 17 00:00:00 2001
From: Spencer
Date: Wed, 26 May 2021 02:02:42 -0700
Subject: [PATCH 13/17] [ftr] migrate "browser" to FtrService class (#100507)
Co-authored-by: spalger
---
test/functional/services/common/browser.ts | 1109 ++++++++++----------
1 file changed, 554 insertions(+), 555 deletions(-)
diff --git a/test/functional/services/common/browser.ts b/test/functional/services/common/browser.ts
index 4dfd30c3b3b68..d38203d5d07d3 100644
--- a/test/functional/services/common/browser.ts
+++ b/test/functional/services/common/browser.ts
@@ -8,586 +8,585 @@
import { delay } from 'bluebird';
import { cloneDeepWith } from 'lodash';
-import { Key, Origin } from 'selenium-webdriver';
+import { Key, Origin, WebDriver } from 'selenium-webdriver';
// @ts-ignore internal modules are not typed
import { LegacyActionSequence } from 'selenium-webdriver/lib/actions';
-import { ProvidedType } from '@kbn/test';
import { modifyUrl } from '@kbn/std';
import Jimp from 'jimp';
import { WebElementWrapper } from '../lib/web_element_wrapper';
-import { FtrProviderContext } from '../../ftr_provider_context';
+import { FtrProviderContext, FtrService } from '../../ftr_provider_context';
import { Browsers } from '../remote/browsers';
-export type Browser = ProvidedType;
-export async function BrowserProvider({ getService }: FtrProviderContext) {
- const log = getService('log');
- const { driver, browserType } = await getService('__webdriver__').init();
-
- return new (class BrowserService {
- /**
- * Keyboard events
- */
- public readonly keys = Key;
-
- /**
- * Browser name
- */
- public readonly browserType: string = browserType;
-
- public readonly isChromium: boolean = [Browsers.Chrome, Browsers.ChromiumEdge].includes(
- browserType
+export type Browser = BrowserService;
+
+class BrowserService extends FtrService {
+ /**
+ * Keyboard events
+ */
+ public readonly keys = Key;
+ public readonly isFirefox: boolean = this.browserType === Browsers.Firefox;
+ public readonly isChromium: boolean =
+ this.browserType === Browsers.Chrome || this.browserType === Browsers.ChromiumEdge;
+
+ private readonly log = this.ctx.getService('log');
+
+ constructor(
+ ctx: FtrProviderContext,
+ public readonly browserType: string,
+ private readonly driver: WebDriver
+ ) {
+ super(ctx);
+ }
+
+ /**
+ * Returns instance of Actions API based on driver w3c flag
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#actions
+ */
+ public getActions() {
+ return this.driver.actions();
+ }
+
+ /**
+ * Get handle for an alert, confirm, or prompt dialog. (if any).
+ * @return {Promise}
+ */
+ public async getAlert() {
+ try {
+ return await this.driver.switchTo().alert();
+ } catch (e) {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the a rect describing the current top-level window's size and position.
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Window.html
+ *
+ * @return {Promise<{height: number, width: number, x: number, y: number}>}
+ */
+ public async getWindowSize(): Promise<{ height: number; width: number; x: number; y: number }> {
+ return await this.driver.manage().window().getRect();
+ }
+
+ /**
+ * Sets the dimensions of a window.
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Window.html
+ *
+ * @param {number} width
+ * @param {number} height
+ * @return {Promise}
+ */
+ public async setWindowSize(width: number, height: number) {
+ await this.driver.manage().window().setRect({ width, height });
+ }
+
+ /**
+ * Gets a screenshot of the focused window and returns it as a Bitmap object
+ */
+ public async getScreenshotAsBitmap() {
+ const screenshot = await this.takeScreenshot();
+ const buffer = Buffer.from(screenshot, 'base64');
+ const session = (await Jimp.read(buffer)).clone();
+ return session.bitmap;
+ }
+
+ /**
+ * Sets the dimensions of a window to get the right size screenshot.
+ *
+ * @param {number} width
+ * @param {number} height
+ * @return {Promise}
+ */
+ public async setScreenshotSize(width: number, height: number) {
+ this.log.debug(`======browser======== setWindowSize ${width} ${height}`);
+ // We really want to set the Kibana app to a specific size without regard to the browser chrome (borders)
+ // But that means we first need to figure out the display scaling factor.
+ // NOTE: None of this is required when running Chrome headless because there's no scaling and no borders.
+ await this.setWindowSize(1200, 800);
+ const bitmap1 = await this.getScreenshotAsBitmap();
+ this.log.debug(
+ `======browser======== actual initial screenshot size width=${bitmap1.width}, height=${bitmap1.height}`
);
- public readonly isFirefox: boolean = browserType === Browsers.Firefox;
-
- /**
- * Returns instance of Actions API based on driver w3c flag
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#actions
- */
- public getActions() {
- return driver.actions();
- }
-
- /**
- * Get handle for an alert, confirm, or prompt dialog. (if any).
- * @return {Promise}
- */
- public async getAlert() {
- try {
- return await driver.switchTo().alert();
- } catch (e) {
- return null;
- }
- }
-
- /**
- * Retrieves the a rect describing the current top-level window's size and position.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Window.html
- *
- * @return {Promise<{height: number, width: number, x: number, y: number}>}
- */
- public async getWindowSize(): Promise<{ height: number; width: number; x: number; y: number }> {
- return await driver.manage().window().getRect();
- }
-
- /**
- * Sets the dimensions of a window.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Window.html
- *
- * @param {number} width
- * @param {number} height
- * @return {Promise}
- */
- public async setWindowSize(width: number, height: number) {
- await driver.manage().window().setRect({ width, height });
- }
-
- /**
- * Gets a screenshot of the focused window and returns it as a Bitmap object
- */
- public async getScreenshotAsBitmap() {
- const screenshot = await this.takeScreenshot();
- const buffer = Buffer.from(screenshot, 'base64');
- const session = (await Jimp.read(buffer)).clone();
- return session.bitmap;
- }
+ // drasticly change the window size so we can calculate the scaling
+ await this.setWindowSize(600, 400);
+ const bitmap2 = await this.getScreenshotAsBitmap();
+ this.log.debug(
+ `======browser======== actual second screenshot size width= ${bitmap2.width}, height=${bitmap2.height}`
+ );
- /**
- * Sets the dimensions of a window to get the right size screenshot.
- *
- * @param {number} width
- * @param {number} height
- * @return {Promise}
- */
- public async setScreenshotSize(width: number, height: number) {
- log.debug(`======browser======== setWindowSize ${width} ${height}`);
- // We really want to set the Kibana app to a specific size without regard to the browser chrome (borders)
- // But that means we first need to figure out the display scaling factor.
- // NOTE: None of this is required when running Chrome headless because there's no scaling and no borders.
- await this.setWindowSize(1200, 800);
- const bitmap1 = await this.getScreenshotAsBitmap();
- log.debug(
- `======browser======== actual initial screenshot size width=${bitmap1.width}, height=${bitmap1.height}`
- );
-
- // drasticly change the window size so we can calculate the scaling
- await this.setWindowSize(600, 400);
- const bitmap2 = await this.getScreenshotAsBitmap();
- log.debug(
- `======browser======== actual second screenshot size width= ${bitmap2.width}, height=${bitmap2.height}`
- );
-
- const xScaling = (bitmap1.width - bitmap2.width) / 600;
- const yScaling = (bitmap1.height - bitmap2.height) / 400;
- const xBorder = Math.round(600 - bitmap2.width / xScaling);
- const yBorder = Math.round(400 - bitmap2.height / yScaling);
- log.debug(
- `======browser======== calculated values xBorder= ${xBorder}, yBorder=${yBorder}, xScaling=${xScaling}, yScaling=${yScaling}`
- );
- log.debug(
- `======browser======== setting browser size to ${width + xBorder} x ${height + yBorder}`
- );
- await this.setWindowSize(width + xBorder, height + yBorder);
-
- const bitmap3 = await this.getScreenshotAsBitmap();
- // when there is display scaling this won't show the expected size. It will show expected size * scaling factor
- log.debug(
- `======browser======== final screenshot size width=${bitmap3.width}, height=${bitmap3.height}`
- );
- }
+ const xScaling = (bitmap1.width - bitmap2.width) / 600;
+ const yScaling = (bitmap1.height - bitmap2.height) / 400;
+ const xBorder = Math.round(600 - bitmap2.width / xScaling);
+ const yBorder = Math.round(400 - bitmap2.height / yScaling);
+ this.log.debug(
+ `======browser======== calculated values xBorder= ${xBorder}, yBorder=${yBorder}, xScaling=${xScaling}, yScaling=${yScaling}`
+ );
+ this.log.debug(
+ `======browser======== setting browser size to ${width + xBorder} x ${height + yBorder}`
+ );
+ await this.setWindowSize(width + xBorder, height + yBorder);
- /**
- * Gets the URL that is loaded in the focused window/frame.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#getCurrentUrl
- *
- * @return {Promise}
- */
- public async getCurrentUrl() {
- // strip _t=Date query param when url is read
- const current = await driver.getCurrentUrl();
- const currentWithoutTime = modifyUrl(current, (parsed) => {
- delete (parsed.query as any)._t;
+ const bitmap3 = await this.getScreenshotAsBitmap();
+ // when there is display scaling this won't show the expected size. It will show expected size * scaling factor
+ this.log.debug(
+ `======browser======== final screenshot size width=${bitmap3.width}, height=${bitmap3.height}`
+ );
+ }
+
+ /**
+ * Gets the URL that is loaded in the focused window/frame.
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_WebDriver.html#getCurrentUrl
+ *
+ * @return {Promise}
+ */
+ public async getCurrentUrl() {
+ // strip _t=Date query param when url is read
+ const current = await this.driver.getCurrentUrl();
+ const currentWithoutTime = modifyUrl(current, (parsed) => {
+ delete (parsed.query as any)._t;
+ return void 0;
+ });
+ return currentWithoutTime;
+ }
+
+ /**
+ * Gets the page/document title of the focused window/frame.
+ * https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/chrome_exports_Driver.html#getTitle
+ */
+ public async getTitle() {
+ return await this.driver.getTitle();
+ }
+
+ /**
+ * Navigates the focused window/frame to a new URL.
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/chrome_exports_Driver.html#get
+ *
+ * @param {string} url
+ * @param {boolean} insertTimestamp Optional
+ * @return {Promise}
+ */
+ public async get(url: string, insertTimestamp: boolean = true) {
+ if (insertTimestamp) {
+ const urlWithTime = modifyUrl(url, (parsed) => {
+ (parsed.query as any)._t = Date.now();
return void 0;
});
- return currentWithoutTime;
- }
-
- /**
- * Gets the page/document title of the focused window/frame.
- * https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/chrome_exports_Driver.html#getTitle
- */
- public async getTitle() {
- return await driver.getTitle();
- }
-
- /**
- * Navigates the focused window/frame to a new URL.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/chrome_exports_Driver.html#get
- *
- * @param {string} url
- * @param {boolean} insertTimestamp Optional
- * @return {Promise}
- */
- public async get(url: string, insertTimestamp: boolean = true) {
- if (insertTimestamp) {
- const urlWithTime = modifyUrl(url, (parsed) => {
- (parsed.query as any)._t = Date.now();
- return void 0;
- });
-
- return await driver.get(urlWithTime);
- }
- return await driver.get(url);
- }
- /**
- * Retrieves the cookie with the given name. Returns null if there is no such cookie. The cookie will be returned as
- * a JSON object as described by the WebDriver wire protocol.
- * https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Options.html
- *
- * @param {string} cookieName
- * @return {Promise}
- */
- public async getCookie(cookieName: string) {
- return await driver.manage().getCookie(cookieName);
- }
-
- /**
- * Pauses the execution in the browser, similar to setting a breakpoint for debugging.
- * @return {Promise}
- */
- public async pause() {
- await driver.executeAsyncScript(`(async () => { debugger; return Promise.resolve(); })()`);
- }
-
- /**
- * Moves the remote environment’s mouse cursor to the specified point {x, y} which is
- * offset to browser page top left corner.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#move
- *
- * @param {x: number, y: number} point on browser page
- * @return {Promise}
- */
- public async moveMouseTo(point: { x: number; y: number }): Promise {
- await this.getActions().move({ x: 0, y: 0 }).perform();
- await this.getActions().move({ x: point.x, y: point.y, origin: Origin.POINTER }).perform();
- }
-
- /**
- * Does a drag-and-drop action from one point to another
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#dragAndDrop
- *
- * @return {Promise}
- */
- public async dragAndDrop(
- from: {
- location: WebElementWrapper | { x?: number; y?: number };
- offset?: { x?: number; y?: number };
- },
- to: {
- location: WebElementWrapper | { x?: number; y?: number };
- offset?: { x?: number; y?: number };
+ return await this.driver.get(urlWithTime);
+ }
+ return await this.driver.get(url);
+ }
+
+ /**
+ * Retrieves the cookie with the given name. Returns null if there is no such cookie. The cookie will be returned as
+ * a JSON object as described by the WebDriver wire protocol.
+ * https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Options.html
+ *
+ * @param {string} cookieName
+ * @return {Promise}
+ */
+ public async getCookie(cookieName: string) {
+ return await this.driver.manage().getCookie(cookieName);
+ }
+
+ /**
+ * Pauses the execution in the browser, similar to setting a breakpoint for debugging.
+ * @return {Promise}
+ */
+ public async pause() {
+ await this.driver.executeAsyncScript(`(async () => { debugger; return Promise.resolve(); })()`);
+ }
+
+ /**
+ * Moves the remote environment’s mouse cursor to the specified point {x, y} which is
+ * offset to browser page top left corner.
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#move
+ *
+ * @param {x: number, y: number} point on browser page
+ * @return {Promise}
+ */
+ public async moveMouseTo(point: { x: number; y: number }): Promise {
+ await this.getActions().move({ x: 0, y: 0 }).perform();
+ await this.getActions().move({ x: point.x, y: point.y, origin: Origin.POINTER }).perform();
+ }
+
+ /**
+ * Does a drag-and-drop action from one point to another
+ * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#dragAndDrop
+ *
+ * @return {Promise}
+ */
+ public async dragAndDrop(
+ from: {
+ location: WebElementWrapper | { x?: number; y?: number };
+ offset?: { x?: number; y?: number };
+ },
+ to: {
+ location: WebElementWrapper | { x?: number; y?: number };
+ offset?: { x?: number; y?: number };
+ }
+ ) {
+ // The offset should be specified in pixels relative to the center of the element's bounding box
+ const getW3CPoint = (data: any) => {
+ if (!data.offset) {
+ data.offset = {};
}
- ) {
- // The offset should be specified in pixels relative to the center of the element's bounding box
- const getW3CPoint = (data: any) => {
- if (!data.offset) {
- data.offset = {};
- }
- return data.location instanceof WebElementWrapper
- ? {
- x: data.offset.x || 0,
- y: data.offset.y || 0,
- origin: data.location._webElement,
- }
- : { x: data.location.x, y: data.location.y, origin: Origin.POINTER };
- };
-
- const startPoint = getW3CPoint(from);
- const endPoint = getW3CPoint(to);
- await this.getActions().move({ x: 0, y: 0 }).perform();
- return await this.getActions().move(startPoint).press().move(endPoint).release().perform();
- }
-
- /**
- * Performs drag and drop for html5 native drag and drop implementation
- * There's a bug in Chromedriver for html5 dnd that doesn't allow to use the method `dragAndDrop` defined above
- * https://github.com/SeleniumHQ/selenium/issues/6235
- * This implementation simulates user's action by calling the drag and drop specific events directly.
- *
- * @param {string} from html selector
- * @param {string} to html selector
- * @return {Promise}
- */
- public async html5DragAndDrop(from: string, to: string) {
- await this.execute(
- `
- function createEvent(typeOfEvent) {
- const event = document.createEvent("CustomEvent");
- event.initCustomEvent(typeOfEvent, true, true, null);
- event.dataTransfer = {
- data: {},
- setData: function (key, value) {
- this.data[key] = value;
- },
- getData: function (key) {
- return this.data[key];
- }
- };
- return event;
+ return data.location instanceof WebElementWrapper
+ ? {
+ x: data.offset.x || 0,
+ y: data.offset.y || 0,
+ origin: data.location._webElement,
}
- function dispatchEvent(element, event, transferData) {
- if (transferData !== undefined) {
- event.dataTransfer = transferData;
- }
- if (element.dispatchEvent) {
- element.dispatchEvent(event);
- } else if (element.fireEvent) {
- element.fireEvent("on" + event.type, event);
+ : { x: data.location.x, y: data.location.y, origin: Origin.POINTER };
+ };
+
+ const startPoint = getW3CPoint(from);
+ const endPoint = getW3CPoint(to);
+ await this.getActions().move({ x: 0, y: 0 }).perform();
+ return await this.getActions().move(startPoint).press().move(endPoint).release().perform();
+ }
+
+ /**
+ * Performs drag and drop for html5 native drag and drop implementation
+ * There's a bug in Chromedriver for html5 dnd that doesn't allow to use the method `dragAndDrop` defined above
+ * https://github.com/SeleniumHQ/selenium/issues/6235
+ * This implementation simulates user's action by calling the drag and drop specific events directly.
+ *
+ * @param {string} from html selector
+ * @param {string} to html selector
+ * @return {Promise}
+ */
+ public async html5DragAndDrop(from: string, to: string) {
+ await this.execute(
+ `
+ function createEvent(typeOfEvent) {
+ const event = document.createEvent("CustomEvent");
+ event.initCustomEvent(typeOfEvent, true, true, null);
+ event.dataTransfer = {
+ data: {},
+ setData: function (key, value) {
+ this.data[key] = value;
+ },
+ getData: function (key) {
+ return this.data[key];
}
+ };
+ return event;
+ }
+ function dispatchEvent(element, event, transferData) {
+ if (transferData !== undefined) {
+ event.dataTransfer = transferData;
}
-
- const origin = document.querySelector(arguments[0]);
-
- const dragStartEvent = createEvent('dragstart');
- dispatchEvent(origin, dragStartEvent);
-
- setTimeout(() => {
- const dropEvent = createEvent('drop');
- const target = document.querySelector(arguments[1]);
- dispatchEvent(target, dropEvent, dragStartEvent.dataTransfer);
- const dragEndEvent = createEvent('dragend');
- dispatchEvent(origin, dragEndEvent, dropEvent.dataTransfer);
- }, 100);
- `,
- from,
- to
- );
- // wait for 150ms to make sure the script has run
- await delay(150);
- }
-
- /**
- * Reloads the current browser window/frame.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Navigation.html#refresh
- *
- * @return {Promise}
- */
- public async refresh() {
- await driver.navigate().refresh();
- }
-
- /**
- * Navigates the focused window/frame back one page using the browser’s navigation history.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Navigation.html#back
- *
- * @return {Promise}
- */
- public async goBack() {
- await driver.navigate().back();
- }
-
- /**
- * Moves forwards in the browser history.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Navigation.html#forward
- *
- * @return {Promise}
- */
- public async goForward() {
- await driver.navigate().forward();
- }
-
- /**
- * Navigates to a URL via the browser history.
- * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver_exports_Navigation.html#to
- *
- * @return {Promise