diff --git a/.eslintrc.js b/.eslintrc.js
index dde0ce010d4d44..56c06902e062b5 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -238,6 +238,7 @@ module.exports = {
],
from: [
'(src|x-pack)/plugins/**/(public|server)/**/*',
+ '!(src|x-pack)/plugins/**/(public|server)/mocks/index.{js,ts}',
'!(src|x-pack)/plugins/**/(public|server)/(index|mocks).{js,ts,tsx}',
],
allowSameFolder: true,
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md
index 24b56a9b986216..a79244a24acf57 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md
@@ -21,3 +21,9 @@ export interface IIndexPattern
| [title](./kibana-plugin-plugins-data-server.iindexpattern.title.md) | string | |
| [type](./kibana-plugin-plugins-data-server.iindexpattern.type.md) | string | |
+## Methods
+
+| Method | Description |
+| --- | --- |
+| [getTimeField()](./kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md) | |
+
diff --git a/docs/user/alerting/action-types.asciidoc b/docs/user/alerting/action-types.asciidoc
index 8794c389d72bcb..09878b3059ac87 100644
--- a/docs/user/alerting/action-types.asciidoc
+++ b/docs/user/alerting/action-types.asciidoc
@@ -43,11 +43,10 @@ see https://www.elastic.co/subscriptions[the subscription page].
[[create-connectors]]
=== Preconfigured connectors and action types
-You can create connectors for actions in <> or via the action API.
-For out-of-the-box and standardized connectors, you can <>
+For out-of-the-box and standardized connectors, you can <>
before {kib} starts.
-Action type with only preconfigured connectors could be specified as a <>.
+If you preconfigure a connector, you can also <>.
include::action-types/email.asciidoc[]
include::action-types/index.asciidoc[]
@@ -56,4 +55,3 @@ include::action-types/server-log.asciidoc[]
include::action-types/slack.asciidoc[]
include::action-types/webhook.asciidoc[]
include::pre-configured-connectors.asciidoc[]
-include::pre-configured-action-types.asciidoc[]
diff --git a/docs/user/alerting/action-types/email.asciidoc b/docs/user/alerting/action-types/email.asciidoc
index 794fc14005f2ff..689d870d9cadc3 100644
--- a/docs/user/alerting/action-types/email.asciidoc
+++ b/docs/user/alerting/action-types/email.asciidoc
@@ -19,6 +19,37 @@ Username:: username for 'login' type authentication.
Password:: password for 'login' type authentication.
[float]
+[[Preconfigured-email-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ id: 'my-email'
+ name: preconfigured-email-action-type
+ actionTypeId: .email
+ config:
+ from: testsender@test.com <1.1>
+ host: validhostname <1.2>
+ port: 8080 <1.3>
+ secure: false <1.4>
+ secrets:
+ user: testuser <2.1>
+ password: passwordkeystorevalue <2.2>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1.1> `from:` is an email address and correspond to *Sender*.
+<1.2> `host:` is a string and correspond to *Host*.
+<1.3> `port:` is a number and correspond to *Port*.
+<1.4> `secure:` is a boolean and correspond to *Secure*.
+
+`secrets` defines action type sensitive configuration:
+
+<2.1> `user:` is a string and correspond to *User*.
+<2.2> `password:` is a string and correspond to *Password*. Should be stored in the <>.
+
+
[[email-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/index.asciidoc b/docs/user/alerting/action-types/index.asciidoc
index 625b8f704b7c6d..4f5254e3311d81 100644
--- a/docs/user/alerting/action-types/index.asciidoc
+++ b/docs/user/alerting/action-types/index.asciidoc
@@ -15,6 +15,28 @@ Index:: The {es} index to be written to.
Refresh:: Setting for the {ref}/docs-refresh.html[refresh] policy for the write request.
Execution time field:: This field will be automatically set to the time the alert condition was detected.
+[float]
+[[Preconfigured-index-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ id: 'my-index'
+ name: action-type-index
+ actionTypeId: .index
+ config:
+ index: .kibana <1>
+ refresh: true <2>
+ executionTimeField: somedate <3>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1> `index:` is a string and correspond to *Index*.
+<2> `refresh:` is a boolean and correspond to *Refresh*.
+<3> `executionTimeField:` is a string and correspond to *Execution time field*.
+
+
[float]
[[index-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc
index 673b4f6263e18f..957c035b028f62 100644
--- a/docs/user/alerting/action-types/pagerduty.asciidoc
+++ b/docs/user/alerting/action-types/pagerduty.asciidoc
@@ -135,6 +135,29 @@ Name:: The name of the connector. The name is used to identify a connector
API URL:: An optional PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue`. If you are using the <> setting, make sure the hostname is whitelisted.
Integration Key:: A 32 character PagerDuty Integration Key for an integration on a service, also referred to as the routing key.
+[float]
+[[Preconfigured-pagerduty-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ id: 'my-pagerduty'
+ name: preconfigured-pagerduty-action-type
+ actionTypeId: .pagerduty
+ config:
+ apiUrl: https://test.host <1.1>
+ secrets:
+ routingKey: testroutingkey <2.1>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1.1> `apiUrl:` is URL string and correspond to *API URL*.
+
+`secrets` defines action type sensitive configuration:
+
+<2.1> `routingKey:` is a string and correspond to *Integration Key*.
+
[float]
[[pagerduty-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/server-log.asciidoc b/docs/user/alerting/action-types/server-log.asciidoc
index 8f888785626c9b..f08dbe5542f0fe 100644
--- a/docs/user/alerting/action-types/server-log.asciidoc
+++ b/docs/user/alerting/action-types/server-log.asciidoc
@@ -12,6 +12,17 @@ Server log connectors have the following configuration properties:
Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action.
+[float]
+[[Preconfigured-server-log-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ id: 'my-server-log'
+ name: test
+ actionTypeId: .server-log
+--
+
[float]
[[server-log-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/slack.asciidoc b/docs/user/alerting/action-types/slack.asciidoc
index c0965d65bfdbec..195093536bc044 100644
--- a/docs/user/alerting/action-types/slack.asciidoc
+++ b/docs/user/alerting/action-types/slack.asciidoc
@@ -13,6 +13,24 @@ Slack connectors have the following configuration properties:
Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action.
Webhook URL:: The URL of the incoming webhook. See https://api.slack.com/messaging/webhooks#getting_started[Slack Incoming Webhooks] for instructions on generating this URL. If you are using the <> setting, make sure the hostname is whitelisted.
+[float]
+[[Preconfigured-slack-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ id: 'my-slack'
+ name: preconfigured-slack-action-type
+ actionTypeId: .slack
+ config:
+ webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz' <1>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1> `webhookUrl:` is URL string and correspond to *Webhook URL*.
+
+
[float]
[[slack-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/action-types/webhook.asciidoc b/docs/user/alerting/action-types/webhook.asciidoc
index 64bfa6a1d6364b..f4c108426642d1 100644
--- a/docs/user/alerting/action-types/webhook.asciidoc
+++ b/docs/user/alerting/action-types/webhook.asciidoc
@@ -17,6 +17,36 @@ Headers:: A set of key-value pairs sent as headers with the request
User:: An optional username. If set, HTTP basic authentication is used. Currently only basic authentication is supported.
Password:: An optional password. If set, HTTP basic authentication is used. Currently only basic authentication is supported.
+[float]
+[[Preconfigured-webhook-configuration]]
+==== Preconfigured action type
+
+[source,text]
+--
+ id: 'my-webhook'
+ name: preconfigured-webhook-action-type
+ actionTypeId: .webhook
+ config:
+ url: https://test.host <1.1>
+ method: POST <1.2>
+ headers: <1.3>
+ testheader: testvalue
+ secrets:
+ user: testuser <2.1>
+ password: passwordkeystorevalue <2.2>
+--
+
+`config` defines the action type specific to the configuration and contains the following properties:
+
+<1.1> `url:` is URL string and correspond to *URL*.
+<1.2> `method:` is a string and correspond to *Method*.
+<1.3> `headers:` is Record and correspond to *Headers*.
+
+`secrets` defines action type sensitive configuration:
+
+<2.1> `user:` is a string and correspond to *User*.
+<2.2> `password:` is a string and correspond to *Password*. Should be stored in the <>.
+
[float]
[[webhook-action-configuration]]
==== Action configuration
diff --git a/docs/user/alerting/images/pre-configured-action-type-select-type.png b/docs/user/alerting/images/pre-configured-action-type-select-type.png
index 5f555f851cd816..29e5a29edc7c06 100644
Binary files a/docs/user/alerting/images/pre-configured-action-type-select-type.png and b/docs/user/alerting/images/pre-configured-action-type-select-type.png differ
diff --git a/docs/user/alerting/pre-configured-action-types.asciidoc b/docs/user/alerting/pre-configured-action-types.asciidoc
deleted file mode 100644
index 780a2119037b1a..00000000000000
--- a/docs/user/alerting/pre-configured-action-types.asciidoc
+++ /dev/null
@@ -1,61 +0,0 @@
-[role="xpack"]
-[[pre-configured-action-types]]
-
-== Preconfigured action types
-
-A preconfigure an action type has all the information it needs prior to startup.
-A preconfigured action type offers the following capabilities:
-
-- Requires no setup. Configuration and credentials needed to execute an
-action are predefined.
-- Has only <>.
-- Connectors of the preconfigured action type cannot be edited or deleted.
-
-[float]
-[[preconfigured-action-type-example]]
-=== Creating a preconfigured action
-
-In the `kibana.yml` file:
-
-. Exclude the action type from `xpack.actions.enabledActionTypes`.
-. Add all its connectors.
-
-The following example shows a valid configuration of preconfigured action type with one out-of-the box connector.
-
-```js
- xpack.actions.enabledActionTypes: ['.slack', '.email', '.index'] <1>
- xpack.actions.preconfigured: <2>
- - id: 'my-server-log'
- actionTypeId: .server-log
- name: 'Server log #xyz'
-```
-
-<1> `enabledActionTypes` should exclude preconfigured action type to prevent creating and deleting connectors.
-<2> `preconfigured` is the setting for defining the list of available connectors for the preconfigured action type.
-
-[float]
-[[pre-configured-action-type-alert-form]]
-=== Attaching a preconfigured action to an alert
-
-To attach an action to an alert,
-select from a list of available action types, and
-then select the *Server log* type. This action type was configured previously.
-
-[role="screenshot"]
-image::images/pre-configured-action-type-alert-form.png[Create alert with selected Server log action type]
-
-[float]
-[[managing-pre-configured-action-types]]
-=== Managing preconfigured actions
-
-Connectors with preconfigured actions appear in the connector list, regardless of which space the user is in.
-They are tagged as “preconfigured” and cannot be deleted.
-
-[role="screenshot"]
-image::images/pre-configured-action-type-managing.png[Connectors managing tab with pre-cofigured]
-
-Clicking *Create connector* shows the list of available action types.
-Preconfigured action types are not included because you can't create a connector with a preconfigured action type.
-
-[role="screenshot"]
-image::images/pre-configured-action-type-select-type.png[Pre-configured connector create menu]
diff --git a/docs/user/alerting/pre-configured-connectors.asciidoc b/docs/user/alerting/pre-configured-connectors.asciidoc
index 4c408da92f5791..5ff4ea15df561f 100644
--- a/docs/user/alerting/pre-configured-connectors.asciidoc
+++ b/docs/user/alerting/pre-configured-connectors.asciidoc
@@ -1,11 +1,10 @@
[role="xpack"]
-[[pre-configured-connectors]]
+[[pre-configured-action-types-and-connectors]]
-== Preconfigured connectors
+== Preconfigured connectors and action types
-You can preconfigure an action connector to have all the information it needs prior to startup
+You can preconfigure an action type or a connector to have all the information it needs prior to startup
by adding it to the `kibana.yml` file.
-Sensitive configuration information, such as credentials, can use the {kib} keystore.
Preconfigured connectors offer the following capabilities:
@@ -14,11 +13,15 @@ action are predefined, including the connector name and ID.
- Appear in all spaces because they are not saved objects.
- Cannot be edited or deleted.
+Sensitive configuration information, such as credentials, can use the <>.
+
+A preconfigured action types has only preconfigured connectors. Preconfigured connectors can belong to either the preconfigured action type or to the regular action type.
+
[float]
[[preconfigured-connector-example]]
-=== Example of a preconfigured connector
+=== Creating a preconfigured connector
-The following example shows a valid configuration 2 out-of-the box connector.
+The following example shows a valid configuration of two out-of-the box connectors: <> and <>.
```js
xpack.actions.preconfigured:
@@ -49,26 +52,30 @@ The following example shows a valid configuration 2 out-of-the box connector.
[NOTE]
==============================================
-Sensitive properties, such as passwords, can also be stored in the {kib} keystore.
+Sensitive properties, such as passwords, can also be stored in the <>.
==============================================
[float]
-[[pre-configured-connector-alert-form]]
-=== Creating an alert with a preconfigured connector
+[[preconfigured-action-type-example]]
+=== Creating a preconfigured action type
-When attaching an action to an alert,
-select from a list of available action types, and
-then select the Slack or Webhook type. Those action types were configured previously.
-The preconfigured connector is installed and is automatically selected.
+In the `kibana.yml` file:
-[role="screenshot"]
-image::images/alert-pre-configured-slack-connector.png[Create alert with selected Slack action type]
+. Exclude the action type from `xpack.actions.enabledActionTypes`.
+. Add all its preconfigured connectors.
-The dropdown is populated with additional preconfigured Slack connectors.
-The `preconfigured` label distinguishes them from space-aware connectors that use saved objects.
+The following example shows a valid configuration of preconfigured action type with one out-of-the box connector.
-[role="screenshot"]
-image::images/alert-pre-configured-connectors-dropdown.png[Dropdown list with pre-cofigured connectors]
+```js
+ xpack.actions.enabledActionTypes: ['.slack', '.email', '.index'] <1>
+ xpack.actions.preconfigured: <2>
+ - id: 'my-server-log'
+ actionTypeId: .server-log
+ name: 'Server log #xyz'
+```
+
+<1> `enabledActionTypes` should exclude preconfigured action type to prevent creating and deleting connectors.
+<2> `preconfigured` is the setting for defining the list of available connectors for the preconfigured action type.
[float]
[[managing-pre-configured-connectors]]
@@ -85,3 +92,37 @@ A message indicates that this is a preconfigured connector.
[role="screenshot"]
image::images/pre-configured-connectors-view-screen.png[Pre-configured connector view details]
+
+The connector details preview is disabled for preconfigured connectors.
+
+[role="screenshot"]
+image::images/pre-configured-action-type-managing.png[Connectors managing tab with pre-cofigured]
+
+
+[float]
+[[managing-pre-configured-action-types]]
+=== Managing preconfigured action types
+
+Clicking *Create connector* shows the list of available action types.
+Disabled action types are not included.
+
+[role="screenshot"]
+image::images/pre-configured-action-type-select-type.png[Pre-configured connector create menu]
+
+[float]
+[[pre-configured-connector-alert-form]]
+=== Alert with a preconfigured connector
+
+When attaching an action to an alert,
+select from a list of available action types, and
+then select the Slack or Webhook type. Those action types were configured previously.
+The preconfigured connector is installed and is automatically selected.
+
+[role="screenshot"]
+image::images/alert-pre-configured-slack-connector.png[Create alert with selected Slack action type]
+
+The dropdown is populated with additional preconfigured Slack connectors.
+The `preconfigured` label distinguishes them from space-aware connectors that use saved objects.
+
+[role="screenshot"]
+image::images/alert-pre-configured-connectors-dropdown.png[Dropdown list with pre-cofigured connectors]
diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts
index a13f61af601733..5019c8bd223411 100644
--- a/src/dev/typescript/projects.ts
+++ b/src/dev/typescript/projects.ts
@@ -50,6 +50,9 @@ export const PROJECTS = [
...glob
.sync('test/plugin_functional/plugins/*/tsconfig.json', { cwd: REPO_ROOT })
.map(path => new Project(resolve(REPO_ROOT, path))),
+ ...glob
+ .sync('test/interpreter_functional/plugins/*/tsconfig.json', { cwd: REPO_ROOT })
+ .map(path => new Project(resolve(REPO_ROOT, path))),
];
export function filterProjectsByFlag(projectFlag?: string) {
diff --git a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
index 1bc85fa110ca0b..698c124d2d8057 100644
--- a/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
+++ b/src/plugins/dashboard/public/application/__snapshots__/dashboard_empty_screen.test.tsx.snap
@@ -301,7 +301,7 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = `
>
@@ -995,7 +995,7 @@ exports[`DashboardEmptyScreen renders correctly without visualize paragraph 1`]
>
diff --git a/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx b/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
index 8bf205b8cb5070..955d5244ce1904 100644
--- a/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
+++ b/src/plugins/dashboard/public/application/dashboard_empty_screen.tsx
@@ -50,8 +50,8 @@ export function DashboardEmptyScreen({
}: DashboardEmptyScreenProps) {
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
const emptyStateGraphicURL = IS_DARK_THEME
- ? '/plugins/kibana/home/assets/welcome_graphic_dark_2x.png'
- : '/plugins/kibana/home/assets/welcome_graphic_light_2x.png';
+ ? '/plugins/home/assets/welcome_graphic_dark_2x.png'
+ : '/plugins/home/assets/welcome_graphic_light_2x.png';
const linkToVisualizeParagraph = (
}
description={
diff --git a/src/legacy/core_plugins/kibana/public/home/assets/illustration_elastic_heart.png b/src/plugins/home/public/assets/illustration_elastic_heart.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/assets/illustration_elastic_heart.png
rename to src/plugins/home/public/assets/illustration_elastic_heart.png
diff --git a/src/legacy/core_plugins/kibana/public/home/sample_data_resources/ecommerce/dashboard.png b/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/sample_data_resources/ecommerce/dashboard.png
rename to src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard.png
diff --git a/src/legacy/core_plugins/kibana/public/home/sample_data_resources/ecommerce/dashboard_dark.png b/src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard_dark.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/sample_data_resources/ecommerce/dashboard_dark.png
rename to src/plugins/home/public/assets/sample_data_resources/ecommerce/dashboard_dark.png
diff --git a/src/legacy/core_plugins/kibana/public/home/sample_data_resources/flights/dashboard.png b/src/plugins/home/public/assets/sample_data_resources/flights/dashboard.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/sample_data_resources/flights/dashboard.png
rename to src/plugins/home/public/assets/sample_data_resources/flights/dashboard.png
diff --git a/src/legacy/core_plugins/kibana/public/home/sample_data_resources/flights/dashboard_dark.png b/src/plugins/home/public/assets/sample_data_resources/flights/dashboard_dark.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/sample_data_resources/flights/dashboard_dark.png
rename to src/plugins/home/public/assets/sample_data_resources/flights/dashboard_dark.png
diff --git a/src/legacy/core_plugins/kibana/public/home/sample_data_resources/logs/dashboard.png b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/sample_data_resources/logs/dashboard.png
rename to src/plugins/home/public/assets/sample_data_resources/logs/dashboard.png
diff --git a/src/legacy/core_plugins/kibana/public/home/sample_data_resources/logs/dashboard_dark.png b/src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/sample_data_resources/logs/dashboard_dark.png
rename to src/plugins/home/public/assets/sample_data_resources/logs/dashboard_dark.png
diff --git a/src/legacy/core_plugins/kibana/public/home/assets/welcome_graphic_dark_2x.png b/src/plugins/home/public/assets/welcome_graphic_dark_2x.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/assets/welcome_graphic_dark_2x.png
rename to src/plugins/home/public/assets/welcome_graphic_dark_2x.png
diff --git a/src/legacy/core_plugins/kibana/public/home/assets/welcome_graphic_light_2x.png b/src/plugins/home/public/assets/welcome_graphic_light_2x.png
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/home/assets/welcome_graphic_light_2x.png
rename to src/plugins/home/public/assets/welcome_graphic_light_2x.png
diff --git a/src/plugins/home/server/services/sample_data/data_sets/ecommerce/index.ts b/src/plugins/home/server/services/sample_data/data_sets/ecommerce/index.ts
index 3e16187c443432..b0cc2e2db3cc92 100644
--- a/src/plugins/home/server/services/sample_data/data_sets/ecommerce/index.ts
+++ b/src/plugins/home/server/services/sample_data/data_sets/ecommerce/index.ts
@@ -36,8 +36,8 @@ export const ecommerceSpecProvider = function(): SampleDatasetSchema {
id: 'ecommerce',
name: ecommerceName,
description: ecommerceDescription,
- previewImagePath: '/plugins/kibana/home/sample_data_resources/ecommerce/dashboard.png',
- darkPreviewImagePath: '/plugins/kibana/home/sample_data_resources/ecommerce/dashboard_dark.png',
+ previewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard.png',
+ darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/ecommerce/dashboard_dark.png',
overviewDashboard: '722b74f0-b882-11e8-a6d9-e546fe2bba5f',
appLinks: initialAppLinks,
defaultIndex: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f',
diff --git a/src/plugins/home/server/services/sample_data/data_sets/flights/index.ts b/src/plugins/home/server/services/sample_data/data_sets/flights/index.ts
index d63ea8f7fb4930..fc3cb6094b5eaa 100644
--- a/src/plugins/home/server/services/sample_data/data_sets/flights/index.ts
+++ b/src/plugins/home/server/services/sample_data/data_sets/flights/index.ts
@@ -36,8 +36,8 @@ export const flightsSpecProvider = function(): SampleDatasetSchema {
id: 'flights',
name: flightsName,
description: flightsDescription,
- previewImagePath: '/plugins/kibana/home/sample_data_resources/flights/dashboard.png',
- darkPreviewImagePath: '/plugins/kibana/home/sample_data_resources/flights/dashboard_dark.png',
+ previewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard.png',
+ darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/flights/dashboard_dark.png',
overviewDashboard: '7adfa750-4c81-11e8-b3d7-01146121b73d',
appLinks: initialAppLinks,
defaultIndex: 'd3d7af60-4c81-11e8-b3d7-01146121b73d',
diff --git a/src/plugins/home/server/services/sample_data/data_sets/logs/index.ts b/src/plugins/home/server/services/sample_data/data_sets/logs/index.ts
index bb6e2982f59a08..d8f205dff24e8b 100644
--- a/src/plugins/home/server/services/sample_data/data_sets/logs/index.ts
+++ b/src/plugins/home/server/services/sample_data/data_sets/logs/index.ts
@@ -36,8 +36,8 @@ export const logsSpecProvider = function(): SampleDatasetSchema {
id: 'logs',
name: logsName,
description: logsDescription,
- previewImagePath: '/plugins/kibana/home/sample_data_resources/logs/dashboard.png',
- darkPreviewImagePath: '/plugins/kibana/home/sample_data_resources/logs/dashboard_dark.png',
+ previewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard.png',
+ darkPreviewImagePath: '/plugins/home/assets/sample_data_resources/logs/dashboard_dark.png',
overviewDashboard: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b',
appLinks: initialAppLinks,
defaultIndex: '90943e30-9a47-11e8-b64d-95841ca0b247',
diff --git a/test/functional/apps/timelion/index.js b/test/functional/apps/timelion/index.js
index 3b5167addf4e6f..021fa243978506 100644
--- a/test/functional/apps/timelion/index.js
+++ b/test/functional/apps/timelion/index.js
@@ -28,7 +28,7 @@ export default function({ getService, loadTestFile }) {
before(async function() {
log.debug('Starting timelion before method');
- browser.setWindowSize(1280, 800);
+ await browser.setWindowSize(1280, 800);
await esArchiver.loadIfNeeded('logstash_functional');
await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' });
});
diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts
index 81d22838d1e8b7..b7a6e10efd7dc1 100644
--- a/test/functional/page_objects/settings_page.ts
+++ b/test/functional/page_objects/settings_page.ts
@@ -33,7 +33,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
class SettingsPage {
async clickNavigation() {
- find.clickDisplayedByCssSelector('.app-link:nth-child(5) a');
+ await find.clickDisplayedByCssSelector('.app-link:nth-child(5) a');
}
async clickLinkText(text: string) {
@@ -110,7 +110,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
}
async toggleAdvancedSettingCheckbox(propertyName: string) {
- testSubjects.click(`advancedSetting-editField-${propertyName}`);
+ await testSubjects.click(`advancedSetting-editField-${propertyName}`);
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.click(`advancedSetting-saveButton`);
await PageObjects.header.waitUntilLoadingHasFinished();
diff --git a/test/functional/services/find.ts b/test/functional/services/find.ts
index 312668b718dc0c..bdcc5ba95e9fbd 100644
--- a/test/functional/services/find.ts
+++ b/test/functional/services/find.ts
@@ -476,7 +476,7 @@ export async function FindProvider({ getService }: FtrProviderContext) {
value: string
): Promise {
log.debug(`Find.waitForAttributeToChange('${selector}', '${attribute}', '${value}')`);
- retry.waitFor(`${attribute} to equal "${value}"`, async () => {
+ await retry.waitFor(`${attribute} to equal "${value}"`, async () => {
const el = await this.byCssSelector(selector);
return value === (await el.getAttribute(attribute));
});
diff --git a/test/interpreter_functional/config.ts b/test/interpreter_functional/config.ts
index 0fe7df4d507154..d3cfcea9823e9c 100644
--- a/test/interpreter_functional/config.ts
+++ b/test/interpreter_functional/config.ts
@@ -50,6 +50,9 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
+
+ // Required to load new platform plugins via `--plugin-path` flag.
+ '--env.name=development',
...plugins.map(
pluginDir => `--plugin-path=${path.resolve(__dirname, 'plugins', pluginDir)}`
),
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/index.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/index.ts
deleted file mode 100644
index 1d5564ec06e4ef..00000000000000
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/index.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { Legacy } from 'kibana';
-import {
- ArrayOrItem,
- LegacyPluginApi,
- LegacyPluginSpec,
- LegacyPluginOptions,
-} from 'src/legacy/plugin_discovery/types';
-
-// eslint-disable-next-line import/no-default-export
-export default function(kibana: LegacyPluginApi): ArrayOrItem {
- const pluginSpec: Partial = {
- id: 'kbn_tp_run_pipeline',
- uiExports: {
- app: {
- title: 'Run Pipeline',
- description: 'This is a sample plugin to test running pipeline expressions',
- main: 'plugins/kbn_tp_run_pipeline/legacy',
- },
- },
-
- init(server: Legacy.Server) {
- // The following lines copy over some configuration variables from Kibana
- // to this plugin. This will be needed when embedding visualizations, so that e.g.
- // region map is able to get its configuration.
- server.injectUiAppVars('kbn_tp_run_pipeline', async () => {
- return server.getInjectedUiAppVars('kibana');
- });
- },
- };
- return new kibana.Plugin(pluginSpec);
-}
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/kibana.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/kibana.json
new file mode 100644
index 00000000000000..f0c1c3a34fbc09
--- /dev/null
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/kibana.json
@@ -0,0 +1,13 @@
+{
+ "id": "kbn_tp_run_pipeline",
+ "version": "0.0.1",
+ "kibanaVersion": "kibana",
+ "requiredPlugins": [
+ "data",
+ "savedObjects",
+ "kibanaUtils",
+ "expressions"
+ ],
+ "server": false,
+ "ui": true
+}
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
index 338e85038922de..ebc74be937ef06 100644
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json
@@ -1,6 +1,7 @@
{
"name": "kbn_tp_run_pipeline",
"version": "1.0.0",
+ "main": "target/test/interpreter_functional/plugins/kbn_tp_run_pipeline",
"kibana": {
"version": "kibana",
"templateVersion": "1.0.0"
@@ -10,5 +11,13 @@
"@elastic/eui": "22.3.1",
"react": "^16.12.0",
"react-dom": "^16.12.0"
+ },
+ "scripts": {
+ "kbn": "node ../../../../scripts/kbn.js",
+ "build": "rm -rf './target' && tsc"
+ },
+ "devDependencies": {
+ "@kbn/plugin-helpers": "9.0.2",
+ "typescript": "3.7.2"
}
}
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/app.tsx b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/app.tsx
similarity index 100%
rename from test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/app.tsx
rename to test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/app.tsx
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx
similarity index 99%
rename from test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx
rename to test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx
index a50248a5b6fa32..ace2af2b4f0cfe 100644
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx
@@ -21,7 +21,7 @@ import React from 'react';
import { EuiPage, EuiPageBody, EuiPageContent, EuiPageContentHeader } from '@elastic/eui';
import { first } from 'rxjs/operators';
import { IInterpreterRenderHandlers, ExpressionValue } from 'src/plugins/expressions';
-import { RequestAdapter, DataAdapter } from '../../../../../../../../src/plugins/inspector';
+import { RequestAdapter, DataAdapter } from '../../../../../../../src/plugins/inspector';
import { Adapters, ExpressionRenderHandler } from '../../types';
import { getExpressions } from '../../services';
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/index.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/index.ts
index c4cc7175d61570..d7a764b581c01d 100644
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/index.ts
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/index.ts
@@ -17,4 +17,12 @@
* under the License.
*/
-export * from './np_ready';
+import { PluginInitializer, PluginInitializerContext } from 'src/core/public';
+import { Plugin, StartDeps } from './plugin';
+export { StartDeps };
+
+export const plugin: PluginInitializer = (
+ initializerContext: PluginInitializerContext
+) => {
+ return new Plugin(initializerContext);
+};
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/legacy.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/legacy.ts
deleted file mode 100644
index a7cd313038d69c..00000000000000
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/legacy.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { PluginInitializerContext } from 'src/core/public';
-import { npSetup, npStart } from 'ui/new_platform';
-
-import { plugin } from './np_ready';
-
-// This is required so some default styles and required scripts/Angular modules are loaded,
-// or the timezone setting is correctly applied.
-import 'ui/autoload/all';
-// Used to run esaggs queries
-import 'uiExports/fieldFormats';
-import 'uiExports/search';
-// Used for kibana_context function
-
-import 'uiExports/savedObjectTypes';
-import 'uiExports/interpreter';
-
-const pluginInstance = plugin({} as PluginInitializerContext);
-
-export const setup = pluginInstance.setup(npSetup.core, npSetup.plugins);
-export const start = pluginInstance.start(npStart.core, npStart.plugins);
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/index.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/index.ts
deleted file mode 100644
index d7a764b581c01d..00000000000000
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/index.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { PluginInitializer, PluginInitializerContext } from 'src/core/public';
-import { Plugin, StartDeps } from './plugin';
-export { StartDeps };
-
-export const plugin: PluginInitializer = (
- initializerContext: PluginInitializerContext
-) => {
- return new Plugin(initializerContext);
-};
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/plugin.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/plugin.ts
similarity index 100%
rename from test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/plugin.ts
rename to test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/plugin.ts
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/services.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/services.ts
similarity index 91%
rename from test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/services.ts
rename to test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/services.ts
index a700727d87299d..4972911d5894f0 100644
--- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/services.ts
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/services.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { createGetterSetter } from '../../../../../../src/plugins/kibana_utils/public';
+import { createGetterSetter } from '../../../../../src/plugins/kibana_utils/public';
import { ExpressionsStart } from './types';
export const [getExpressions, setExpressions] = createGetterSetter('Expressions');
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/types.ts
similarity index 100%
rename from test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts
rename to test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/types.ts
diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/tsconfig.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/tsconfig.json
new file mode 100644
index 00000000000000..5fcaeafbb0d852
--- /dev/null
+++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./target",
+ "skipLibCheck": true
+ },
+ "include": [
+ "index.ts",
+ "public/**/*.ts",
+ "public/**/*.tsx",
+ "../../../../typings/**/*",
+ ],
+ "exclude": []
+}
diff --git a/test/interpreter_functional/test_suites/run_pipeline/basic.ts b/test/interpreter_functional/test_suites/run_pipeline/basic.ts
index a2172dd2da1ba3..51ad789143c541 100644
--- a/test/interpreter_functional/test_suites/run_pipeline/basic.ts
+++ b/test/interpreter_functional/test_suites/run_pipeline/basic.ts
@@ -113,10 +113,11 @@ export default function({
await expectExpression('partial_test_2', metricExpr, context).toMatchSnapshot()
).toMatchScreenshot();
- const regionMapExpr = `regionmap visConfig='{"metric":{"accessor":1,"format":{"id":"number"}},"bucket":{"accessor":0}}'`;
- await (
- await expectExpression('partial_test_3', regionMapExpr, context).toMatchSnapshot()
- ).toMatchScreenshot();
+ // TODO: should be uncommented when the region map is migrated to the new platform
+ // const regionMapExpr = `regionmap visConfig='{"metric":{"accessor":1,"format":{"id":"number"}},"bucket":{"accessor":0}}'`;
+ // await (
+ // await expectExpression('partial_test_3', regionMapExpr, context).toMatchSnapshot()
+ // ).toMatchScreenshot();
});
});
});
diff --git a/test/interpreter_functional/test_suites/run_pipeline/helpers.ts b/test/interpreter_functional/test_suites/run_pipeline/helpers.ts
index 00693845bb2662..2486fb0e1fbd04 100644
--- a/test/interpreter_functional/test_suites/run_pipeline/helpers.ts
+++ b/test/interpreter_functional/test_suites/run_pipeline/helpers.ts
@@ -21,6 +21,17 @@ import expect from '@kbn/expect';
import { ExpressionValue } from 'src/plugins/expressions';
import { FtrProviderContext } from '../../../functional/ftr_provider_context';
+declare global {
+ interface Window {
+ runPipeline: (
+ expressions: string,
+ context?: ExpressionValue,
+ initialContext?: ExpressionValue
+ ) => any;
+ renderPipelineResponse: (context?: ExpressionValue) => Promise;
+ }
+}
+
export type ExpressionResult = any;
export type ExpectExpression = (
@@ -165,7 +176,7 @@ export function expectExpressionProvider({
log.debug('starting to render');
const result = await browser.executeAsync(
(_context: ExpressionResult, done: (renderResult: any) => void) =>
- window.renderPipelineResponse(_context).then(renderResult => {
+ window.renderPipelineResponse(_context).then((renderResult: any) => {
done(renderResult);
return renderResult;
}),
diff --git a/test/plugin_functional/plugins/core_provider_plugin/index.ts b/test/plugin_functional/plugins/core_provider_plugin/index.ts
deleted file mode 100644
index 01f3a67c6b5541..00000000000000
--- a/test/plugin_functional/plugins/core_provider_plugin/index.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { resolve } from 'path';
-import { Legacy } from '../../../../kibana';
-
-// eslint-disable-next-line import/no-default-export
-export default function CoreProviderPlugin(kibana: any) {
- const config: Legacy.PluginSpecOptions = {
- id: 'core-provider',
- require: [],
- publicDir: resolve(__dirname, 'public'),
- init: (server: Legacy.Server) => ({}),
- uiExports: {
- hacks: [resolve(__dirname, 'public/index')],
- },
- };
-
- return new kibana.Plugin(config);
-}
diff --git a/test/plugin_functional/plugins/core_provider_plugin/kibana.json b/test/plugin_functional/plugins/core_provider_plugin/kibana.json
new file mode 100644
index 00000000000000..1d5c5824d6b970
--- /dev/null
+++ b/test/plugin_functional/plugins/core_provider_plugin/kibana.json
@@ -0,0 +1,8 @@
+{
+ "id": "core_provider_plugin",
+ "version": "0.0.1",
+ "kibanaVersion": "kibana",
+ "optionalPlugins": ["core_plugin_a", "core_plugin_b", "licensing"],
+ "server": false,
+ "ui": true
+}
diff --git a/test/plugin_functional/plugins/core_provider_plugin/public/index.ts b/test/plugin_functional/plugins/core_provider_plugin/public/index.ts
index c74928203db56f..2f271fe5ef65b3 100644
--- a/test/plugin_functional/plugins/core_provider_plugin/public/index.ts
+++ b/test/plugin_functional/plugins/core_provider_plugin/public/index.ts
@@ -16,13 +16,31 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { npSetup, npStart } from 'ui/new_platform';
+import { Plugin, CoreSetup, CoreStart } from 'kibana/public';
import '../types';
-window.__coreProvider = {
- setup: npSetup,
- start: npStart,
- testUtils: {
- delay: (ms: number) => new Promise(res => setTimeout(res, ms)),
- },
-};
+export const plugin = () => new CoreProviderPlugin();
+
+class CoreProviderPlugin implements Plugin {
+ private setupDeps?: { core: CoreSetup; plugins: Record };
+ public setup(core: CoreSetup, plugins: Record) {
+ this.setupDeps = {
+ core,
+ plugins,
+ };
+ }
+
+ public start(core: CoreStart, plugins: Record) {
+ window.__coreProvider = {
+ setup: this.setupDeps!,
+ start: {
+ core,
+ plugins,
+ },
+ testUtils: {
+ delay: (ms: number) => new Promise(res => setTimeout(res, ms)),
+ },
+ };
+ }
+ public stop() {}
+}
diff --git a/test/plugin_functional/plugins/core_provider_plugin/tsconfig.json b/test/plugin_functional/plugins/core_provider_plugin/tsconfig.json
index c29959197958df..baedb5f2f621bf 100644
--- a/test/plugin_functional/plugins/core_provider_plugin/tsconfig.json
+++ b/test/plugin_functional/plugins/core_provider_plugin/tsconfig.json
@@ -8,7 +8,7 @@
"index.ts",
"types.ts",
"public/**/*.ts",
- "../../../../typings/**/*",
+ "../../../../typings/**/*"
],
"exclude": []
}
diff --git a/test/plugin_functional/plugins/core_provider_plugin/types.ts b/test/plugin_functional/plugins/core_provider_plugin/types.ts
index bf19578c37baab..cae3b604ecd959 100644
--- a/test/plugin_functional/plugins/core_provider_plugin/types.ts
+++ b/test/plugin_functional/plugins/core_provider_plugin/types.ts
@@ -16,17 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { LegacyCoreSetup, LegacyCoreStart } from 'kibana/public';
+import { CoreSetup, CoreStart } from 'kibana/public';
declare global {
interface Window {
__coreProvider: {
setup: {
- core: LegacyCoreSetup;
+ core: CoreSetup;
plugins: Record;
};
start: {
- core: LegacyCoreStart;
+ core: CoreStart;
plugins: Record;
};
testUtils: {
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/index.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/index.ts
deleted file mode 100644
index 99f54277be5d2e..00000000000000
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/index.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { Legacy } from 'kibana';
-
-// eslint-disable-next-line import/no-default-export
-export default function(kibana: any) {
- return new kibana.Plugin({
- require: ['kibana'],
- uiExports: {
- app: {
- title: 'Embeddable Explorer',
- order: 1,
- main: 'plugins/kbn_tp_embeddable_explorer/np_ready/public/legacy',
- },
- },
- init(server: Legacy.Server) {
- server.injectUiAppVars('kbn_tp_embeddable_explorer', async () =>
- server.getInjectedUiAppVars('kibana')
- );
- },
- });
-}
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/kibana.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/kibana.json
new file mode 100644
index 00000000000000..6c8d51ccb8651f
--- /dev/null
+++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/kibana.json
@@ -0,0 +1,16 @@
+{
+ "id": "kbn_tp_embeddable_explorer",
+ "version": "0.0.1",
+ "kibanaVersion": "kibana",
+ "requiredPlugins": [
+ "visTypeMarkdown",
+ "visTypeVislib",
+ "data",
+ "embeddable",
+ "uiActions",
+ "inspector",
+ "discover"
+ ],
+ "server": false,
+ "ui": true
+}
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/app.tsx
similarity index 100%
rename from test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx
rename to test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/app.tsx
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_container_example.tsx
similarity index 98%
rename from test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx
rename to test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_container_example.tsx
index 16c2840d6a32e8..e56b82378ddf7c 100644
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx
+++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_container_example.tsx
@@ -24,7 +24,7 @@ import {
DASHBOARD_CONTAINER_TYPE,
DashboardContainer,
DashboardContainerInput,
-} from '../../../../../../../../src/plugins/dashboard/public';
+} from '../../../../../../src/plugins/dashboard/public';
import { dashboardInput } from './dashboard_input';
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_input.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_input.ts
similarity index 96%
rename from test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_input.ts
rename to test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_input.ts
index 37ef8cad948cb7..6f4e1f052f5e09 100644
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_input.ts
+++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/dashboard_input.ts
@@ -18,7 +18,7 @@
*/
import { ViewMode, CONTACT_CARD_EMBEDDABLE, HELLO_WORLD_EMBEDDABLE } from '../embeddable_api';
-import { DashboardContainerInput } from '../../../../../../../../src/plugins/dashboard/public';
+import { DashboardContainerInput } from '../../../../../../src/plugins/dashboard/public';
export const dashboardInput: DashboardContainerInput = {
panels: {
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/index.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/index.ts
similarity index 100%
rename from test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/index.ts
rename to test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/app/index.ts
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/embeddable_api.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/embeddable_api.ts
similarity index 74%
rename from test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/embeddable_api.ts
rename to test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/embeddable_api.ts
index dd25bebf899200..9f6597fefa1e40 100644
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/embeddable_api.ts
+++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/embeddable_api.ts
@@ -17,6 +17,9 @@
* under the License.
*/
-export * from '../../../../../../../src/plugins/embeddable/public';
-export * from '../../../../../../../src/plugins/embeddable/public/lib/test_samples';
-export { HELLO_WORLD_EMBEDDABLE } from '../../../../../../../examples/embeddable_examples/public';
+export * from '../../../../../src/plugins/embeddable/public';
+export * from '../../../../../src/plugins/embeddable/public/lib/test_samples';
+export {
+ HELLO_WORLD_EMBEDDABLE,
+ HelloWorldEmbeddableFactory,
+} from '../../../../../examples/embeddable_examples/public';
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/index.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/index.ts
similarity index 100%
rename from test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/index.ts
rename to test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/index.ts
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/initialize.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/initialize.ts
deleted file mode 100644
index a4bc3cf17026c3..00000000000000
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/initialize.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import './np_ready/public/legacy';
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/kibana.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/kibana.json
deleted file mode 100644
index d0d0784eae8d33..00000000000000
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/kibana.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "id": "kbn_tp_embeddable_explorer",
- "version": "kibana",
- "requiredPlugins": [
- "embeddable",
- "inspector"
- ],
- "server": false,
- "ui": true
-}
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/index.html b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/index.html
deleted file mode 100644
index a242631e1638f3..00000000000000
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/index.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
ANGULAR STUFF!
-
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/legacy.ts b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/legacy.ts
deleted file mode 100644
index 6d125bc3002e00..00000000000000
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/legacy.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/* eslint-disable @kbn/eslint/no-restricted-paths */
-import 'ui/autoload/all';
-
-import 'uiExports/interpreter';
-import 'uiExports/embeddableFactories';
-import 'uiExports/embeddableActions';
-import 'uiExports/contextMenuActions';
-import 'uiExports/devTools';
-import 'uiExports/docViews';
-import 'uiExports/embeddableActions';
-import 'uiExports/fieldFormatEditors';
-import 'uiExports/fieldFormats';
-import 'uiExports/home';
-import 'uiExports/indexManagement';
-import 'uiExports/inspectorViews';
-import 'uiExports/savedObjectTypes';
-import 'uiExports/search';
-import 'uiExports/shareContextMenuExtensions';
-import 'uiExports/visTypes';
-import 'uiExports/visualize';
-
-import { npSetup, npStart } from 'ui/new_platform';
-import { ExitFullScreenButton } from 'ui/exit_full_screen';
-import uiRoutes from 'ui/routes';
-// @ts-ignore
-import { uiModules } from 'ui/modules';
-/* eslint-enable @kbn/eslint/no-restricted-paths */
-
-import template from './index.html';
-
-import { plugin } from '.';
-
-const pluginInstance = plugin({} as any);
-
-export const setup = pluginInstance.setup(npSetup.core, {
- embeddable: npSetup.plugins.embeddable,
- inspector: npSetup.plugins.inspector,
- __LEGACY: {
- ExitFullScreenButton,
- },
-});
-
-let rendered = false;
-const onRenderCompleteListeners: Array<() => void> = [];
-
-uiRoutes.enable();
-uiRoutes.defaults(/\embeddable_explorer/, {});
-uiRoutes.when('/', {
- template,
- controller($scope) {
- $scope.$$postDigest(() => {
- rendered = true;
- onRenderCompleteListeners.forEach(listener => listener());
- });
- },
-});
-
-export const start = pluginInstance.start(npStart.core, {
- embeddable: npStart.plugins.embeddable,
- inspector: npStart.plugins.inspector,
- uiActions: npStart.plugins.uiActions,
- __LEGACY: {
- ExitFullScreenButton,
- onRenderComplete: (renderCompleteListener: () => void) => {
- if (rendered) {
- renderCompleteListener();
- } else {
- onRenderCompleteListeners.push(renderCompleteListener);
- }
- },
- },
-});
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx
deleted file mode 100644
index b47e84216dd16e..00000000000000
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { CoreSetup, CoreStart, Plugin } from 'src/core/public';
-import { UiActionsStart } from '../../../../../../../src/plugins/ui_actions/public';
-import { createHelloWorldAction } from '../../../../../../../src/plugins/ui_actions/public/tests/test_samples';
-
-import {
- Start as InspectorStartContract,
- Setup as InspectorSetupContract,
-} from '../../../../../../../src/plugins/inspector/public';
-
-import { CONTEXT_MENU_TRIGGER } from './embeddable_api';
-
-const REACT_ROOT_ID = 'embeddableExplorerRoot';
-
-import { SayHelloAction, createSendMessageAction } from './embeddable_api';
-import { App } from './app';
-import {
- EmbeddableStart,
- EmbeddableSetup,
-} from '.../../../../../../../src/plugins/embeddable/public';
-
-export interface SetupDependencies {
- embeddable: EmbeddableSetup;
- inspector: InspectorSetupContract;
- __LEGACY: {
- ExitFullScreenButton: React.ComponentType;
- };
-}
-
-interface StartDependencies {
- embeddable: EmbeddableStart;
- uiActions: UiActionsStart;
- inspector: InspectorStartContract;
- __LEGACY: {
- ExitFullScreenButton: React.ComponentType;
- onRenderComplete: (onRenderComplete: () => void) => void;
- };
-}
-
-export type EmbeddableExplorerSetup = void;
-export type EmbeddableExplorerStart = void;
-
-export class EmbeddableExplorerPublicPlugin
- implements
- Plugin {
- public setup(core: CoreSetup, setupDeps: SetupDependencies): EmbeddableExplorerSetup {}
-
- public start(core: CoreStart, plugins: StartDependencies): EmbeddableExplorerStart {
- const helloWorldAction = createHelloWorldAction(core.overlays);
- const sayHelloAction = new SayHelloAction(alert);
- const sendMessageAction = createSendMessageAction(core.overlays);
-
- plugins.uiActions.registerAction(sayHelloAction);
- plugins.uiActions.registerAction(sendMessageAction);
-
- plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, helloWorldAction);
-
- plugins.__LEGACY.onRenderComplete(() => {
- const root = document.getElementById(REACT_ROOT_ID);
- ReactDOM.render(, root);
- });
- }
-
- public stop() {}
-}
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/plugin.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/plugin.tsx
new file mode 100644
index 00000000000000..f99d89ca630bbc
--- /dev/null
+++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/plugin.tsx
@@ -0,0 +1,98 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
+import { CoreSetup, Plugin, AppMountParameters } from 'kibana/public';
+import { UiActionsStart, UiActionsSetup } from '../../../../../src/plugins/ui_actions/public';
+import { createHelloWorldAction } from '../../../../../src/plugins/ui_actions/public/tests/test_samples';
+
+import {
+ Start as InspectorStartContract,
+ Setup as InspectorSetupContract,
+} from '../../../../../src/plugins/inspector/public';
+
+import { App } from './app';
+import {
+ CONTEXT_MENU_TRIGGER,
+ CONTACT_CARD_EMBEDDABLE,
+ HELLO_WORLD_EMBEDDABLE,
+ HelloWorldEmbeddableFactory,
+ ContactCardEmbeddableFactory,
+ SayHelloAction,
+ createSendMessageAction,
+} from './embeddable_api';
+import {
+ EmbeddableStart,
+ EmbeddableSetup,
+} from '.../../../../../../../src/plugins/embeddable/public';
+
+export interface SetupDependencies {
+ embeddable: EmbeddableSetup;
+ inspector: InspectorSetupContract;
+ uiActions: UiActionsSetup;
+}
+
+interface StartDependencies {
+ embeddable: EmbeddableStart;
+ uiActions: UiActionsStart;
+ inspector: InspectorStartContract;
+}
+
+export type EmbeddableExplorerSetup = void;
+export type EmbeddableExplorerStart = void;
+
+export class EmbeddableExplorerPublicPlugin
+ implements
+ Plugin {
+ public setup(core: CoreSetup, setupDeps: SetupDependencies): EmbeddableExplorerSetup {
+ const helloWorldAction = createHelloWorldAction({} as any);
+ const sayHelloAction = new SayHelloAction(alert);
+ const sendMessageAction = createSendMessageAction({} as any);
+
+ setupDeps.uiActions.registerAction(helloWorldAction);
+ setupDeps.uiActions.registerAction(sayHelloAction);
+ setupDeps.uiActions.registerAction(sendMessageAction);
+
+ setupDeps.uiActions.attachAction(CONTEXT_MENU_TRIGGER, helloWorldAction.id);
+
+ setupDeps.embeddable.registerEmbeddableFactory(
+ HELLO_WORLD_EMBEDDABLE,
+ new HelloWorldEmbeddableFactory()
+ );
+
+ setupDeps.embeddable.registerEmbeddableFactory(
+ CONTACT_CARD_EMBEDDABLE,
+ new ContactCardEmbeddableFactory((() => null) as any, {} as any)
+ );
+
+ core.application.register({
+ id: 'EmbeddableExplorer',
+ title: 'Embeddable Explorer',
+ async mount(params: AppMountParameters) {
+ const startPlugins = (await core.getStartServices())[1] as StartDependencies;
+ render(, params.element);
+
+ return () => unmountComponentAtNode(params.element);
+ },
+ });
+ }
+
+ public start() {}
+ public stop() {}
+}
diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts
index 097833750bc800..b8e26b8e6ffcbd 100644
--- a/test/plugin_functional/test_suites/core_plugins/rendering.ts
+++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts
@@ -40,8 +40,8 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
const find = getService('find');
const testSubjects = getService('testSubjects');
- const navigateTo = (path: string) =>
- browser.navigateTo(`${PageObjects.common.getHostPort()}${path}`);
+ const navigateTo = async (path: string) =>
+ await browser.navigateTo(`${PageObjects.common.getHostPort()}${path}`);
const navigateToApp = async (title: string) => {
await appsMenu.clickLink(title);
return browser.execute(() => {
diff --git a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts
index 8ddd0ff96ba8f5..b2393443989f94 100644
--- a/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts
+++ b/test/plugin_functional/test_suites/core_plugins/ui_plugins.ts
@@ -47,14 +47,6 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider
await PageObjects.common.navigateToApp('settings');
});
- it('to injectedMetadata service', async () => {
- expect(
- await browser.execute(() => {
- return window.__coreProvider.setup.core.injectedMetadata.getKibanaBuildNumber();
- })
- ).to.be.a('number');
- });
-
it('to start services via coreSetup.getStartServices', async () => {
expect(
await browser.executeAsync(async cb => {
diff --git a/test/scripts/jenkins_build_kibana.sh b/test/scripts/jenkins_build_kibana.sh
index 1f6e09fad19e95..e3f46e7a6ada41 100755
--- a/test/scripts/jenkins_build_kibana.sh
+++ b/test/scripts/jenkins_build_kibana.sh
@@ -6,6 +6,7 @@ echo " -> building kibana platform plugins"
node scripts/build_kibana_platform_plugins \
--oss \
--scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \
+ --scan-dir "$KIBANA_DIR/test/interpreter_functional/plugins" \
--verbose;
# doesn't persist, also set in kibanaPipeline.groovy
diff --git a/test/scripts/jenkins_xpack_build_kibana.sh b/test/scripts/jenkins_xpack_build_kibana.sh
index 8dc41639fa9468..c962b962b1e5e6 100755
--- a/test/scripts/jenkins_xpack_build_kibana.sh
+++ b/test/scripts/jenkins_xpack_build_kibana.sh
@@ -5,6 +5,7 @@ source src/dev/ci_setup/setup_env.sh
echo " -> building kibana platform plugins"
node scripts/build_kibana_platform_plugins \
+ --scan-dir "$KIBANA_DIR/test/plugin_functional/plugins" \
--scan-dir "$XPACK_DIR/test/plugin_functional/plugins" \
--scan-dir "$XPACK_DIR/test/functional_with_es_ssl/fixtures/plugins" \
--scan-dir "$XPACK_DIR/test/alerting_api_integration/plugins" \
diff --git a/test/tsconfig.json b/test/tsconfig.json
index 5a3716e620fed8..a270144bd49fea 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -19,6 +19,7 @@
"typings/**/*"
],
"exclude": [
- "plugin_functional/plugins/**/*"
+ "plugin_functional/plugins/**/*",
+ "interpreter_functional/plugins/**/*"
]
}
diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx
index 867ead688d23d0..4d14226777a0b8 100644
--- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx
+++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx
@@ -32,9 +32,9 @@ export interface ActionWizardProps {
/**
* Action factory selected changed
- * null - means user click "change" and removed action factory selection
+ * empty - means user click "change" and removed action factory selection
*/
- onActionFactoryChange: (actionFactory: ActionFactory | null) => void;
+ onActionFactoryChange: (actionFactory?: ActionFactory) => void;
/**
* current config for currently selected action factory
@@ -71,7 +71,7 @@ export const ActionWizard: React.FC = ({
actionFactory={currentActionFactory}
showDeselect={actionFactories.length > 1}
onDeselect={() => {
- onActionFactoryChange(null);
+ onActionFactoryChange(undefined);
}}
context={context}
config={config}
diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx
index c3e749f163c949..692e86b53f09d7 100644
--- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx
+++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx
@@ -167,7 +167,7 @@ export function Demo({ actionFactories }: { actionFactories: Array({});
- function changeActionFactory(newActionFactory: ActionFactory | null) {
+ function changeActionFactory(newActionFactory?: ActionFactory) {
if (!newActionFactory) {
// removing action factory
return setState({});
diff --git a/x-pack/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx b/x-pack/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.test.tsx
similarity index 94%
rename from x-pack/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx
rename to x-pack/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.test.tsx
index cb983cdffa0282..1e3a73acfab57f 100644
--- a/x-pack/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx
+++ b/x-pack/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.test.tsx
@@ -5,8 +5,8 @@
*/
import { Location } from 'history';
-import { BreadcrumbRoute, getBreadcrumbs } from '../ProvideBreadcrumbs';
-import { RouteName } from '../route_config/route_names';
+import { BreadcrumbRoute, getBreadcrumbs } from './ProvideBreadcrumbs';
+import { RouteName } from './route_config/route_names';
describe('getBreadcrumbs', () => {
const getTestRoutes = (): BreadcrumbRoute[] => [
diff --git a/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx b/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx
index 8960af0f21fd29..b4a556c497c1bb 100644
--- a/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx
+++ b/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx
@@ -30,10 +30,18 @@ function getTitleFromBreadCrumbs(breadcrumbs: Breadcrumb[]) {
class UpdateBreadcrumbsComponent extends React.Component {
public updateHeaderBreadcrumbs() {
- const breadcrumbs = this.props.breadcrumbs.map(({ value, match }) => ({
- text: value,
- href: getAPMHref(match.url, this.props.location.search)
- }));
+ const breadcrumbs = this.props.breadcrumbs.map(
+ ({ value, match }, index) => {
+ const isLastBreadcrumbItem =
+ index === this.props.breadcrumbs.length - 1;
+ return {
+ text: value,
+ href: isLastBreadcrumbItem
+ ? undefined // makes the breadcrumb item not clickable
+ : getAPMHref(match.url, this.props.location.search)
+ };
+ }
+ );
document.title = getTitleFromBreadCrumbs(this.props.breadcrumbs);
this.props.core.chrome.setBreadcrumbs(breadcrumbs);
diff --git a/x-pack/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap b/x-pack/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap
index 51bdb63874e633..e7f6cba59318a5 100644
--- a/x-pack/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap
+++ b/x-pack/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap
@@ -15,7 +15,7 @@ Array [
"text": "opbeans-node",
},
Object {
- "href": "#/services/opbeans-node/errors?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=myKuery",
+ "href": undefined,
"text": "Errors",
},
]
@@ -40,7 +40,7 @@ Array [
"text": "Errors",
},
Object {
- "href": "#/services/opbeans-node/errors/myGroupId?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=myKuery",
+ "href": undefined,
"text": "myGroupId",
},
]
@@ -61,7 +61,7 @@ Array [
"text": "opbeans-node",
},
Object {
- "href": "#/services/opbeans-node/transactions?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=myKuery",
+ "href": undefined,
"text": "Transactions",
},
]
@@ -86,7 +86,7 @@ Array [
"text": "Transactions",
},
Object {
- "href": "#/services/opbeans-node/transactions/view?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=myKuery",
+ "href": undefined,
"text": "my-transaction-name",
},
]
@@ -95,7 +95,7 @@ Array [
exports[`UpdateBreadcrumbs Homepage 1`] = `
Array [
Object {
- "href": "#/?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=myKuery",
+ "href": undefined,
"text": "APM",
},
]
diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx
index 3a6f94b9758002..79a6370b4be46a 100644
--- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx
@@ -132,7 +132,10 @@ export function AgentConfigurationCreateEdit({
setPage('choose-settings-step')}
+ onClickNext={() => {
+ resetSettings();
+ setPage('choose-settings-step');
+ }}
/>
)}
diff --git a/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx b/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx
index 6749b41e81fc70..52c53f32ff09b4 100644
--- a/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx
+++ b/x-pack/plugins/drilldowns/public/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx
@@ -173,6 +173,42 @@ test('Create only mode', async () => {
expect(await mockDynamicActionManager.state.get().events.length).toBe(1);
});
+test('After switching between action factories state is restored', async () => {
+ const screen = render(
+
+ );
+ // wait for initial render. It is async because resolving compatible action factories is async
+ await wait(() => expect(screen.getAllByText(/Create/i).length).toBeGreaterThan(0));
+ fireEvent.change(screen.getByLabelText(/name/i), {
+ target: { value: 'test' },
+ });
+ fireEvent.click(screen.getByText(/Go to URL/i));
+ fireEvent.change(screen.getByLabelText(/url/i), {
+ target: { value: 'https://elastic.co' },
+ });
+
+ // change to dashboard
+ fireEvent.click(screen.getByText(/change/i));
+ fireEvent.click(screen.getByText(/Go to Dashboard/i));
+
+ // change back to url
+ fireEvent.click(screen.getByText(/change/i));
+ fireEvent.click(screen.getByText(/Go to URL/i));
+
+ expect(screen.getByLabelText(/url/i)).toHaveValue('https://elastic.co');
+ expect(screen.getByLabelText(/name/i)).toHaveValue('test');
+
+ fireEvent.click(screen.getAllByText(/Create Drilldown/i)[1]);
+ await wait(() => expect(notifications.toasts.addSuccess).toBeCalled());
+ expect(await (mockDynamicActionManager.state.get().events[0].action.config as any).url).toBe(
+ 'https://elastic.co'
+ );
+});
+
test.todo("Error when can't fetch drilldown list");
test("Error when can't save drilldown changes", async () => {
diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx
index 8541aae06ff0c7..1f775a5ff103f1 100644
--- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx
+++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx
@@ -41,6 +41,72 @@ export interface FlyoutDrilldownWizardProps void;
+ setActionConfig: (actionConfig: object) => void;
+ setActionFactory: (actionFactory?: ActionFactory) => void;
+ }
+] {
+ const [wizardConfig, setWizardConfig] = useState(
+ () =>
+ initialDrilldownWizardConfig ?? {
+ name: '',
+ }
+ );
+ const [actionConfigCache, setActionConfigCache] = useState>(
+ initialDrilldownWizardConfig?.actionFactory
+ ? {
+ [initialDrilldownWizardConfig.actionFactory
+ .id]: initialDrilldownWizardConfig.actionConfig!,
+ }
+ : {}
+ );
+
+ return [
+ wizardConfig,
+ {
+ setName: (name: string) => {
+ setWizardConfig({
+ ...wizardConfig,
+ name,
+ });
+ },
+ setActionConfig: (actionConfig: object) => {
+ setWizardConfig({
+ ...wizardConfig,
+ actionConfig,
+ });
+ },
+ setActionFactory: (actionFactory?: ActionFactory) => {
+ if (actionFactory) {
+ setWizardConfig({
+ ...wizardConfig,
+ actionFactory,
+ actionConfig: actionConfigCache[actionFactory.id] ?? actionFactory.createConfig(),
+ });
+ } else {
+ if (wizardConfig.actionFactory?.id) {
+ setActionConfigCache({
+ ...actionConfigCache,
+ [wizardConfig.actionFactory.id]: wizardConfig.actionConfig!,
+ });
+ }
+
+ setWizardConfig({
+ ...wizardConfig,
+ actionFactory: undefined,
+ actionConfig: undefined,
+ });
+ }
+ },
+ },
+ ];
+}
+
export function FlyoutDrilldownWizard({
onClose,
onBack,
@@ -53,11 +119,8 @@ export function FlyoutDrilldownWizard) {
- const [wizardConfig, setWizardConfig] = useState(
- () =>
- initialDrilldownWizardConfig ?? {
- name: '',
- }
+ const [wizardConfig, { setActionFactory, setActionConfig, setName }] = useWizardConfigState(
+ initialDrilldownWizardConfig
);
const isActionValid = (
@@ -95,35 +158,11 @@ export function FlyoutDrilldownWizard {
- setWizardConfig({
- ...wizardConfig,
- name: newName,
- });
- }}
+ onNameChange={setName}
actionConfig={wizardConfig.actionConfig}
- onActionConfigChange={newActionConfig => {
- setWizardConfig({
- ...wizardConfig,
- actionConfig: newActionConfig,
- });
- }}
+ onActionConfigChange={setActionConfig}
currentActionFactory={wizardConfig.actionFactory}
- onActionFactoryChange={actionFactory => {
- if (!actionFactory) {
- setWizardConfig({
- ...wizardConfig,
- actionFactory: undefined,
- actionConfig: undefined,
- });
- } else {
- setWizardConfig({
- ...wizardConfig,
- actionFactory,
- actionConfig: actionFactory.createConfig(),
- });
- }
- }}
+ onActionFactoryChange={setActionFactory}
actionFactories={drilldownActionFactories}
actionFactoryContext={actionFactoryContext!}
/>
diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx
index 93b3710bf6cc66..3bed81a9719216 100644
--- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx
+++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx
@@ -19,7 +19,7 @@ export interface FormDrilldownWizardProps {
onNameChange?: (name: string) => void;
currentActionFactory?: ActionFactory;
- onActionFactoryChange?: (actionFactory: ActionFactory | null) => void;
+ onActionFactoryChange?: (actionFactory?: ActionFactory) => void;
actionFactoryContext: object;
actionConfig?: object;
diff --git a/x-pack/plugins/graph/public/angular/templates/index.html b/x-pack/plugins/graph/public/angular/templates/index.html
index 8555658596179a..939d92518e271c 100644
--- a/x-pack/plugins/graph/public/angular/templates/index.html
+++ b/x-pack/plugins/graph/public/angular/templates/index.html
@@ -122,8 +122,8 @@
diff --git a/x-pack/plugins/index_management/__jest__/components/index_table.test.js b/x-pack/plugins/index_management/__jest__/components/index_table.test.js
index 15c3ef0b845624..84fbc04aa5a313 100644
--- a/x-pack/plugins/index_management/__jest__/components/index_table.test.js
+++ b/x-pack/plugins/index_management/__jest__/components/index_table.test.js
@@ -8,6 +8,15 @@ import React from 'react';
import axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import { MemoryRouter } from 'react-router-dom';
+
+/**
+ * The below import is required to avoid a console error warn from brace package
+ * console.warn ../node_modules/brace/index.js:3999
+ Could not load worker ReferenceError: Worker is not defined
+ at createWorker (//node_modules/brace/index.js:17992:5)
+ */
+import * as stubWebWorker from '../../../../test_utils/stub_web_worker'; // eslint-disable-line no-unused-vars
+
import { AppWithoutRouter } from '../../public/application/app';
import { AppContextProvider } from '../../public/application/app_context';
import { Provider } from 'react-redux';
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/index.ts
new file mode 100644
index 00000000000000..eac68770d3de23
--- /dev/null
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { defaultShapeParameters } from './shape_datatype.test';
+export { defaultTextParameters } from './text_datatype.test';
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/shape_datatype.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/shape_datatype.test.tsx
new file mode 100644
index 00000000000000..19bf6973472ff3
--- /dev/null
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/shape_datatype.test.tsx
@@ -0,0 +1,81 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { act } from 'react-dom/test-utils';
+
+import { componentHelpers, MappingsEditorTestBed } from '../helpers';
+
+const { setup, getMappingsEditorDataFactory } = componentHelpers.mappingsEditor;
+const onChangeHandler = jest.fn();
+const getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler);
+
+// Parameters automatically added to the shape datatype when saved (with the default values)
+export const defaultShapeParameters = {
+ type: 'shape',
+ coerce: false,
+ ignore_malformed: false,
+ ignore_z_value: true,
+};
+
+describe('Mappings editor: shape datatype', () => {
+ let testBed: MappingsEditorTestBed;
+
+ /**
+ * Variable to store the mappings data forwarded to the consumer component
+ */
+ let data: any;
+
+ test('initial view and default parameters values', async () => {
+ const defaultMappings = {
+ _meta: {},
+ _source: {},
+ properties: {
+ myField: {
+ type: 'shape',
+ },
+ },
+ };
+
+ const updatedMappings = { ...defaultMappings };
+
+ await act(async () => {
+ testBed = await setup({ value: defaultMappings, onChange: onChangeHandler });
+ });
+
+ const {
+ exists,
+ waitFor,
+ waitForFn,
+ actions: { startEditField, updateFieldAndCloseFlyout },
+ } = testBed;
+
+ // Open the flyout to edit the field
+ await act(async () => {
+ startEditField('myField');
+ });
+
+ await waitFor('mappingsEditorFieldEdit');
+
+ // Save the field and close the flyout
+ await act(async () => {
+ await updateFieldAndCloseFlyout();
+ });
+
+ await waitForFn(
+ async () => exists('mappingsEditorFieldEdit') === false,
+ 'Error waiting for the details flyout to close'
+ );
+
+ // It should have the default parameters values added
+ updatedMappings.properties.myField = {
+ type: 'shape',
+ ...defaultShapeParameters,
+ };
+
+ ({ data } = await getMappingsEditorData());
+ expect(data).toEqual(updatedMappings);
+ });
+});
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx
new file mode 100644
index 00000000000000..2bfaa884a01329
--- /dev/null
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/text_datatype.test.tsx
@@ -0,0 +1,459 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { act } from 'react-dom/test-utils';
+
+import { componentHelpers, MappingsEditorTestBed } from '../helpers';
+import { getFieldConfig } from '../../../lib';
+
+const { setup, getMappingsEditorDataFactory } = componentHelpers.mappingsEditor;
+const onChangeHandler = jest.fn();
+const getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler);
+
+// Parameters automatically added to the text datatype when saved (with the default values)
+export const defaultTextParameters = {
+ type: 'text',
+ eager_global_ordinals: false,
+ fielddata: false,
+ index: true,
+ index_options: 'positions',
+ index_phrases: false,
+ norms: true,
+ store: false,
+};
+
+describe('Mappings editor: text datatype', () => {
+ let testBed: MappingsEditorTestBed;
+
+ /**
+ * Variable to store the mappings data forwarded to the consumer component
+ */
+ let data: any;
+
+ afterEach(() => {
+ onChangeHandler.mockReset();
+ });
+
+ test('initial view and default parameters values', async () => {
+ const defaultMappings = {
+ _meta: {},
+ _source: {},
+ properties: {
+ myField: {
+ type: 'text',
+ },
+ },
+ };
+
+ const updatedMappings = { ...defaultMappings };
+
+ await act(async () => {
+ testBed = await setup({ value: defaultMappings, onChange: onChangeHandler });
+ });
+
+ const {
+ exists,
+ waitFor,
+ waitForFn,
+ actions: { startEditField, getToggleValue, updateFieldAndCloseFlyout },
+ } = testBed;
+
+ // Open the flyout to edit the field
+ await act(async () => {
+ startEditField('myField');
+ });
+
+ await waitFor('mappingsEditorFieldEdit');
+
+ // It should have searchable ("index" param) active by default
+ const indexFieldConfig = getFieldConfig('index');
+ expect(getToggleValue('indexParameter.formRowToggle')).toBe(indexFieldConfig.defaultValue);
+
+ // Save the field and close the flyout
+ await act(async () => {
+ updateFieldAndCloseFlyout();
+ });
+
+ await waitForFn(
+ async () => exists('mappingsEditorFieldEdit') === false,
+ 'Error waiting for the details flyout to close'
+ );
+
+ // It should have the default parameters values added
+ updatedMappings.properties.myField = {
+ type: 'text',
+ ...defaultTextParameters,
+ };
+
+ ({ data } = await getMappingsEditorData());
+ expect(data).toEqual(updatedMappings);
+ }, 30000);
+
+ test('analyzer parameter: default values', async () => {
+ const defaultMappings = {
+ _meta: {},
+ _source: {},
+ properties: {
+ myField: {
+ type: 'text',
+ // Should have 2 dropdown selects:
+ // The first one set to 'language' and the second one set to 'french
+ search_quote_analyzer: 'french',
+ },
+ },
+ };
+
+ testBed = await setup({ value: defaultMappings, onChange: onChangeHandler });
+
+ const {
+ find,
+ exists,
+ waitFor,
+ waitForFn,
+ form: { selectCheckBox, setSelectValue },
+ actions: {
+ startEditField,
+ getCheckboxValue,
+ showAdvancedSettings,
+ updateFieldAndCloseFlyout,
+ },
+ } = testBed;
+ const fieldToEdit = 'myField';
+
+ // Start edit and immediately save to have all the default values
+ await act(async () => {
+ startEditField(fieldToEdit);
+ });
+ await waitFor('mappingsEditorFieldEdit');
+ await showAdvancedSettings();
+
+ await act(async () => {
+ updateFieldAndCloseFlyout();
+ });
+
+ await waitForFn(
+ async () => exists('mappingsEditorFieldEdit') === false,
+ 'Error waiting for the details flyout to close'
+ );
+
+ ({ data } = await getMappingsEditorData());
+
+ let updatedMappings: any = {
+ ...defaultMappings,
+ properties: {
+ myField: {
+ ...defaultMappings.properties.myField,
+ ...defaultTextParameters,
+ },
+ },
+ };
+ expect(data).toEqual(updatedMappings);
+
+ // Re-open the edit panel
+ await act(async () => {
+ startEditField('myField');
+ });
+ await waitFor('mappingsEditorFieldEdit');
+ await showAdvancedSettings();
+
+ // When no analyzer is defined, defaults to "Index default"
+ let indexAnalyzerValue = find('indexAnalyzer.select').props().value;
+ expect(indexAnalyzerValue).toEqual('index_default');
+
+ const searchQuoteAnalyzerSelects = find('searchQuoteAnalyzer.select');
+
+ expect(searchQuoteAnalyzerSelects.length).toBe(2);
+ expect(searchQuoteAnalyzerSelects.at(0).props().value).toBe('language');
+ expect(searchQuoteAnalyzerSelects.at(1).props().value).toBe(
+ defaultMappings.properties.myField.search_quote_analyzer
+ );
+
+ // When no "search_analyzer" is defined, the checkBox should be checked
+ let isUseSameAnalyzerForSearchChecked = getCheckboxValue(
+ 'useSameAnalyzerForSearchCheckBox.input'
+ );
+ expect(isUseSameAnalyzerForSearchChecked).toBe(true);
+
+ // And the search analyzer select should not exist
+ expect(exists('searchAnalyzer')).toBe(false);
+
+ // Uncheck the "Use same analyzer for search" checkbox and wait for the search analyzer select
+ await act(async () => {
+ selectCheckBox('useSameAnalyzerForSearchCheckBox.input', false);
+ });
+
+ await waitFor('searchAnalyzer');
+
+ let searchAnalyzerValue = find('searchAnalyzer.select').props().value;
+ expect(searchAnalyzerValue).toEqual('index_default');
+
+ await act(async () => {
+ // Change the value of the 3 analyzers
+ setSelectValue('indexAnalyzer.select', 'standard');
+ setSelectValue('searchAnalyzer.select', 'simple');
+ setSelectValue(find('searchQuoteAnalyzer.select').at(0), 'whitespace');
+ });
+
+ // Make sure the second dropdown select has been removed
+ await waitForFn(
+ async () => find('searchQuoteAnalyzer.select').length === 1,
+ 'Error waiting for the second dropdown select of search quote analyzer to be removed'
+ );
+
+ await act(async () => {
+ // Save & close
+ updateFieldAndCloseFlyout();
+ });
+
+ await waitForFn(
+ async () => exists('mappingsEditorFieldEdit') === false,
+ 'Error waiting for the details flyout to close'
+ );
+
+ updatedMappings = {
+ ...updatedMappings,
+ properties: {
+ myField: {
+ ...updatedMappings.properties.myField,
+ analyzer: 'standard',
+ search_analyzer: 'simple',
+ search_quote_analyzer: 'whitespace',
+ },
+ },
+ };
+
+ ({ data } = await getMappingsEditorData());
+ expect(data).toEqual(updatedMappings);
+
+ // Re-open the flyout and make sure the select have the correct updated value
+ await act(async () => {
+ startEditField('myField');
+ });
+ await waitFor('mappingsEditorFieldEdit');
+ await showAdvancedSettings();
+
+ isUseSameAnalyzerForSearchChecked = getCheckboxValue('useSameAnalyzerForSearchCheckBox.input');
+ expect(isUseSameAnalyzerForSearchChecked).toBe(false);
+
+ indexAnalyzerValue = find('indexAnalyzer.select').props().value;
+ searchAnalyzerValue = find('searchAnalyzer.select').props().value;
+ const searchQuoteAnalyzerValue = find('searchQuoteAnalyzer.select').props().value;
+
+ expect(indexAnalyzerValue).toBe('standard');
+ expect(searchAnalyzerValue).toBe('simple');
+ expect(searchQuoteAnalyzerValue).toBe('whitespace');
+ }, 30000);
+
+ test('analyzer parameter: custom analyzer (external plugin)', async () => {
+ const defaultMappings = {
+ _meta: {},
+ _source: {},
+ properties: {
+ myField: {
+ type: 'text',
+ analyzer: 'myCustomIndexAnalyzer',
+ search_analyzer: 'myCustomSearchAnalyzer',
+ search_quote_analyzer: 'myCustomSearchQuoteAnalyzer',
+ },
+ },
+ };
+
+ let updatedMappings: any = {
+ ...defaultMappings,
+ properties: {
+ myField: {
+ ...defaultMappings.properties.myField,
+ ...defaultTextParameters,
+ },
+ },
+ };
+
+ await act(async () => {
+ testBed = await setup({ value: defaultMappings, onChange: onChangeHandler });
+ });
+
+ const {
+ find,
+ exists,
+ waitFor,
+ waitForFn,
+ component,
+ form: { setInputValue, setSelectValue },
+ actions: { startEditField, showAdvancedSettings, updateFieldAndCloseFlyout },
+ } = testBed;
+ const fieldToEdit = 'myField';
+
+ await act(async () => {
+ startEditField(fieldToEdit);
+ });
+
+ await waitFor('mappingsEditorFieldEdit');
+ await showAdvancedSettings();
+
+ expect(exists('indexAnalyzer-custom')).toBe(true);
+ expect(exists('searchAnalyzer-custom')).toBe(true);
+ expect(exists('searchQuoteAnalyzer-custom')).toBe(true);
+
+ const indexAnalyzerValue = find('indexAnalyzer-custom.input').props().value;
+ const searchAnalyzerValue = find('searchAnalyzer-custom.input').props().value;
+ const searchQuoteAnalyzerValue = find('searchQuoteAnalyzer-custom.input').props().value;
+
+ expect(indexAnalyzerValue).toBe(defaultMappings.properties.myField.analyzer);
+ expect(searchAnalyzerValue).toBe(defaultMappings.properties.myField.search_analyzer);
+ expect(searchQuoteAnalyzerValue).toBe(defaultMappings.properties.myField.search_quote_analyzer);
+
+ const updatedIndexAnalyzer = 'newCustomIndexAnalyzer';
+ const updatedSearchAnalyzer = 'whitespace';
+
+ await act(async () => {
+ // Change the index analyzer to another custom one
+ setInputValue('indexAnalyzer-custom.input', updatedIndexAnalyzer);
+
+ // Change the search analyzer to a built-in analyzer
+ find('searchAnalyzer-toggleCustomButton').simulate('click');
+ component.update();
+ });
+
+ await waitFor('searchAnalyzer');
+
+ await act(async () => {
+ setSelectValue('searchAnalyzer.select', updatedSearchAnalyzer);
+
+ // Change the searchQuote to use built-in analyzer
+ // By default it means using the "index default"
+ find('searchQuoteAnalyzer-toggleCustomButton').simulate('click');
+ component.update();
+ });
+
+ await waitFor('searchQuoteAnalyzer');
+
+ await act(async () => {
+ // Save & close
+ updateFieldAndCloseFlyout();
+ });
+
+ await waitForFn(
+ async () => exists('mappingsEditorFieldEdit') === false,
+ 'Error waiting for the details flyout to close'
+ );
+
+ ({ data } = await getMappingsEditorData());
+
+ updatedMappings = {
+ ...updatedMappings,
+ properties: {
+ myField: {
+ ...updatedMappings.properties.myField,
+ analyzer: updatedIndexAnalyzer,
+ search_analyzer: updatedSearchAnalyzer,
+ search_quote_analyzer: undefined, // Index default means not declaring the analyzer
+ },
+ },
+ };
+
+ expect(data).toEqual(updatedMappings);
+ }, 30000);
+
+ test('analyzer parameter: custom analyzer (from index settings)', async () => {
+ const indexSettings = {
+ analysis: {
+ analyzer: {
+ customAnalyzer_1: {},
+ customAnalyzer_2: {},
+ customAnalyzer_3: {},
+ },
+ },
+ };
+
+ const customAnalyzers = Object.keys(indexSettings.analysis.analyzer);
+
+ const defaultMappings = {
+ _meta: {},
+ _source: {},
+ properties: {
+ myField: {
+ type: 'text',
+ analyzer: customAnalyzers[0],
+ },
+ },
+ };
+
+ let updatedMappings: any = {
+ ...defaultMappings,
+ properties: {
+ myField: {
+ ...defaultMappings.properties.myField,
+ ...defaultTextParameters,
+ },
+ },
+ };
+
+ testBed = await setup({
+ value: defaultMappings,
+ onChange: onChangeHandler,
+ indexSettings,
+ });
+
+ const {
+ find,
+ exists,
+ waitFor,
+ waitForFn,
+ form: { setSelectValue },
+ actions: { startEditField, showAdvancedSettings, updateFieldAndCloseFlyout },
+ } = testBed;
+ const fieldToEdit = 'myField';
+
+ await act(async () => {
+ startEditField(fieldToEdit);
+ });
+ await waitFor('mappingsEditorFieldEdit');
+ await showAdvancedSettings();
+
+ // It should have 2 selects
+ const indexAnalyzerSelects = find('indexAnalyzer.select');
+
+ expect(indexAnalyzerSelects.length).toBe(2);
+ expect(indexAnalyzerSelects.at(0).props().value).toBe('custom');
+ expect(indexAnalyzerSelects.at(1).props().value).toBe(
+ defaultMappings.properties.myField.analyzer
+ );
+
+ // Access the list of option of the second dropdown select
+ const subSelectOptions = indexAnalyzerSelects
+ .at(1)
+ .find('option')
+ .map(wrapper => wrapper.text());
+
+ expect(subSelectOptions).toEqual(customAnalyzers);
+
+ await act(async () => {
+ // Change the custom analyzer dropdown to another one from the index settings
+ setSelectValue(find('indexAnalyzer.select').at(1), customAnalyzers[2]);
+
+ // Save & close
+ updateFieldAndCloseFlyout();
+ });
+
+ await waitForFn(
+ async () => exists('mappingsEditorFieldEdit') === false,
+ 'Error waiting for the details flyout to close'
+ );
+
+ ({ data } = await getMappingsEditorData());
+
+ updatedMappings = {
+ ...updatedMappings,
+ properties: {
+ myField: {
+ ...updatedMappings.properties.myField,
+ analyzer: customAnalyzers[2],
+ },
+ },
+ };
+
+ expect(data).toEqual(updatedMappings);
+ }, 30000);
+});
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/edit_field.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/edit_field.test.tsx
new file mode 100644
index 00000000000000..4af5f82d851e38
--- /dev/null
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/edit_field.test.tsx
@@ -0,0 +1,127 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { act } from 'react-dom/test-utils';
+
+import { componentHelpers, MappingsEditorTestBed } from './helpers';
+import { defaultTextParameters, defaultShapeParameters } from './datatypes';
+const { setup, getMappingsEditorDataFactory } = componentHelpers.mappingsEditor;
+const onChangeHandler = jest.fn();
+const getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler);
+
+describe('Mappings editor: edit field', () => {
+ let testBed: MappingsEditorTestBed;
+
+ afterEach(() => {
+ onChangeHandler.mockReset();
+ });
+
+ test('should open a flyout with the correct field to edit', async () => {
+ const defaultMappings = {
+ properties: {
+ user: {
+ type: 'object',
+ properties: {
+ address: {
+ type: 'object',
+ properties: {
+ street: { type: 'text' },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ await act(async () => {
+ testBed = await setup({ value: defaultMappings, onChange: onChangeHandler });
+ // Make sure all the fields are expanded and present in the DOM
+ await testBed.actions.expandAllFieldsAndReturnMetadata();
+ });
+
+ const {
+ find,
+ waitFor,
+ actions: { startEditField },
+ } = testBed;
+ // Open the flyout to edit the field
+ await act(async () => {
+ startEditField('user.address.street');
+ });
+
+ await waitFor('mappingsEditorFieldEdit');
+
+ // It should have the correct title
+ expect(find('mappingsEditorFieldEdit.flyoutTitle').text()).toEqual(`Edit field 'street'`);
+
+ // It should have the correct field path
+ expect(find('mappingsEditorFieldEdit.fieldPath').text()).toEqual('user > address > street');
+
+ // The advanced settings should be hidden initially
+ expect(find('mappingsEditorFieldEdit.advancedSettings').props().style.display).toEqual('none');
+ });
+
+ test('should update form parameters when changing the field datatype', async () => {
+ const defaultMappings = {
+ _meta: {},
+ _source: {},
+ properties: {
+ myField: {
+ ...defaultTextParameters,
+ },
+ },
+ };
+
+ await act(async () => {
+ testBed = await setup({ value: defaultMappings, onChange: onChangeHandler });
+ });
+
+ const {
+ find,
+ exists,
+ waitFor,
+ waitForFn,
+ component,
+ actions: { startEditField, updateFieldAndCloseFlyout },
+ } = testBed;
+
+ // Open the flyout, change the field type and save it
+ await act(async () => {
+ startEditField('myField');
+ });
+
+ await waitFor('mappingsEditorFieldEdit');
+
+ await act(async () => {
+ // Change the field type
+ find('mappingsEditorFieldEdit.fieldType').simulate('change', [
+ { label: 'Shape', value: defaultShapeParameters.type },
+ ]);
+ component.update();
+ });
+
+ await act(async () => {
+ await updateFieldAndCloseFlyout();
+ });
+
+ await waitForFn(
+ async () => exists('mappingsEditorFieldEdit') === false,
+ 'Error waiting for the details flyout to close'
+ );
+
+ const { data } = await getMappingsEditorData();
+
+ const updatedMappings = {
+ ...defaultMappings,
+ properties: {
+ myField: {
+ ...defaultShapeParameters,
+ },
+ },
+ };
+
+ expect(data).toEqual(updatedMappings);
+ }, 15000);
+});
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts
index fa6bee56349e95..afdc039ae77d2a 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts
@@ -3,7 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import { setup as mappingsEditorSetup, MappingsEditorTestBed } from './mappings_editor.helpers';
+import {
+ setup as mappingsEditorSetup,
+ MappingsEditorTestBed,
+ DomFields,
+ getMappingsEditorDataFactory,
+} from './mappings_editor.helpers';
export {
nextTick,
@@ -13,7 +18,7 @@ export {
} from '../../../../../../../../../test_utils';
export const componentHelpers = {
- mappingsEditor: { setup: mappingsEditorSetup },
+ mappingsEditor: { setup: mappingsEditorSetup, getMappingsEditorDataFactory },
};
-export { MappingsEditorTestBed };
+export { MappingsEditorTestBed, DomFields };
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx
index c8c8ef8bfe9b3d..58242ec35018c8 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx
@@ -4,7 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
+import { act } from 'react-dom/test-utils';
+import { ReactWrapper } from 'enzyme';
+
import { registerTestBed, TestBed, nextTick } from '../../../../../../../../../test_utils';
+import { getChildFieldsName } from '../../../lib';
import { MappingsEditor } from '../../../mappings_editor';
jest.mock('@elastic/eui', () => ({
@@ -14,6 +18,7 @@ jest.mock('@elastic/eui', () => ({
EuiComboBox: (props: any) => (
{
props.onChange([syntheticEvent['0']]);
}}
@@ -29,14 +34,121 @@ jest.mock('@elastic/eui', () => ({
}}
/>
),
+ // Mocking EuiSuperSelect to be able to easily change its value
+ // with a `myWrapper.simulate('change', { target: { value: 'someValue' } })`
+ EuiSuperSelect: (props: any) => (
+ {
+ props.onChange(e.target.value);
+ }}
+ />
+ ),
}));
+export interface DomFields {
+ [key: string]: {
+ type: string;
+ properties?: DomFields;
+ fields?: DomFields;
+ };
+}
+
const createActions = (testBed: TestBed) => {
- const { find, waitFor, form, component } = testBed;
+ const { find, exists, waitFor, waitForFn, form, component } = testBed;
+
+ const getFieldInfo = (testSubjectField: string): { name: string; type: string } => {
+ const name = find(`${testSubjectField}-fieldName` as TestSubjects).text();
+ const type = find(`${testSubjectField}-datatype` as TestSubjects).props()['data-type-value'];
+ return { name, type };
+ };
+
+ const expandField = async (
+ field: ReactWrapper
+ ): Promise<{ hasChildren: boolean; testSubjectField: string }> => {
+ /**
+ * Field list item have 2 test subject assigned to them:
+ * data-test-subj="fieldsListItem "
+ *
+ * We read the second one as it is unique.
+ */
+ const testSubjectField = (field.props() as any)['data-test-subj']
+ .split(' ')
+ .filter((subj: string) => subj !== 'fieldsListItem')[0] as string;
+
+ const expandButton = find(`${testSubjectField}.toggleExpandButton` as TestSubjects);
+
+ // No expand button, so this field is not expanded
+ if (expandButton.length === 0) {
+ return { hasChildren: false, testSubjectField };
+ }
+
+ const isExpanded = (expandButton.props()['aria-label'] as string).includes('Collapse');
+
+ if (!isExpanded) {
+ expandButton.simulate('click');
+ }
+
+ // Wait for the children FieldList to be in the DOM
+ await waitFor(`${testSubjectField}.fieldsList` as TestSubjects);
+
+ return { hasChildren: true, testSubjectField };
+ };
+
+ /**
+ * Expand all the children of a field and return a metadata object of the fields found in the DOM.
+ *
+ * @param fieldName The field under wich we want to expand all the children.
+ * If no fieldName is provided, we expand all the **root** level fields.
+ */
+ const expandAllFieldsAndReturnMetadata = async (
+ fieldName?: string,
+ domTreeMetadata: DomFields = {}
+ ): Promise => {
+ const fields = find(
+ fieldName ? (`${fieldName}.fieldsList.fieldsListItem` as TestSubjects) : 'fieldsListItem'
+ ).map(wrapper => wrapper); // convert to Array for our for of loop below
+
+ for (const field of fields) {
+ const { hasChildren, testSubjectField } = await expandField(field);
+
+ // Read the info from the DOM about that field and add it to our domFieldMeta
+ const { name, type } = getFieldInfo(testSubjectField);
+ domTreeMetadata[name] = {
+ type,
+ };
+
+ if (hasChildren) {
+ // Update our metadata object
+ const childFieldName = getChildFieldsName(type as any)!;
+ domTreeMetadata[name][childFieldName] = {};
+
+ // Expand its children
+ await expandAllFieldsAndReturnMetadata(
+ testSubjectField,
+ domTreeMetadata[name][childFieldName]
+ );
+ }
+ }
+
+ return domTreeMetadata;
+ };
+
+ // Get a nested field in the rendered DOM tree
+ const getFieldAt = (path: string) => {
+ const testSubjectField = `${path.split('.').join('')}Field`;
+ return find(testSubjectField as TestSubjects);
+ };
const addField = async (name: string, type: string) => {
const currentCount = find('fieldsListItem').length;
+ if (!exists('createFieldForm')) {
+ find('addFieldButton').simulate('click');
+ await waitFor('createFieldForm');
+ }
+
form.setInputValue('nameParameterInput', name);
find('createFieldForm.fieldType').simulate('change', [
{
@@ -54,6 +166,36 @@ const createActions = (testBed: TestBed) => {
await waitFor('fieldsListItem', currentCount + 1);
};
+ const startEditField = (path: string) => {
+ const field = getFieldAt(path);
+ find('editFieldButton', field).simulate('click');
+ component.update();
+ };
+
+ const updateFieldAndCloseFlyout = () => {
+ find('mappingsEditorFieldEdit.editFieldUpdateButton').simulate('click');
+ component.update();
+ };
+
+ const showAdvancedSettings = async () => {
+ const checkIsVisible = async () =>
+ find('mappingsEditorFieldEdit.advancedSettings').props().style.display === 'block';
+
+ if (await checkIsVisible()) {
+ // Already opened, nothing else to do
+ return;
+ }
+
+ await act(async () => {
+ find('mappingsEditorFieldEdit.toggleAdvancedSetting').simulate('click');
+ });
+
+ await waitForFn(
+ checkIsVisible,
+ 'Error waiting for the advanced settings CSS style.display to be "block"'
+ );
+ };
+
const selectTab = async (tab: 'fields' | 'templates' | 'advanced') => {
const index = ['fields', 'templates', 'advanced'].indexOf(tab);
const tabIdToContentMap: { [key: string]: TestSubjects } = {
@@ -87,11 +229,33 @@ const createActions = (testBed: TestBed) => {
return value;
};
+ const getComboBoxValue = (testSubject: TestSubjects) => {
+ const value = find(testSubject).props()['data-currentvalue'];
+ if (value === undefined) {
+ return [];
+ }
+ return value.map(({ label }: any) => label);
+ };
+
+ const getToggleValue = (testSubject: TestSubjects): boolean =>
+ find(testSubject).props()['aria-checked'];
+
+ const getCheckboxValue = (testSubject: TestSubjects): boolean =>
+ find(testSubject).props().checked;
+
return {
selectTab,
+ getFieldAt,
addField,
+ expandAllFieldsAndReturnMetadata,
+ startEditField,
+ updateFieldAndCloseFlyout,
+ showAdvancedSettings,
updateJsonEditor,
getJsonEditorValue,
+ getComboBoxValue,
+ getToggleValue,
+ getCheckboxValue,
};
};
@@ -109,6 +273,33 @@ export const setup = async (props: any = { onUpdate() {} }): Promise) => {
+ /**
+ * Helper to access the latest data sent to the onChange handler back to the consumer of the .
+ * Read the latest call with its argument passed and build the mappings object from it.
+ */
+ return async () => {
+ const mockCalls = onChangeHandler.mock.calls;
+
+ if (mockCalls.length === 0) {
+ throw new Error(
+ `Can't access data forwarded as the onChange() prop handler hasn't been called.`
+ );
+ }
+
+ const [arg] = mockCalls[mockCalls.length - 1];
+ const { isValid, validate, getData } = arg;
+
+ const isMappingsValid = isValid === undefined ? await act(validate) : isValid;
+ const data = getData(isMappingsValid);
+
+ return {
+ isValid: isMappingsValid,
+ data,
+ };
+ };
+};
+
export type MappingsEditorTestBed = TestBed & {
actions: ReturnType;
};
@@ -116,7 +307,9 @@ export type MappingsEditorTestBed = TestBed & {
export type TestSubjects =
| 'formTab'
| 'mappingsEditor'
+ | 'fieldsList'
| 'fieldsListItem'
+ | 'fieldsListItem.fieldName'
| 'fieldName'
| 'mappingTypesDetectedCallout'
| 'documentFields'
@@ -126,7 +319,38 @@ export type TestSubjects =
| 'advancedConfiguration.numericDetection.input'
| 'advancedConfiguration.dynamicMappingsToggle'
| 'advancedConfiguration.dynamicMappingsToggle.input'
+ | 'advancedConfiguration.metaField'
+ | 'advancedConfiguration.routingRequiredToggle.input'
+ | 'sourceField.includesField'
+ | 'sourceField.excludesField'
| 'dynamicTemplatesEditor'
| 'nameParameterInput'
+ | 'addFieldButton'
+ | 'editFieldButton'
+ | 'toggleExpandButton'
+ | 'createFieldForm'
| 'createFieldForm.fieldType'
- | 'createFieldForm.addButton';
+ | 'createFieldForm.addButton'
+ | 'mappingsEditorFieldEdit'
+ | 'mappingsEditorFieldEdit.fieldType'
+ | 'mappingsEditorFieldEdit.editFieldUpdateButton'
+ | 'mappingsEditorFieldEdit.flyoutTitle'
+ | 'mappingsEditorFieldEdit.documentationLink'
+ | 'mappingsEditorFieldEdit.fieldPath'
+ | 'mappingsEditorFieldEdit.advancedSettings'
+ | 'mappingsEditorFieldEdit.toggleAdvancedSetting'
+ | 'indexParameter.formRowToggle'
+ | 'indexAnalyzer.select'
+ | 'searchAnalyzer'
+ | 'searchAnalyzer.select'
+ | 'searchQuoteAnalyzer'
+ | 'searchQuoteAnalyzer.select'
+ | 'indexAnalyzer-custom'
+ | 'indexAnalyzer-custom.input'
+ | 'searchAnalyzer-toggleCustomButton'
+ | 'searchAnalyzer-custom'
+ | 'searchAnalyzer-custom.input'
+ | 'searchQuoteAnalyzer-custom'
+ | 'searchQuoteAnalyzer-toggleCustomButton'
+ | 'searchQuoteAnalyzer-custom.input'
+ | 'useSameAnalyzerForSearchCheckBox.input';
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mapped_fields.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mapped_fields.test.tsx
new file mode 100644
index 00000000000000..8989e85d9f188d
--- /dev/null
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mapped_fields.test.tsx
@@ -0,0 +1,104 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import { act } from 'react-dom/test-utils';
+
+import { componentHelpers, MappingsEditorTestBed, DomFields, nextTick } from './helpers';
+
+const { setup } = componentHelpers.mappingsEditor;
+const onChangeHandler = jest.fn();
+
+describe('Mappings editor: mapped fields', () => {
+ afterEach(() => {
+ onChangeHandler.mockReset();
+ });
+
+ describe('', () => {
+ let testBed: MappingsEditorTestBed;
+ const defaultMappings = {
+ properties: {
+ myField: {
+ type: 'text',
+ fields: {
+ raw: {
+ type: 'keyword',
+ },
+ simpleAnalyzer: {
+ type: 'text',
+ },
+ },
+ },
+ myObject: {
+ type: 'object',
+ properties: {
+ deeplyNested: {
+ type: 'object',
+ properties: {
+ title: {
+ type: 'text',
+ fields: {
+ raw: { type: 'keyword' },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+
+ test('should correctly represent the fields in the DOM tree', async () => {
+ await act(async () => {
+ testBed = await setup({
+ value: defaultMappings,
+ onChange: onChangeHandler,
+ });
+ });
+
+ const {
+ actions: { expandAllFieldsAndReturnMetadata },
+ } = testBed;
+
+ let domTreeMetadata: DomFields = {};
+ await act(async () => {
+ domTreeMetadata = await expandAllFieldsAndReturnMetadata();
+ });
+
+ expect(domTreeMetadata).toEqual(defaultMappings.properties);
+ });
+
+ test('should allow to be controlled by parent component and update on prop change', async () => {
+ await act(async () => {
+ testBed = await setup({
+ value: defaultMappings,
+ onChange: onChangeHandler,
+ });
+ });
+
+ const {
+ component,
+ setProps,
+ actions: { expandAllFieldsAndReturnMetadata },
+ } = testBed;
+
+ const newMappings = { properties: { hello: { type: 'text' } } };
+ let domTreeMetadata: DomFields = {};
+
+ await act(async () => {
+ // Change the `value` prop of our
+ setProps({ value: newMappings });
+
+ // Don't ask me why but the 3 following lines are all required
+ component.update();
+ await nextTick();
+ component.update();
+
+ domTreeMetadata = await expandAllFieldsAndReturnMetadata();
+ });
+
+ expect(domTreeMetadata).toEqual(newMappings.properties);
+ });
+ });
+});
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx
index 0cf5bf3f4453ff..f516dfdb372ce7 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx
@@ -5,15 +5,55 @@
*/
import { act } from 'react-dom/test-utils';
-import { componentHelpers, MappingsEditorTestBed, nextTick, getRandomString } from './helpers';
+import { componentHelpers, MappingsEditorTestBed, nextTick } from './helpers';
-const { setup } = componentHelpers.mappingsEditor;
-const mockOnUpdate = () => undefined;
+const { setup, getMappingsEditorDataFactory } = componentHelpers.mappingsEditor;
+const onChangeHandler = jest.fn();
+const getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler);
+
+describe('Mappings editor: core', () => {
+ /**
+ * Variable to store the mappings data forwarded to the consumer component
+ */
+ let data: any;
+
+ afterEach(() => {
+ onChangeHandler.mockReset();
+ });
+
+ test('default behaviour', async () => {
+ const defaultMappings = {
+ properties: {
+ user: {
+ // No type defined for user
+ properties: {
+ name: { type: 'text' },
+ },
+ },
+ },
+ };
+
+ await setup({ value: defaultMappings, onChange: onChangeHandler });
+
+ const expectedMappings = {
+ _meta: {}, // Was not defined so an empty object is returned
+ _source: {}, // Was not defined so an empty object is returned
+ ...defaultMappings,
+ properties: {
+ user: {
+ type: 'object', // Was not defined so it defaults to "object" type
+ ...defaultMappings.properties.user,
+ },
+ },
+ };
+
+ ({ data } = await getMappingsEditorData());
+ expect(data).toEqual(expectedMappings);
+ });
-describe('', () => {
describe('multiple mappings detection', () => {
test('should show a warning when multiple mappings are detected', async () => {
- const defaultValue = {
+ const value = {
type1: {
properties: {
name1: {
@@ -29,7 +69,7 @@ describe('', () => {
},
},
};
- const testBed = await setup({ onUpdate: mockOnUpdate, defaultValue });
+ const testBed = await setup({ onChange: onChangeHandler, value });
const { exists } = testBed;
expect(exists('mappingsEditor')).toBe(true);
@@ -38,14 +78,14 @@ describe('', () => {
});
test('should not show a warning when mappings a single-type', async () => {
- const defaultValue = {
+ const value = {
properties: {
name1: {
type: 'keyword',
},
},
};
- const testBed = await setup({ onUpdate: mockOnUpdate, defaultValue });
+ const testBed = await setup({ onChange: onChangeHandler, value });
const { exists } = testBed;
expect(exists('mappingsEditor')).toBe(true);
@@ -62,12 +102,12 @@ describe('', () => {
let testBed: MappingsEditorTestBed;
beforeEach(async () => {
- testBed = await setup({ defaultValue: defaultMappings, onUpdate() {} });
+ testBed = await setup({ value: defaultMappings, onChange: onChangeHandler });
});
test('should keep the changes when switching tabs', async () => {
const {
- actions: { addField, selectTab, updateJsonEditor, getJsonEditorValue },
+ actions: { addField, selectTab, updateJsonEditor, getJsonEditorValue, getToggleValue },
component,
find,
exists,
@@ -79,7 +119,7 @@ describe('', () => {
// -------------------------------------
expect(find('fieldsListItem').length).toEqual(0); // Check that we start with an empty list
- const newField = { name: getRandomString(), type: 'text' };
+ const newField = { name: 'John', type: 'text' };
await act(async () => {
await addField(newField.name, newField.type);
});
@@ -101,7 +141,6 @@ describe('', () => {
// Update the dynamic templates editor value
const updatedValueTemplates = [{ after: 'bar' }];
-
await act(async () => {
await updateJsonEditor('dynamicTemplatesEditor', updatedValueTemplates);
await nextTick();
@@ -118,9 +157,9 @@ describe('', () => {
await selectTab('advanced');
});
- let isDynamicMappingsEnabled = find(
+ let isDynamicMappingsEnabled = getToggleValue(
'advancedConfiguration.dynamicMappingsToggle.input'
- ).props()['aria-checked'];
+ );
expect(isDynamicMappingsEnabled).toBe(true);
let isNumericDetectionVisible = exists('advancedConfiguration.numericDetection');
@@ -134,9 +173,9 @@ describe('', () => {
await nextTick();
});
- isDynamicMappingsEnabled = find('advancedConfiguration.dynamicMappingsToggle.input').props()[
- 'aria-checked'
- ];
+ isDynamicMappingsEnabled = getToggleValue(
+ 'advancedConfiguration.dynamicMappingsToggle.input'
+ );
expect(isDynamicMappingsEnabled).toBe(false);
isNumericDetectionVisible = exists('advancedConfiguration.numericDetection');
@@ -166,12 +205,185 @@ describe('', () => {
await selectTab('advanced');
});
- isDynamicMappingsEnabled = find('advancedConfiguration.dynamicMappingsToggle.input').props()[
- 'aria-checked'
- ];
+ isDynamicMappingsEnabled = getToggleValue(
+ 'advancedConfiguration.dynamicMappingsToggle.input'
+ );
expect(isDynamicMappingsEnabled).toBe(false);
isNumericDetectionVisible = exists('advancedConfiguration.numericDetection');
expect(isNumericDetectionVisible).toBe(false);
});
});
+
+ describe('component props', () => {
+ /**
+ * Note: the "indexSettings" prop will be tested along with the "analyzer" parameter on a text datatype field,
+ * as it is the only place where it is consumed by the mappings editor.
+ *
+ * The test that covers it is text_datatype.test.tsx: "analyzer parameter: custom analyzer (from index settings)"
+ */
+ const defaultMappings: any = {
+ dynamic: true,
+ numeric_detection: false,
+ date_detection: true,
+ properties: {
+ title: { type: 'text' },
+ address: {
+ type: 'object',
+ properties: {
+ street: { type: 'text' },
+ city: { type: 'text' },
+ },
+ },
+ },
+ dynamic_templates: [{ initial: 'value' }],
+ _source: {
+ enabled: true,
+ includes: ['field1', 'field2'],
+ excludes: ['field3'],
+ },
+ _meta: {
+ some: 'metaData',
+ },
+ _routing: {
+ required: false,
+ },
+ };
+
+ let testBed: MappingsEditorTestBed;
+
+ beforeEach(async () => {
+ testBed = await setup({ value: defaultMappings, onChange: onChangeHandler });
+ });
+
+ test('props.value => should prepopulate the editor data', async () => {
+ const {
+ actions: { selectTab, getJsonEditorValue, getComboBoxValue, getToggleValue },
+ find,
+ } = testBed;
+
+ /**
+ * Mapped fields
+ */
+ // Test that root-level mappings "properties" are rendered as root-level "DOM tree items"
+ const fields = find('fieldsListItem.fieldName').map(item => item.text());
+ expect(fields).toEqual(Object.keys(defaultMappings.properties).sort());
+
+ /**
+ * Dynamic templates
+ */
+ await act(async () => {
+ await selectTab('templates');
+ });
+
+ // Test that dynamic templates JSON is rendered in the templates editor
+ const templatesValue = getJsonEditorValue('dynamicTemplatesEditor');
+ expect(templatesValue).toEqual(defaultMappings.dynamic_templates);
+
+ /**
+ * Advanced settings
+ */
+ await act(async () => {
+ await selectTab('advanced');
+ });
+
+ const isDynamicMappingsEnabled = getToggleValue(
+ 'advancedConfiguration.dynamicMappingsToggle.input'
+ );
+ expect(isDynamicMappingsEnabled).toBe(defaultMappings.dynamic);
+
+ const isNumericDetectionEnabled = getToggleValue(
+ 'advancedConfiguration.numericDetection.input'
+ );
+ expect(isNumericDetectionEnabled).toBe(defaultMappings.numeric_detection);
+
+ expect(getComboBoxValue('sourceField.includesField')).toEqual(
+ defaultMappings._source.includes
+ );
+ expect(getComboBoxValue('sourceField.excludesField')).toEqual(
+ defaultMappings._source.excludes
+ );
+
+ const metaFieldValue = getJsonEditorValue('advancedConfiguration.metaField');
+ expect(metaFieldValue).toEqual(defaultMappings._meta);
+
+ const isRoutingRequired = getToggleValue('advancedConfiguration.routingRequiredToggle.input');
+ expect(isRoutingRequired).toBe(defaultMappings._routing.required);
+ });
+
+ test('props.onChange() => should forward the changes to the consumer component', async () => {
+ let updatedMappings = { ...defaultMappings };
+
+ const {
+ actions: { addField, selectTab, updateJsonEditor },
+ component,
+ form,
+ } = testBed;
+
+ /**
+ * Mapped fields
+ */
+ const newField = { name: 'someNewField', type: 'text' };
+ updatedMappings = {
+ ...updatedMappings,
+ properties: {
+ ...updatedMappings.properties,
+ [newField.name]: { type: 'text' },
+ },
+ };
+
+ await act(async () => {
+ await addField(newField.name, newField.type);
+ });
+
+ ({ data } = await getMappingsEditorData());
+ expect(data).toEqual(updatedMappings);
+
+ /**
+ * Dynamic templates
+ */
+ await act(async () => {
+ await selectTab('templates');
+ });
+
+ const updatedTemplatesValue = [{ someTemplateProp: 'updated' }];
+ updatedMappings = {
+ ...updatedMappings,
+ dynamic_templates: updatedTemplatesValue,
+ };
+
+ await act(async () => {
+ await updateJsonEditor('dynamicTemplatesEditor', updatedTemplatesValue);
+ await nextTick();
+ component.update();
+ });
+
+ ({ data } = await getMappingsEditorData());
+ expect(data).toEqual(updatedMappings);
+
+ /**
+ * Advanced settings
+ */
+ await act(async () => {
+ await selectTab('advanced');
+ });
+
+ // Disbable dynamic mappings
+ await act(async () => {
+ form.toggleEuiSwitch('advancedConfiguration.dynamicMappingsToggle.input');
+ });
+
+ ({ data } = await getMappingsEditorData());
+
+ // When we disable dynamic mappings, we set it to "false" and remove date and numeric detections
+ updatedMappings = {
+ ...updatedMappings,
+ dynamic: false,
+ date_detection: undefined,
+ dynamic_date_formats: undefined,
+ numeric_detection: undefined,
+ };
+
+ expect(data).toEqual(updatedMappings);
+ });
+ });
});
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx
index 6b33d4450c3ae2..c84756cab8e886 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx
@@ -7,6 +7,7 @@ import React, { useEffect, useRef } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { useForm, Form, SerializerFunc } from '../../shared_imports';
+import { GenericObject } from '../../types';
import { Types, useDispatch } from '../../mappings_state';
import { DynamicMappingSection } from './dynamic_mapping_section';
import { SourceFieldSection } from './source_field_section';
@@ -17,10 +18,10 @@ import { configurationFormSchema } from './configuration_form_schema';
type MappingsConfiguration = Types['MappingsConfiguration'];
interface Props {
- defaultValue?: MappingsConfiguration;
+ value?: MappingsConfiguration;
}
-const stringifyJson = (json: { [key: string]: any }) =>
+const stringifyJson = (json: GenericObject) =>
Object.keys(json).length ? JSON.stringify(json, null, 2) : '{\n\n}';
const formSerializer: SerializerFunc = formData => {
@@ -57,7 +58,7 @@ const formSerializer: SerializerFunc = formData => {
};
};
-const formDeserializer = (formData: { [key: string]: any }) => {
+const formDeserializer = (formData: GenericObject) => {
const {
dynamic,
numeric_detection,
@@ -86,14 +87,14 @@ const formDeserializer = (formData: { [key: string]: any }) => {
};
};
-export const ConfigurationForm = React.memo(({ defaultValue }: Props) => {
+export const ConfigurationForm = React.memo(({ value }: Props) => {
const didMountRef = useRef(false);
const { form } = useForm({
schema: configurationFormSchema,
serializer: formSerializer,
deserializer: formDeserializer,
- defaultValue,
+ defaultValue: value,
});
const dispatch = useDispatch();
@@ -114,14 +115,14 @@ export const ConfigurationForm = React.memo(({ defaultValue }: Props) => {
useEffect(() => {
if (didMountRef.current) {
- // If the defaultValue has changed (it probably means that we have loaded a new JSON)
+ // If the value has changed (it probably means that we have loaded a new JSON)
// we need to reset the form to update the fields values.
form.reset({ resetValues: true });
} else {
// Avoid reseting the form on component mount.
didMountRef.current = true;
}
- }, [defaultValue]); // eslint-disable-line react-hooks/exhaustive-deps
+ }, [value]); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
return () => {
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx
index cb9b464d270ce3..c1a2b195a3f576 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx
@@ -67,6 +67,7 @@ export const DynamicMappingSection = () => (
return (
<>
@@ -87,6 +88,7 @@ export const DynamicMappingSection = () => (
} else {
return (
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx
index 68b76a1203ad52..7185016029e00e 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx
@@ -46,6 +46,7 @@ export const MetaFieldSection = () => (
'aria-label': i18n.translate('xpack.idxMgmt.mappingsEditor.metaFieldEditorAriaLabel', {
defaultMessage: '_meta field data editor',
}),
+ 'data-test-subj': 'metaField',
},
}}
/>
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx
index 7f434d6f834b2b..f06b292bc33c8e 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx
@@ -35,7 +35,11 @@ export const RoutingSection = () => {
/>
}
>
-
+
);
};
diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx
index f79741d9a1a9f1..4278598dfc7c16 100644
--- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx
+++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx
@@ -65,7 +65,7 @@ export const SourceFieldSection = () => {
);
const renderFormFields = () => (
- <>
+