diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md
index 5347f0b55e19b..98d7b0610abea 100644
--- a/docs/development/core/server/kibana-plugin-core-server.md
+++ b/docs/development/core/server/kibana-plugin-core-server.md
@@ -153,6 +153,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [SavedObjectMigrationMap](./kibana-plugin-core-server.savedobjectmigrationmap.md) | A map of [migration functions](./kibana-plugin-core-server.savedobjectmigrationfn.md) to be used for a given type. The map's keys must be valid semver versions.For a given document, only migrations with a higher version number than that of the document will be applied. Migrations are executed in order, starting from the lowest version and ending with the highest one. |
| [SavedObjectReference](./kibana-plugin-core-server.savedobjectreference.md) | A reference to another saved object. |
| [SavedObjectsAddToNamespacesOptions](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md) | |
+| [SavedObjectsAddToNamespacesResponse](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md) | |
| [SavedObjectsBaseOptions](./kibana-plugin-core-server.savedobjectsbaseoptions.md) | |
| [SavedObjectsBulkCreateObject](./kibana-plugin-core-server.savedobjectsbulkcreateobject.md) | |
| [SavedObjectsBulkGetObject](./kibana-plugin-core-server.savedobjectsbulkgetobject.md) | |
@@ -167,6 +168,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md) | |
| [SavedObjectsDeleteByNamespaceOptions](./kibana-plugin-core-server.savedobjectsdeletebynamespaceoptions.md) | |
| [SavedObjectsDeleteFromNamespacesOptions](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesoptions.md) | |
+| [SavedObjectsDeleteFromNamespacesResponse](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md) | |
| [SavedObjectsDeleteOptions](./kibana-plugin-core-server.savedobjectsdeleteoptions.md) | |
| [SavedObjectsExportOptions](./kibana-plugin-core-server.savedobjectsexportoptions.md) | Options controlling the export operation. |
| [SavedObjectsExportResultDetails](./kibana-plugin-core-server.savedobjectsexportresultdetails.md) | Structure of the export result details entry |
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md
new file mode 100644
index 0000000000000..306f502f0b0b3
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md
@@ -0,0 +1,19 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsAddToNamespacesResponse](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md)
+
+## SavedObjectsAddToNamespacesResponse interface
+
+
+Signature:
+
+```typescript
+export interface SavedObjectsAddToNamespacesResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [namespaces](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md) | string[]
| The namespaces the object exists in after this operation is complete. |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md
new file mode 100644
index 0000000000000..4fc2e376304d4
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsAddToNamespacesResponse](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md) > [namespaces](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.namespaces.md)
+
+## SavedObjectsAddToNamespacesResponse.namespaces property
+
+The namespaces the object exists in after this operation is complete.
+
+Signature:
+
+```typescript
+namespaces: string[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md
index 45c9c39f9626a..567390faba9b2 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.addtonamespaces.md
@@ -9,7 +9,7 @@ Adds namespaces to a SavedObject
Signature:
```typescript
-addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise<{}>;
+addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise;
```
## Parameters
@@ -23,5 +23,5 @@ addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedO
Returns:
-`Promise<{}>`
+`Promise`
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md
index 80b58d29d393b..18ef5c3e6350c 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.deletefromnamespaces.md
@@ -9,7 +9,7 @@ Removes namespaces from a SavedObject
Signature:
```typescript
-deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise<{}>;
+deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise;
```
## Parameters
@@ -23,5 +23,5 @@ deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: S
Returns:
-`Promise<{}>`
+`Promise`
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md
new file mode 100644
index 0000000000000..6021c8866f018
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md
@@ -0,0 +1,19 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsDeleteFromNamespacesResponse](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md)
+
+## SavedObjectsDeleteFromNamespacesResponse interface
+
+
+Signature:
+
+```typescript
+export interface SavedObjectsDeleteFromNamespacesResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [namespaces](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md) | string[]
| The namespaces the object exists in after this operation is complete. An empty array indicates the object was deleted. |
+
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md
new file mode 100644
index 0000000000000..9600a9e891380
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsDeleteFromNamespacesResponse](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.md) > [namespaces](./kibana-plugin-core-server.savedobjectsdeletefromnamespacesresponse.namespaces.md)
+
+## SavedObjectsDeleteFromNamespacesResponse.namespaces property
+
+The namespaces the object exists in after this operation is complete. An empty array indicates the object was deleted.
+
+Signature:
+
+```typescript
+namespaces: string[];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md
index bbb20d2bc3b96..4b69b10318ed3 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md
@@ -9,7 +9,7 @@ Adds one or more namespaces to a given multi-namespace saved object. This method
Signature:
```typescript
-addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise<{}>;
+addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise;
```
## Parameters
@@ -23,5 +23,5 @@ addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedO
Returns:
-`Promise<{}>`
+`Promise`
diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md
index 471c3e3c5092d..d5ffb6d9ff9d8 100644
--- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md
+++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md
@@ -9,7 +9,7 @@ Removes one or more namespaces from a given multi-namespace saved object. If no
Signature:
```typescript
-deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise<{}>;
+deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise;
```
## Parameters
@@ -23,5 +23,5 @@ deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: S
Returns:
-`Promise<{}>`
+`Promise`
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md
index 4233eea42cded..c54e549c42a44 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-export declare type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T;
+export declare type FieldFormatsGetConfigFn = GetConfigFn;
```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getsearchparamsfromrequest.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getsearchparamsfromrequest.md
index 5bd1694cbeea3..337b4b3302cc3 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getsearchparamsfromrequest.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.getsearchparamsfromrequest.md
@@ -4,12 +4,13 @@
## getSearchParamsFromRequest() function
+
Signature:
```typescript
export declare function getSearchParamsFromRequest(searchRequest: SearchRequest, dependencies: {
- injectedMetadata: CoreStart['injectedMetadata'];
- uiSettings: IUiSettingsClient;
+ esShardTimeout: number;
+ getConfig: GetConfigFn;
}): ISearchRequestParams;
```
@@ -18,7 +19,7 @@ export declare function getSearchParamsFromRequest(searchRequest: SearchRequest,
| Parameter | Type | Description |
| --- | --- | --- |
| searchRequest | SearchRequest
| |
-| dependencies | {
injectedMetadata: CoreStart['injectedMetadata'];
uiSettings: IUiSettingsClient;
}
| |
+| dependencies | {
esShardTimeout: number;
getConfig: GetConfigFn;
}
| |
Returns:
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md
index c8815ed42d3b3..a3f6c709246ea 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-export declare type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T;
+export declare type FieldFormatsGetConfigFn = GetConfigFn;
```
diff --git a/docs/visualize/vega.asciidoc b/docs/visualize/vega.asciidoc
index 9b8c32d7e41f0..b231159e86bde 100644
--- a/docs/visualize/vega.asciidoc
+++ b/docs/visualize/vega.asciidoc
@@ -1,8 +1,6 @@
[[vega-graph]]
== Vega
-experimental[]
-
Build custom visualizations using Vega and Vega-Lite, backed by one or more
data sources including {es}, Elastic Map Service, URL,
or static data. Use the {kib} extensions to Vega to embed Vega into
@@ -1259,7 +1257,7 @@ Override it by providing a different `stroke`, `fill`, or `color` (Vega-Lite) va
[[vega-queries]]
==== Writing {es} queries in Vega
-experimental[] {kib} extends the Vega https://vega.github.io/vega/docs/data/[data] elements
+{kib} extends the Vega https://vega.github.io/vega/docs/data/[data] elements
with support for direct {es} queries specified as a `url`.
Because of this, {kib} is **unable to support dynamically loaded data**,
@@ -1414,7 +1412,7 @@ try to get about 10-15 data points (buckets).
[[vega-esmfiles]]
=== Access Elastic Map Service files
-experimental[] Access the Elastic Map Service files via the same mechanism:
+Access the Elastic Map Service files via the same mechanism:
[source,yaml]
----
@@ -1619,7 +1617,7 @@ kibanaSetTimeFilter(start, end)
[[vega-useful-links]]
=== Resources and examples
-experimental[] To learn more about Vega and Vega-Lite, refer to the resources and examples.
+To learn more about Vega and Vega-Lite, refer to the resources and examples.
==== Vega editor
The https://vega.github.io/editor/[Vega Editor] includes examples for Vega & Vega-Lite, but does not support any
diff --git a/examples/embeddable_examples/public/book/book_embeddable.tsx b/examples/embeddable_examples/public/book/book_embeddable.tsx
index 73b1629d985b7..33876ab24414e 100644
--- a/examples/embeddable_examples/public/book/book_embeddable.tsx
+++ b/examples/embeddable_examples/public/book/book_embeddable.tsx
@@ -26,7 +26,6 @@ import {
EmbeddableOutput,
SavedObjectEmbeddableInput,
ReferenceOrValueEmbeddable,
- Container,
} from '../../../../src/plugins/embeddable/public';
import { BookSavedObjectAttributes } from '../../common';
import { BookEmbeddableComponent } from './book_component';
@@ -104,16 +103,13 @@ export class BookEmbeddable extends Embeddable => {
- const input =
- this.getRoot() && (this.getRoot() as Container).getInput().panels[this.id].explicitInput
- ? ((this.getRoot() as Container).getInput().panels[this.id]
- .explicitInput as BookEmbeddableInput)
- : this.input;
+ const input = this.attributeService.getExplicitInputFromEmbeddable(this);
return this.attributeService.getInputAsValueType(input);
};
getInputAsRefType = async (): Promise => {
- return this.attributeService.getInputAsRefType(this.input, { showSaveModal: true });
+ const input = this.attributeService.getExplicitInputFromEmbeddable(this);
+ return this.attributeService.getInputAsRefType(input, { showSaveModal: true });
};
public render(node: HTMLElement) {
diff --git a/package.json b/package.json
index 7d12af2eb5066..23fc31f369b8d 100644
--- a/package.json
+++ b/package.json
@@ -123,7 +123,7 @@
"dependencies": {
"@babel/core": "^7.11.1",
"@babel/register": "^7.10.5",
- "@elastic/apm-rum": "^5.4.0",
+ "@elastic/apm-rum": "^5.5.0",
"@elastic/charts": "19.8.1",
"@elastic/datemath": "5.0.3",
"@elastic/elasticsearch": "7.9.0-rc.2",
diff --git a/src/core/server/index.ts b/src/core/server/index.ts
index 382318ea86a34..76bcf5f7df665 100644
--- a/src/core/server/index.ts
+++ b/src/core/server/index.ts
@@ -274,7 +274,9 @@ export {
SavedObjectsUpdateOptions,
SavedObjectsUpdateResponse,
SavedObjectsAddToNamespacesOptions,
+ SavedObjectsAddToNamespacesResponse,
SavedObjectsDeleteFromNamespacesOptions,
+ SavedObjectsDeleteFromNamespacesResponse,
SavedObjectsServiceStart,
SavedObjectsServiceSetup,
SavedObjectStatusMeta,
diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js
index 941c4091a66a7..6d85223d1fc88 100644
--- a/src/core/server/saved_objects/service/lib/repository.test.js
+++ b/src/core/server/saved_objects/service/lib/repository.test.js
@@ -393,14 +393,14 @@ describe('SavedObjectsRepository', () => {
});
describe('returns', () => {
- it(`returns an empty object on success`, async () => {
+ it(`returns all existing and new namespaces on success`, async () => {
const result = await addToNamespacesSuccess(type, id, [newNs1, newNs2]);
- expect(result).toEqual({});
+ expect(result).toEqual({ namespaces: [currentNs1, currentNs2, newNs1, newNs2] });
});
it(`succeeds when adding existing namespaces`, async () => {
const result = await addToNamespacesSuccess(type, id, [currentNs1]);
- expect(result).toEqual({});
+ expect(result).toEqual({ namespaces: [currentNs1, currentNs2] });
});
});
});
@@ -3102,17 +3102,17 @@ describe('SavedObjectsRepository', () => {
});
describe('returns', () => {
- it(`returns an empty object on success (delete)`, async () => {
+ it(`returns an empty namespaces array on success (delete)`, async () => {
const test = async (namespaces) => {
const result = await deleteFromNamespacesSuccess(type, id, namespaces, namespaces);
- expect(result).toEqual({});
+ expect(result).toEqual({ namespaces: [] });
client.delete.mockClear();
};
await test([namespace1]);
await test([namespace1, namespace2]);
});
- it(`returns an empty object on success (update)`, async () => {
+ it(`returns remaining namespaces on success (update)`, async () => {
const test = async (remaining) => {
const currentNamespaces = [namespace1].concat(remaining);
const result = await deleteFromNamespacesSuccess(
@@ -3121,7 +3121,7 @@ describe('SavedObjectsRepository', () => {
[namespace1],
currentNamespaces
);
- expect(result).toEqual({});
+ expect(result).toEqual({ namespaces: remaining });
client.delete.mockClear();
};
await test([namespace2]);
@@ -3132,7 +3132,7 @@ describe('SavedObjectsRepository', () => {
const namespaces = [namespace2];
const currentNamespaces = [namespace1];
const result = await deleteFromNamespacesSuccess(type, id, namespaces, currentNamespaces);
- expect(result).toEqual({});
+ expect(result).toEqual({ namespaces: currentNamespaces });
});
});
});
diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts
index 9f6db446ea195..28d409f7b65bb 100644
--- a/src/core/server/saved_objects/service/lib/repository.ts
+++ b/src/core/server/saved_objects/service/lib/repository.ts
@@ -52,7 +52,9 @@ import {
SavedObjectsBulkUpdateOptions,
SavedObjectsDeleteOptions,
SavedObjectsAddToNamespacesOptions,
+ SavedObjectsAddToNamespacesResponse,
SavedObjectsDeleteFromNamespacesOptions,
+ SavedObjectsDeleteFromNamespacesResponse,
} from '../saved_objects_client';
import {
SavedObject,
@@ -947,7 +949,7 @@ export class SavedObjectsRepository {
id: string,
namespaces: string[],
options: SavedObjectsAddToNamespacesOptions = {}
- ): Promise<{}> {
+ ): Promise {
if (!this._allowedTypes.includes(type)) {
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
@@ -996,7 +998,7 @@ export class SavedObjectsRepository {
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
- return {};
+ return { namespaces: doc.namespaces };
}
/**
@@ -1009,7 +1011,7 @@ export class SavedObjectsRepository {
id: string,
namespaces: string[],
options: SavedObjectsDeleteFromNamespacesOptions = {}
- ): Promise<{}> {
+ ): Promise {
if (!this._allowedTypes.includes(type)) {
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
@@ -1063,7 +1065,7 @@ export class SavedObjectsRepository {
// see "404s from missing index" above
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
- return {};
+ return { namespaces: doc.namespaces };
} else {
// if there are no namespaces remaining, delete the saved object
const { body, statusCode } = await this.client.delete(
@@ -1080,7 +1082,7 @@ export class SavedObjectsRepository {
const deleted = body.result === 'deleted';
if (deleted) {
- return {};
+ return { namespaces: [] };
}
const deleteDocNotFound = body.result === 'not_found';
diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts
index 6a9f4f5143e84..812669ee108a2 100644
--- a/src/core/server/saved_objects/service/saved_objects_client.ts
+++ b/src/core/server/saved_objects/service/saved_objects_client.ts
@@ -135,6 +135,15 @@ export interface SavedObjectsAddToNamespacesOptions extends SavedObjectsBaseOpti
refresh?: MutatingOperationRefreshSetting;
}
+/**
+ *
+ * @public
+ */
+export interface SavedObjectsAddToNamespacesResponse {
+ /** The namespaces the object exists in after this operation is complete. */
+ namespaces: string[];
+}
+
/**
*
* @public
@@ -144,6 +153,15 @@ export interface SavedObjectsDeleteFromNamespacesOptions extends SavedObjectsBas
refresh?: MutatingOperationRefreshSetting;
}
+/**
+ *
+ * @public
+ */
+export interface SavedObjectsDeleteFromNamespacesResponse {
+ /** The namespaces the object exists in after this operation is complete. An empty array indicates the object was deleted. */
+ namespaces: string[];
+}
+
/**
*
* @public
@@ -320,7 +338,7 @@ export class SavedObjectsClient {
id: string,
namespaces: string[],
options: SavedObjectsAddToNamespacesOptions = {}
- ): Promise<{}> {
+ ): Promise {
return await this._repository.addToNamespaces(type, id, namespaces, options);
}
@@ -337,7 +355,7 @@ export class SavedObjectsClient {
id: string,
namespaces: string[],
options: SavedObjectsDeleteFromNamespacesOptions = {}
- ): Promise<{}> {
+ ): Promise {
return await this._repository.deleteFromNamespaces(type, id, namespaces, options);
}
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index ff178e236a12f..772be68f507d5 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -2023,6 +2023,11 @@ export interface SavedObjectsAddToNamespacesOptions extends SavedObjectsBaseOpti
version?: string;
}
+// @public (undocumented)
+export interface SavedObjectsAddToNamespacesResponse {
+ namespaces: string[];
+}
+
// Warning: (ae-forgotten-export) The symbol "SavedObjectDoc" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "Referencable" needs to be exported by the entry point index.d.ts
//
@@ -2092,13 +2097,13 @@ export interface SavedObjectsBulkUpdateResponse {
export class SavedObjectsClient {
// @internal
constructor(repository: ISavedObjectsRepository);
- addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise<{}>;
+ addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise;
bulkCreate(objects: Array>, options?: SavedObjectsCreateOptions): Promise>;
bulkGet(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise>;
bulkUpdate(objects: Array>, options?: SavedObjectsBulkUpdateOptions): Promise>;
create(type: string, attributes: T, options?: SavedObjectsCreateOptions): Promise>;
delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>;
- deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise<{}>;
+ deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise;
// (undocumented)
static errors: typeof SavedObjectsErrorHelpers;
// (undocumented)
@@ -2194,6 +2199,11 @@ export interface SavedObjectsDeleteFromNamespacesOptions extends SavedObjectsBas
refresh?: MutatingOperationRefreshSetting;
}
+// @public (undocumented)
+export interface SavedObjectsDeleteFromNamespacesResponse {
+ namespaces: string[];
+}
+
// @public (undocumented)
export interface SavedObjectsDeleteOptions extends SavedObjectsBaseOptions {
refresh?: MutatingOperationRefreshSetting;
@@ -2492,7 +2502,7 @@ export interface SavedObjectsRawDoc {
// @public (undocumented)
export class SavedObjectsRepository {
- addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise<{}>;
+ addToNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions): Promise;
bulkCreate(objects: Array>, options?: SavedObjectsCreateOptions): Promise>;
bulkGet(objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions): Promise>;
bulkUpdate(objects: Array>, options?: SavedObjectsBulkUpdateOptions): Promise>;
@@ -2503,7 +2513,7 @@ export class SavedObjectsRepository {
static createRepository(migrator: KibanaMigrator, typeRegistry: SavedObjectTypeRegistry, indexName: string, client: ElasticsearchClient, includedHiddenTypes?: string[], injectedConstructor?: any): ISavedObjectsRepository;
delete(type: string, id: string, options?: SavedObjectsDeleteOptions): Promise<{}>;
deleteByNamespace(namespace: string, options?: SavedObjectsDeleteByNamespaceOptions): Promise;
- deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise<{}>;
+ deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise;
// (undocumented)
find({ search, defaultSearchOperator, searchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespaces, type, filter, preference, }: SavedObjectsFindOptions): Promise>;
get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>;
diff --git a/src/legacy/ui/public/new_platform/set_services.ts b/src/legacy/ui/public/new_platform/set_services.ts
index ee92eda064aa8..036157a9f3fbc 100644
--- a/src/legacy/ui/public/new_platform/set_services.ts
+++ b/src/legacy/ui/public/new_platform/set_services.ts
@@ -41,7 +41,6 @@ interface NpStart {
export function setSetupServices(npSetup: NpSetup) {
// Services that need to be set in the legacy platform since the legacy data plugin
// which previously provided them has been removed.
- dataServices.setInjectedMetadata(npSetup.core.injectedMetadata);
visualizationsServices.setUISettings(npSetup.core.uiSettings);
visualizationsServices.setUsageCollector(npSetup.plugins.usageCollection);
}
@@ -49,7 +48,6 @@ export function setSetupServices(npSetup: NpSetup) {
export function setStartServices(npStart: NpStart) {
// Services that need to be set in the legacy platform since the legacy data plugin
// which previously provided them has been removed.
- dataServices.setHttp(npStart.core.http);
dataServices.setNotifications(npStart.core.notifications);
dataServices.setOverlays(npStart.core.overlays);
dataServices.setUiSettings(npStart.core.uiSettings);
diff --git a/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx b/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx
new file mode 100644
index 0000000000000..9fa7fff9ad087
--- /dev/null
+++ b/src/plugins/dashboard/public/application/actions/add_to_library_action.test.tsx
@@ -0,0 +1,162 @@
+/*
+ * 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 { isErrorEmbeddable, IContainer, ReferenceOrValueEmbeddable } from '../../embeddable_plugin';
+import { DashboardContainer } from '../embeddable';
+import { getSampleDashboardInput } from '../test_helpers';
+import {
+ CONTACT_CARD_EMBEDDABLE,
+ ContactCardEmbeddableFactory,
+ ContactCardEmbeddable,
+ ContactCardEmbeddableInput,
+ ContactCardEmbeddableOutput,
+} from '../../embeddable_plugin_test_samples';
+import { coreMock } from '../../../../../core/public/mocks';
+import { CoreStart } from 'kibana/public';
+import { AddToLibraryAction } from '.';
+import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
+import { ViewMode } from '../../../../embeddable/public';
+
+const { setup, doStart } = embeddablePluginMock.createInstance();
+setup.registerEmbeddableFactory(
+ CONTACT_CARD_EMBEDDABLE,
+ new ContactCardEmbeddableFactory((() => null) as any, {} as any)
+);
+const start = doStart();
+
+let container: DashboardContainer;
+let embeddable: ContactCardEmbeddable & ReferenceOrValueEmbeddable;
+let coreStart: CoreStart;
+beforeEach(async () => {
+ coreStart = coreMock.createStart();
+
+ const containerOptions = {
+ ExitFullScreenButton: () => null,
+ SavedObjectFinder: () => null,
+ application: {} as any,
+ embeddable: start,
+ inspector: {} as any,
+ notifications: {} as any,
+ overlays: coreStart.overlays,
+ savedObjectMetaData: {} as any,
+ uiActions: {} as any,
+ };
+
+ container = new DashboardContainer(getSampleDashboardInput(), containerOptions);
+
+ const contactCardEmbeddable = await container.addNewEmbeddable<
+ ContactCardEmbeddableInput,
+ ContactCardEmbeddableOutput,
+ ContactCardEmbeddable
+ >(CONTACT_CARD_EMBEDDABLE, {
+ firstName: 'Kibanana',
+ });
+
+ if (isErrorEmbeddable(contactCardEmbeddable)) {
+ throw new Error('Failed to create embeddable');
+ } else {
+ embeddable = embeddablePluginMock.mockRefOrValEmbeddable<
+ ContactCardEmbeddable,
+ ContactCardEmbeddableInput
+ >(contactCardEmbeddable, {
+ mockedByReferenceInput: { savedObjectId: 'testSavedObjectId', id: contactCardEmbeddable.id },
+ mockedByValueInput: { firstName: 'Kibanana', id: contactCardEmbeddable.id },
+ });
+ embeddable.updateInput({ viewMode: ViewMode.EDIT });
+ }
+});
+
+test('Add to library is compatible when embeddable on dashboard has value type input', async () => {
+ const action = new AddToLibraryAction();
+ embeddable.updateInput(await embeddable.getInputAsValueType());
+ expect(await action.isCompatible({ embeddable })).toBe(true);
+});
+
+test('Add to library is not compatible when embeddable input is by reference', async () => {
+ const action = new AddToLibraryAction();
+ embeddable.updateInput(await embeddable.getInputAsRefType());
+ expect(await action.isCompatible({ embeddable })).toBe(false);
+});
+
+test('Add to library is not compatible when view mode is set to view', async () => {
+ const action = new AddToLibraryAction();
+ embeddable.updateInput(await embeddable.getInputAsRefType());
+ embeddable.updateInput({ viewMode: ViewMode.VIEW });
+ expect(await action.isCompatible({ embeddable })).toBe(false);
+});
+
+test('Add to library is not compatible when embeddable is not in a dashboard container', async () => {
+ let orphanContactCard = await container.addNewEmbeddable<
+ ContactCardEmbeddableInput,
+ ContactCardEmbeddableOutput,
+ ContactCardEmbeddable
+ >(CONTACT_CARD_EMBEDDABLE, {
+ firstName: 'Orphan',
+ });
+ orphanContactCard = embeddablePluginMock.mockRefOrValEmbeddable<
+ ContactCardEmbeddable,
+ ContactCardEmbeddableInput
+ >(orphanContactCard, {
+ mockedByReferenceInput: { savedObjectId: 'test', id: orphanContactCard.id },
+ mockedByValueInput: { firstName: 'Kibanana', id: orphanContactCard.id },
+ });
+ const action = new AddToLibraryAction();
+ expect(await action.isCompatible({ embeddable: orphanContactCard })).toBe(false);
+});
+
+test('Add to library replaces embeddableId but retains panel count', async () => {
+ const dashboard = embeddable.getRoot() as IContainer;
+ const originalPanelCount = Object.keys(dashboard.getInput().panels).length;
+ const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
+ const action = new AddToLibraryAction();
+ await action.execute({ embeddable });
+ expect(Object.keys(container.getInput().panels).length).toEqual(originalPanelCount);
+
+ const newPanelId = Object.keys(container.getInput().panels).find(
+ (key) => !originalPanelKeySet.has(key)
+ );
+ expect(newPanelId).toBeDefined();
+ const newPanel = container.getInput().panels[newPanelId!];
+ expect(newPanel.type).toEqual(embeddable.type);
+});
+
+test('Add to library returns reference type input', async () => {
+ const complicatedAttributes = {
+ attribute1: 'The best attribute',
+ attribute2: 22,
+ attribute3: ['array', 'of', 'strings'],
+ attribute4: { nestedattribute: 'hello from the nest' },
+ };
+
+ embeddable = embeddablePluginMock.mockRefOrValEmbeddable(embeddable, {
+ mockedByReferenceInput: { savedObjectId: 'testSavedObjectId', id: embeddable.id },
+ mockedByValueInput: { attributes: complicatedAttributes, id: embeddable.id },
+ });
+ const dashboard = embeddable.getRoot() as IContainer;
+ const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
+ const action = new AddToLibraryAction();
+ await action.execute({ embeddable });
+ const newPanelId = Object.keys(container.getInput().panels).find(
+ (key) => !originalPanelKeySet.has(key)
+ );
+ expect(newPanelId).toBeDefined();
+ const newPanel = container.getInput().panels[newPanelId!];
+ expect(newPanel.type).toEqual(embeddable.type);
+ expect(newPanel.explicitInput.attributes).toBeUndefined();
+ expect(newPanel.explicitInput.savedObjectId).toBe('testSavedObjectId');
+});
diff --git a/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx b/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx
new file mode 100644
index 0000000000000..3cc1a8a1dffe1
--- /dev/null
+++ b/src/plugins/dashboard/public/application/actions/add_to_library_action.tsx
@@ -0,0 +1,93 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import _ from 'lodash';
+import uuid from 'uuid';
+import { ActionByType, IncompatibleActionError } from '../../ui_actions_plugin';
+import { ViewMode, PanelState, IEmbeddable } from '../../embeddable_plugin';
+import {
+ PanelNotFoundError,
+ EmbeddableInput,
+ isReferenceOrValueEmbeddable,
+} from '../../../../embeddable/public';
+import { DashboardPanelState, DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '..';
+
+export const ACTION_ADD_TO_LIBRARY = 'addToFromLibrary';
+
+export interface AddToLibraryActionContext {
+ embeddable: IEmbeddable;
+}
+
+export class AddToLibraryAction implements ActionByType {
+ public readonly type = ACTION_ADD_TO_LIBRARY;
+ public readonly id = ACTION_ADD_TO_LIBRARY;
+ public order = 15;
+
+ constructor() {}
+
+ public getDisplayName({ embeddable }: AddToLibraryActionContext) {
+ if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
+ throw new IncompatibleActionError();
+ }
+ return i18n.translate('dashboard.panel.AddToLibrary', {
+ defaultMessage: 'Add to library',
+ });
+ }
+
+ public getIconType({ embeddable }: AddToLibraryActionContext) {
+ if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
+ throw new IncompatibleActionError();
+ }
+ return 'folderCheck';
+ }
+
+ public async isCompatible({ embeddable }: AddToLibraryActionContext) {
+ return Boolean(
+ embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
+ embeddable.getRoot() &&
+ embeddable.getRoot().isContainer &&
+ embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE &&
+ isReferenceOrValueEmbeddable(embeddable) &&
+ !embeddable.inputIsRefType(embeddable.getInput())
+ );
+ }
+
+ public async execute({ embeddable }: AddToLibraryActionContext) {
+ if (!isReferenceOrValueEmbeddable(embeddable)) {
+ throw new IncompatibleActionError();
+ }
+
+ const newInput = await embeddable.getInputAsRefType();
+
+ embeddable.updateInput(newInput);
+
+ const dashboard = embeddable.getRoot() as DashboardContainer;
+ const panelToReplace = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
+ if (!panelToReplace) {
+ throw new PanelNotFoundError();
+ }
+
+ const newPanel: PanelState = {
+ type: embeddable.type,
+ explicitInput: { ...newInput, id: uuid.v4() },
+ };
+ dashboard.replacePanel(panelToReplace, newPanel);
+ }
+}
diff --git a/src/plugins/dashboard/public/application/actions/index.ts b/src/plugins/dashboard/public/application/actions/index.ts
index be183976c676f..4343a3409b696 100644
--- a/src/plugins/dashboard/public/application/actions/index.ts
+++ b/src/plugins/dashboard/public/application/actions/index.ts
@@ -33,7 +33,12 @@ export {
ACTION_CLONE_PANEL,
} from './clone_panel_action';
export {
+ AddToLibraryAction,
+ AddToLibraryActionContext,
+ ACTION_ADD_TO_LIBRARY,
+} from './add_to_library_action';
+export {
+ UnlinkFromLibraryAction,
UnlinkFromLibraryActionContext,
ACTION_UNLINK_FROM_LIBRARY,
- UnlinkFromLibraryAction,
} from './unlink_from_library_action';
diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx b/src/plugins/dashboard/public/attribute_service/attribute_service.tsx
index c2f529fe399f3..fe5f6a0c8e2bd 100644
--- a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx
+++ b/src/plugins/dashboard/public/attribute_service/attribute_service.tsx
@@ -30,13 +30,21 @@ import {
SimpleSavedObject,
I18nStart,
NotificationsStart,
+ OverlayStart,
} from '../../../../core/public';
import {
SavedObjectSaveModal,
showSaveModal,
OnSaveProps,
SaveResult,
+ checkForDuplicateTitle,
} from '../../../saved_objects/public';
+import {
+ EmbeddableStart,
+ EmbeddableFactory,
+ EmbeddableFactoryNotFoundError,
+ Container,
+} from '../../../embeddable/public';
/**
* The attribute service is a shared, generic service that embeddables can use to provide the functionality
@@ -49,12 +57,22 @@ export class AttributeService<
ValType extends EmbeddableInput & { attributes: SavedObjectAttributes },
RefType extends SavedObjectEmbeddableInput
> {
+ private embeddableFactory: EmbeddableFactory;
+
constructor(
private type: string,
private savedObjectsClient: SavedObjectsClientContract,
+ private overlays: OverlayStart,
private i18nContext: I18nStart['Context'],
- private toasts: NotificationsStart['toasts']
- ) {}
+ private toasts: NotificationsStart['toasts'],
+ getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']
+ ) {
+ const factory = getEmbeddableFactory(this.type);
+ if (!factory) {
+ throw new EmbeddableFactoryNotFoundError(this.type);
+ }
+ this.embeddableFactory = factory;
+ }
public async unwrapAttributes(input: RefType | ValType): Promise {
if (this.inputIsRefType(input)) {
@@ -105,6 +123,15 @@ export class AttributeService<
return isSavedObjectEmbeddableInput(input);
};
+ public getExplicitInputFromEmbeddable(embeddable: IEmbeddable): ValType | RefType {
+ return embeddable.getRoot() &&
+ (embeddable.getRoot() as Container).getInput().panels[embeddable.id].explicitInput
+ ? ((embeddable.getRoot() as Container).getInput().panels[embeddable.id].explicitInput as
+ | ValType
+ | RefType)
+ : (embeddable.getInput() as ValType | RefType);
+ }
+
getInputAsValueType = async (input: ValType | RefType): Promise => {
if (!this.inputIsRefType(input)) {
return input;
@@ -124,16 +151,31 @@ export class AttributeService<
if (this.inputIsRefType(input)) {
return input;
}
-
return new Promise((resolve, reject) => {
const onSave = async (props: OnSaveProps): Promise => {
+ await checkForDuplicateTitle(
+ {
+ title: props.newTitle,
+ copyOnSave: false,
+ lastSavedTitle: '',
+ getEsType: () => this.type,
+ getDisplayName: this.embeddableFactory.getDisplayName,
+ },
+ props.isTitleDuplicateConfirmed,
+ props.onTitleDuplicate,
+ {
+ savedObjectsClient: this.savedObjectsClient,
+ overlays: this.overlays,
+ }
+ );
try {
- input.attributes.title = props.newTitle;
- const wrappedInput = (await this.wrapAttributes(input.attributes, true)) as RefType;
+ const newAttributes = { ...input.attributes };
+ newAttributes.title = props.newTitle;
+ const wrappedInput = (await this.wrapAttributes(newAttributes, true)) as RefType;
resolve(wrappedInput);
return { id: wrappedInput.savedObjectId };
} catch (error) {
- reject();
+ reject(error);
return { error };
}
};
diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx
index 2a36f2d801850..a788b06f91905 100644
--- a/src/plugins/dashboard/public/plugin.tsx
+++ b/src/plugins/dashboard/public/plugin.tsx
@@ -95,6 +95,11 @@ import { addEmbeddableToDashboardUrl } from './url_utils/url_helper';
import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder';
import { UrlGeneratorState } from '../../share/public';
import { AttributeService } from '.';
+import {
+ AddToLibraryAction,
+ ACTION_ADD_TO_LIBRARY,
+ AddToLibraryActionContext,
+} from './application/actions/add_to_library_action';
declare module '../../share/public' {
export interface UrlGeneratorStateMapping {
@@ -155,6 +160,7 @@ declare module '../../../plugins/ui_actions/public' {
[ACTION_EXPAND_PANEL]: ExpandPanelActionContext;
[ACTION_REPLACE_PANEL]: ReplacePanelActionContext;
[ACTION_CLONE_PANEL]: ClonePanelActionContext;
+ [ACTION_ADD_TO_LIBRARY]: AddToLibraryActionContext;
[ACTION_UNLINK_FROM_LIBRARY]: UnlinkFromLibraryActionContext;
}
}
@@ -406,6 +412,7 @@ export class DashboardPlugin
const {
uiActions,
data: { indexPatterns, search },
+ embeddable,
} = plugins;
const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, core.uiSettings);
@@ -424,6 +431,9 @@ export class DashboardPlugin
uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction.id);
if (this.dashboardFeatureFlagConfig?.allowByValueEmbeddables) {
+ const addToLibraryAction = new AddToLibraryAction();
+ uiActions.registerAction(addToLibraryAction);
+ uiActions.attachAction(CONTEXT_MENU_TRIGGER, addToLibraryAction.id);
const unlinkFromLibraryAction = new UnlinkFromLibraryAction();
uiActions.registerAction(unlinkFromLibraryAction);
uiActions.attachAction(CONTEXT_MENU_TRIGGER, unlinkFromLibraryAction.id);
@@ -452,8 +462,10 @@ export class DashboardPlugin
new AttributeService(
type,
core.savedObjects.client,
+ core.overlays,
core.i18n.Context,
- core.notifications.toasts
+ core.notifications.toasts,
+ embeddable.getEmbeddableFactory
),
};
}
diff --git a/src/plugins/data/common/es_query/es_query/get_es_query_config.ts b/src/plugins/data/common/es_query/es_query/get_es_query_config.ts
index ff8fc5b11b26e..87dbbcee03b81 100644
--- a/src/plugins/data/common/es_query/es_query/get_es_query_config.ts
+++ b/src/plugins/data/common/es_query/es_query/get_es_query_config.ts
@@ -18,10 +18,10 @@
*/
import { EsQueryConfig } from './build_es_query';
-import { UI_SETTINGS } from '../../';
+import { GetConfigFn, UI_SETTINGS } from '../../';
interface KibanaConfig {
- get(key: string): T;
+ get: GetConfigFn;
}
export function getEsQueryConfig(config: KibanaConfig) {
diff --git a/src/plugins/data/common/field_formats/converters/date_nanos_shared.ts b/src/plugins/data/common/field_formats/converters/date_nanos_shared.ts
index 89a63243c76f0..7a225cd7d1ae7 100644
--- a/src/plugins/data/common/field_formats/converters/date_nanos_shared.ts
+++ b/src/plugins/data/common/field_formats/converters/date_nanos_shared.ts
@@ -20,7 +20,8 @@
import { i18n } from '@kbn/i18n';
import { memoize, noop } from 'lodash';
import moment, { Moment } from 'moment';
-import { FieldFormat, FIELD_FORMAT_IDS, KBN_FIELD_TYPES, TextContextTypeConvert } from '../../';
+import { FieldFormat, FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../';
+import { TextContextTypeConvert } from '../types';
/**
* Analyse the given moment.js format pattern for the fractional sec part (S,SS,SSS...)
diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/data/common/field_formats/field_formats_registry.ts
index 4b847ebc358d7..32f9f37b9ba53 100644
--- a/src/plugins/data/common/field_formats/field_formats_registry.ts
+++ b/src/plugins/data/common/field_formats/field_formats_registry.ts
@@ -32,7 +32,7 @@ import {
import { baseFormatters } from './constants/base_formatters';
import { FieldFormat } from './field_format';
import { SerializedFieldFormat } from '../../../expressions/common/types';
-import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../types';
+import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../kbn_field_types/types';
import { UI_SETTINGS } from '../constants';
export class FieldFormatsRegistry {
diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts
index 6f773378de08d..daa44b2b0f85b 100644
--- a/src/plugins/data/common/field_formats/types.ts
+++ b/src/plugins/data/common/field_formats/types.ts
@@ -16,6 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+
+import { GetConfigFn } from '../types';
import { FieldFormat } from './field_format';
import { FieldFormatsRegistry } from './field_formats_registry';
@@ -72,7 +74,7 @@ export interface FieldFormatConfig {
es?: boolean;
}
-export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T;
+export type FieldFormatsGetConfigFn = GetConfigFn;
export type IFieldFormat = PublicMethodsOf;
diff --git a/src/plugins/data/common/search/aggs/aggs_service.ts b/src/plugins/data/common/search/aggs/aggs_service.ts
index 59c54fcce6838..6f3e3904dbbd5 100644
--- a/src/plugins/data/common/search/aggs/aggs_service.ts
+++ b/src/plugins/data/common/search/aggs/aggs_service.ts
@@ -19,6 +19,7 @@
import { ExpressionsServiceSetup } from 'src/plugins/expressions/common';
import { UI_SETTINGS } from '../../../common';
+import { GetConfigFn } from '../../types';
import {
AggConfigs,
AggTypesRegistry,
@@ -48,7 +49,7 @@ export interface AggsCommonSetupDependencies {
/** @internal */
export interface AggsCommonStartDependencies {
- getConfig: (key: string) => T;
+ getConfig: GetConfigFn;
}
/**
diff --git a/src/plugins/data/common/types.ts b/src/plugins/data/common/types.ts
index e2ec1a031b0ca..a1e1252b3112f 100644
--- a/src/plugins/data/common/types.ts
+++ b/src/plugins/data/common/types.ts
@@ -20,4 +20,15 @@
export * from './query/types';
export * from './kbn_field_types/types';
export * from './index_patterns/types';
-export { TextContextTypeConvert, IFieldFormatMetaParams } from './field_formats/types';
+
+/**
+ * If a service is being shared on both the client and the server, and
+ * the client code requires synchronous access to uiSettings, both client
+ * and server should wrap the core uiSettings services in a function
+ * matching this signature.
+ *
+ * This matches the signature of the public `core.uiSettings.get`, and
+ * should only be used in scenarios where async access to uiSettings is
+ * not possible.
+ */
+export type GetConfigFn = (key: string, defaultOverride?: T) => T;
diff --git a/src/plugins/data/public/field_formats/converters/date.ts b/src/plugins/data/public/field_formats/converters/date.ts
index 78ef8b293e8b9..8070633ef2dfe 100644
--- a/src/plugins/data/public/field_formats/converters/date.ts
+++ b/src/plugins/data/public/field_formats/converters/date.ts
@@ -20,12 +20,8 @@
import { i18n } from '@kbn/i18n';
import { memoize, noop } from 'lodash';
import moment from 'moment';
-import {
- FieldFormat,
- KBN_FIELD_TYPES,
- TextContextTypeConvert,
- FIELD_FORMAT_IDS,
-} from '../../../common';
+import { FieldFormat, KBN_FIELD_TYPES, FIELD_FORMAT_IDS } from '../../../common';
+import { TextContextTypeConvert } from '../../../common/field_formats/types';
export class DateFormat extends FieldFormat {
static id = FIELD_FORMAT_IDS.DATE;
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index 564c571b6ccd6..ee0b0714febc0 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -48,9 +48,7 @@ import {
} from './index_patterns';
import {
setFieldFormats,
- setHttp,
setIndexPatterns,
- setInjectedMetadata,
setNotifications,
setOverlays,
setQueryService,
@@ -164,11 +162,9 @@ export class DataPublicPlugin
public start(core: CoreStart, { uiActions }: DataStartDependencies): DataPublicPluginStart {
const { uiSettings, http, notifications, savedObjects, overlays, application } = core;
- setHttp(http);
setNotifications(notifications);
setOverlays(overlays);
setUiSettings(uiSettings);
- setInjectedMetadata(core.injectedMetadata);
const fieldFormats = this.fieldFormatsService.start();
setFieldFormats(fieldFormats);
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 7041bcdfa4221..7defddb8f570a 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -34,7 +34,6 @@ import { InjectedIntl } from '@kbn/i18n/react';
import { ISearchSource as ISearchSource_2 } from 'src/plugins/data/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { IUiSettingsClient } from 'src/core/public';
-import { IUiSettingsClient as IUiSettingsClient_3 } from 'kibana/public';
import { KibanaClient } from '@elastic/elasticsearch/api/kibana';
import { KibanaConfigType } from 'src/core/server/kibana_config';
import { Location } from 'history';
@@ -594,10 +593,11 @@ export const fieldFormats: {
// @public (undocumented)
export type FieldFormatsContentType = 'html' | 'text';
+// Warning: (ae-forgotten-export) The symbol "GetConfigFn" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "FieldFormatsGetConfigFn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T;
+export type FieldFormatsGetConfigFn = GetConfigFn;
// Warning: (ae-missing-release-tag) "FieldList" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -727,12 +727,11 @@ export function getEsPreference(uiSettings: IUiSettingsClient_2, sessionId?: str
export const getKbnTypeNames: () => string[];
// Warning: (ae-forgotten-export) The symbol "ISearchRequestParams" needs to be exported by the entry point index.d.ts
-// Warning: (ae-missing-release-tag) "getSearchParamsFromRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function getSearchParamsFromRequest(searchRequest: SearchRequest, dependencies: {
- injectedMetadata: CoreStart['injectedMetadata'];
- uiSettings: IUiSettingsClient_3;
+ esShardTimeout: number;
+ getConfig: GetConfigFn;
}): ISearchRequestParams;
// Warning: (ae-missing-release-tag) "getTime" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
diff --git a/src/plugins/data/public/search/collectors/create_usage_collector.test.ts b/src/plugins/data/public/search/collectors/create_usage_collector.test.ts
index aaaac5ae6ff7c..315d4678cabf1 100644
--- a/src/plugins/data/public/search/collectors/create_usage_collector.test.ts
+++ b/src/plugins/data/public/search/collectors/create_usage_collector.test.ts
@@ -42,7 +42,7 @@ describe('Search Usage Collector', () => {
{} as any,
]);
mockUsageCollectionSetup = usageCollectionPluginMock.createSetupContract();
- usageCollector = createUsageCollector(mockCoreSetup, mockUsageCollectionSetup);
+ usageCollector = createUsageCollector(mockCoreSetup.getStartServices, mockUsageCollectionSetup);
});
test('tracks query timeouts', async () => {
diff --git a/src/plugins/data/public/search/collectors/create_usage_collector.ts b/src/plugins/data/public/search/collectors/create_usage_collector.ts
index 7adb0c3caa675..321b2c5b99049 100644
--- a/src/plugins/data/public/search/collectors/create_usage_collector.ts
+++ b/src/plugins/data/public/search/collectors/create_usage_collector.ts
@@ -18,16 +18,16 @@
*/
import { first } from 'rxjs/operators';
-import { CoreSetup } from '../../../../../core/public';
+import { StartServicesAccessor } from '../../../../../core/public';
import { METRIC_TYPE, UsageCollectionSetup } from '../../../../usage_collection/public';
import { SEARCH_EVENT_TYPE, SearchUsageCollector } from './types';
export const createUsageCollector = (
- core: CoreSetup,
+ getStartServices: StartServicesAccessor,
usageCollection?: UsageCollectionSetup
): SearchUsageCollector => {
const getCurrentApp = async () => {
- const [{ application }] = await core.getStartServices();
+ const [{ application }] = await getStartServices();
return application.currentAppId$.pipe(first()).toPromise();
};
diff --git a/src/plugins/data/public/search/fetch/get_search_params.test.ts b/src/plugins/data/public/search/fetch/get_search_params.test.ts
index f9b62fdd4fc61..1ecb879b1602d 100644
--- a/src/plugins/data/public/search/fetch/get_search_params.test.ts
+++ b/src/plugins/data/public/search/fetch/get_search_params.test.ts
@@ -18,13 +18,10 @@
*/
import { getSearchParams } from './get_search_params';
-import { IUiSettingsClient } from 'kibana/public';
-import { UI_SETTINGS } from '../../../common';
+import { GetConfigFn, UI_SETTINGS } from '../../../common';
-function getConfigStub(config: any = {}) {
- return {
- get: (key) => config[key],
- } as IUiSettingsClient;
+function getConfigStub(config: any = {}): GetConfigFn {
+ return (key) => config[key];
}
describe('getSearchParams', () => {
diff --git a/src/plugins/data/public/search/fetch/get_search_params.ts b/src/plugins/data/public/search/fetch/get_search_params.ts
index 3246156b6b37e..5e0395189f647 100644
--- a/src/plugins/data/public/search/fetch/get_search_params.ts
+++ b/src/plugins/data/public/search/fetch/get_search_params.ts
@@ -17,37 +17,36 @@
* under the License.
*/
-import { IUiSettingsClient, CoreStart } from 'kibana/public';
-import { UI_SETTINGS, ISearchRequestParams } from '../../../common';
+import { UI_SETTINGS, ISearchRequestParams, GetConfigFn } from '../../../common';
import { SearchRequest } from './types';
const sessionId = Date.now();
-export function getSearchParams(config: IUiSettingsClient, esShardTimeout: number = 0) {
+export function getSearchParams(getConfig: GetConfigFn, esShardTimeout: number = 0) {
return {
rest_total_hits_as_int: true,
ignore_unavailable: true,
- ignore_throttled: getIgnoreThrottled(config),
- max_concurrent_shard_requests: getMaxConcurrentShardRequests(config),
- preference: getPreference(config),
+ ignore_throttled: getIgnoreThrottled(getConfig),
+ max_concurrent_shard_requests: getMaxConcurrentShardRequests(getConfig),
+ preference: getPreference(getConfig),
timeout: getTimeout(esShardTimeout),
};
}
-export function getIgnoreThrottled(config: IUiSettingsClient) {
- return !config.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN);
+export function getIgnoreThrottled(getConfig: GetConfigFn) {
+ return !getConfig(UI_SETTINGS.SEARCH_INCLUDE_FROZEN);
}
-export function getMaxConcurrentShardRequests(config: IUiSettingsClient) {
- const maxConcurrentShardRequests = config.get(UI_SETTINGS.COURIER_MAX_CONCURRENT_SHARD_REQUESTS);
+export function getMaxConcurrentShardRequests(getConfig: GetConfigFn) {
+ const maxConcurrentShardRequests = getConfig(UI_SETTINGS.COURIER_MAX_CONCURRENT_SHARD_REQUESTS);
return maxConcurrentShardRequests > 0 ? maxConcurrentShardRequests : undefined;
}
-export function getPreference(config: IUiSettingsClient) {
- const setRequestPreference = config.get(UI_SETTINGS.COURIER_SET_REQUEST_PREFERENCE);
+export function getPreference(getConfig: GetConfigFn) {
+ const setRequestPreference = getConfig(UI_SETTINGS.COURIER_SET_REQUEST_PREFERENCE);
if (setRequestPreference === 'sessionId') return sessionId;
return setRequestPreference === 'custom'
- ? config.get(UI_SETTINGS.COURIER_CUSTOM_REQUEST_PREFERENCE)
+ ? getConfig(UI_SETTINGS.COURIER_CUSTOM_REQUEST_PREFERENCE)
: undefined;
}
@@ -55,13 +54,15 @@ export function getTimeout(esShardTimeout: number) {
return esShardTimeout > 0 ? `${esShardTimeout}ms` : undefined;
}
+/** @public */
+// TODO: Could provide this on runtime contract with dependencies
+// already wired up.
export function getSearchParamsFromRequest(
searchRequest: SearchRequest,
- dependencies: { injectedMetadata: CoreStart['injectedMetadata']; uiSettings: IUiSettingsClient }
+ dependencies: { esShardTimeout: number; getConfig: GetConfigFn }
): ISearchRequestParams {
- const { injectedMetadata, uiSettings } = dependencies;
- const esShardTimeout = injectedMetadata.getInjectedVar('esShardTimeout') as number;
- const searchParams = getSearchParams(uiSettings, esShardTimeout);
+ const { esShardTimeout, getConfig } = dependencies;
+ const searchParams = getSearchParams(getConfig, esShardTimeout);
return {
index: searchRequest.index.title || searchRequest.index,
diff --git a/src/plugins/data/public/search/fetch/types.ts b/src/plugins/data/public/search/fetch/types.ts
index dda66d6b5238d..18d277204815b 100644
--- a/src/plugins/data/public/search/fetch/types.ts
+++ b/src/plugins/data/public/search/fetch/types.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { IUiSettingsClient } from '../../../../../core/public';
+import { GetConfigFn } from '../../../common';
import { ISearchStartLegacy } from '../types';
export type SearchRequest = any;
@@ -30,7 +30,7 @@ export interface FetchOptions {
export interface FetchHandlers {
legacySearchService: ISearchStartLegacy;
- config: IUiSettingsClient;
+ config: { get: GetConfigFn };
esShardTimeout: number;
}
diff --git a/src/plugins/data/public/search/legacy/default_search_strategy.ts b/src/plugins/data/public/search/legacy/default_search_strategy.ts
index 284768bc5a1cc..6ccb0a01cf898 100644
--- a/src/plugins/data/public/search/legacy/default_search_strategy.ts
+++ b/src/plugins/data/public/search/legacy/default_search_strategy.ts
@@ -42,7 +42,7 @@ function msearch({
index: index.title || index,
search_type: searchType,
ignore_unavailable: true,
- preference: getPreference(config),
+ preference: getPreference(config.get),
};
const inlineBody = {
...body,
@@ -52,7 +52,7 @@ function msearch({
});
const searching = es.msearch({
- ...getMSearchParams(config),
+ ...getMSearchParams(config.get),
body: `${inlineRequests.join('\n')}\n`,
});
diff --git a/src/plugins/data/public/search/legacy/es_client/get_es_client.ts b/src/plugins/data/public/search/legacy/es_client/get_es_client.ts
index 93d9d24920271..4367544ad9ff4 100644
--- a/src/plugins/data/public/search/legacy/es_client/get_es_client.ts
+++ b/src/plugins/data/public/search/legacy/es_client/get_es_client.ts
@@ -22,17 +22,20 @@ import { default as es } from 'elasticsearch-browser/elasticsearch';
import { CoreStart, PackageInfo } from 'kibana/public';
import { BehaviorSubject } from 'rxjs';
-export function getEsClient(
- injectedMetadata: CoreStart['injectedMetadata'],
- http: CoreStart['http'],
- packageInfo: PackageInfo
-) {
- const esRequestTimeout = injectedMetadata.getInjectedVar('esRequestTimeout') as number;
- const esApiVersion = injectedMetadata.getInjectedVar('esApiVersion') as string;
-
+export function getEsClient({
+ esRequestTimeout,
+ esApiVersion,
+ http,
+ packageVersion,
+}: {
+ esRequestTimeout: number;
+ esApiVersion: string;
+ http: CoreStart['http'];
+ packageVersion: PackageInfo['version'];
+}) {
// Use legacy es client for msearch.
const client = es.Client({
- host: getEsUrl(http, packageInfo),
+ host: getEsUrl(http, packageVersion),
log: 'info',
requestTimeout: esRequestTimeout,
apiVersion: esApiVersion,
@@ -78,7 +81,7 @@ function wrapEsClientMethod(esClient: any, method: string, loadingCount$: Behavi
};
}
-function getEsUrl(http: CoreStart['http'], packageInfo: PackageInfo) {
+function getEsUrl(http: CoreStart['http'], packageVersion: PackageInfo['version']) {
const a = document.createElement('a');
a.href = http.basePath.prepend('/elasticsearch');
const protocolPort = /https/.test(a.protocol) ? 443 : 80;
@@ -89,7 +92,7 @@ function getEsUrl(http: CoreStart['http'], packageInfo: PackageInfo) {
protocol: a.protocol,
pathname: a.pathname,
headers: {
- 'kbn-version': packageInfo.version,
+ 'kbn-version': packageVersion,
},
};
}
diff --git a/src/plugins/data/public/search/legacy/fetch_soon.test.ts b/src/plugins/data/public/search/legacy/fetch_soon.test.ts
index 61d3568350b6b..d375398af1add 100644
--- a/src/plugins/data/public/search/legacy/fetch_soon.test.ts
+++ b/src/plugins/data/public/search/legacy/fetch_soon.test.ts
@@ -19,15 +19,12 @@
import { fetchSoon } from './fetch_soon';
import { callClient } from './call_client';
-import { IUiSettingsClient } from 'kibana/public';
import { FetchHandlers, FetchOptions } from '../fetch/types';
import { SearchRequest, SearchResponse } from '../index';
-import { UI_SETTINGS } from '../../../common';
+import { GetConfigFn, UI_SETTINGS } from '../../../common';
-function getConfigStub(config: any = {}) {
- return {
- get: (key) => config[key],
- } as IUiSettingsClient;
+function getConfigStub(config: any = {}): GetConfigFn {
+ return (key) => config[key];
}
const mockResponses: Record = {
@@ -60,9 +57,9 @@ describe('fetchSoon', () => {
});
test('should execute asap if config is set to not batch searches', () => {
- const config = getConfigStub({
- [UI_SETTINGS.COURIER_BATCH_SEARCHES]: false,
- });
+ const config = {
+ get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: false }),
+ };
const request = {};
const options = {};
@@ -72,9 +69,9 @@ describe('fetchSoon', () => {
});
test('should delay by 50ms if config is set to batch searches', () => {
- const config = getConfigStub({
- [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true,
- });
+ const config = {
+ get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }),
+ };
const request = {};
const options = {};
@@ -88,9 +85,9 @@ describe('fetchSoon', () => {
});
test('should send a batch of requests to callClient', () => {
- const config = getConfigStub({
- [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true,
- });
+ const config = {
+ get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }),
+ };
const requests = [{ foo: 1 }, { foo: 2 }];
const options = [{ bar: 1 }, { bar: 2 }];
@@ -105,9 +102,9 @@ describe('fetchSoon', () => {
});
test('should return the response to the corresponding call for multiple batched requests', async () => {
- const config = getConfigStub({
- [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true,
- });
+ const config = {
+ get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }),
+ };
const requests = [{ _mockResponseId: 'foo' }, { _mockResponseId: 'bar' }];
const promises = requests.map((request) => {
@@ -120,9 +117,9 @@ describe('fetchSoon', () => {
});
test('should wait for the previous batch to start before starting a new batch', () => {
- const config = getConfigStub({
- [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true,
- });
+ const config = {
+ get: getConfigStub({ [UI_SETTINGS.COURIER_BATCH_SEARCHES]: true }),
+ };
const firstBatch = [{ foo: 1 }, { foo: 2 }];
const secondBatch = [{ bar: 1 }, { bar: 2 }];
diff --git a/src/plugins/data/public/search/legacy/get_msearch_params.test.ts b/src/plugins/data/public/search/legacy/get_msearch_params.test.ts
index dc61e19406631..d3206950174c8 100644
--- a/src/plugins/data/public/search/legacy/get_msearch_params.test.ts
+++ b/src/plugins/data/public/search/legacy/get_msearch_params.test.ts
@@ -18,13 +18,10 @@
*/
import { getMSearchParams } from './get_msearch_params';
-import { IUiSettingsClient } from '../../../../../core/public';
-import { UI_SETTINGS } from '../../../common';
+import { GetConfigFn, UI_SETTINGS } from '../../../common';
-function getConfigStub(config: any = {}) {
- return {
- get: (key) => config[key],
- } as IUiSettingsClient;
+function getConfigStub(config: any = {}): GetConfigFn {
+ return (key) => config[key];
}
describe('getMSearchParams', () => {
diff --git a/src/plugins/data/public/search/legacy/get_msearch_params.ts b/src/plugins/data/public/search/legacy/get_msearch_params.ts
index 48d13903c972f..c4f77b25078cd 100644
--- a/src/plugins/data/public/search/legacy/get_msearch_params.ts
+++ b/src/plugins/data/public/search/legacy/get_msearch_params.ts
@@ -17,13 +17,13 @@
* under the License.
*/
-import { IUiSettingsClient } from 'kibana/public';
+import { GetConfigFn } from '../../../common';
import { getIgnoreThrottled, getMaxConcurrentShardRequests } from '../fetch';
-export function getMSearchParams(config: IUiSettingsClient) {
+export function getMSearchParams(getConfig: GetConfigFn) {
return {
rest_total_hits_as_int: true,
- ignore_throttled: getIgnoreThrottled(config),
- max_concurrent_shard_requests: getMaxConcurrentShardRequests(config),
+ ignore_throttled: getIgnoreThrottled(getConfig),
+ max_concurrent_shard_requests: getMaxConcurrentShardRequests(getConfig),
};
}
diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts
index 4360a0caa7cde..738f1e8ffbca0 100644
--- a/src/plugins/data/public/search/search_service.test.ts
+++ b/src/plugins/data/public/search/search_service.test.ts
@@ -52,6 +52,7 @@ describe('Search service', () => {
} as any);
expect(start).toHaveProperty('aggs');
expect(start).toHaveProperty('search');
+ expect(start).toHaveProperty('searchSource');
});
});
});
diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts
index 04e1a46c84652..a65b2b4b71f20 100644
--- a/src/plugins/data/public/search/search_service.ts
+++ b/src/plugins/data/public/search/search_service.ts
@@ -51,11 +51,22 @@ export class SearchService implements Plugin {
private usageCollector?: SearchUsageCollector;
public setup(
- core: CoreSetup,
- { packageInfo, usageCollection, expressions }: SearchServiceSetupDependencies
+ { http, getStartServices, injectedMetadata, notifications, uiSettings }: CoreSetup,
+ { expressions, packageInfo, usageCollection }: SearchServiceSetupDependencies
): ISearchSetup {
- this.usageCollector = createUsageCollector(core, usageCollection);
- this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo);
+ const esApiVersion = injectedMetadata.getInjectedVar('esApiVersion') as string;
+ const esRequestTimeout = injectedMetadata.getInjectedVar('esRequestTimeout') as number;
+ const packageVersion = packageInfo.version;
+
+ this.usageCollector = createUsageCollector(getStartServices, usageCollection);
+
+ this.esClient = getEsClient({
+ esRequestTimeout,
+ esApiVersion,
+ http,
+ packageVersion,
+ });
+
/**
* A global object that intercepts all searches and provides convenience methods for cancelling
* all pending search requests, as well as getting the number of pending search requests.
@@ -64,13 +75,13 @@ export class SearchService implements Plugin {
*/
this.searchInterceptor = new SearchInterceptor(
{
- toasts: core.notifications.toasts,
- http: core.http,
- uiSettings: core.uiSettings,
- startServices: core.getStartServices(),
+ toasts: notifications.toasts,
+ http,
+ uiSettings,
+ startServices: getStartServices(),
usageCollector: this.usageCollector!,
},
- core.injectedMetadata.getInjectedVar('esRequestTimeout') as number
+ esRequestTimeout
);
expressions.registerFunction(esdsl);
@@ -79,7 +90,7 @@ export class SearchService implements Plugin {
return {
aggs: this.aggsService.setup({
registerFunction: expressions.registerFunction,
- uiSettings: core.uiSettings,
+ uiSettings,
}),
usageCollector: this.usageCollector!,
__enhance: (enhancements: SearchEnhancements) => {
@@ -101,8 +112,8 @@ export class SearchService implements Plugin {
};
const searchSourceDependencies: SearchSourceDependencies = {
- uiSettings,
- injectedMetadata,
+ getConfig: uiSettings.get.bind(uiSettings),
+ esShardTimeout: injectedMetadata.getInjectedVar('esShardTimeout') as number,
search,
legacySearch,
};
diff --git a/src/plugins/data/public/search/search_source/create_search_source.test.ts b/src/plugins/data/public/search/search_source/create_search_source.test.ts
index 23ab5979595af..56f6ca6c56270 100644
--- a/src/plugins/data/public/search/search_source/create_search_source.test.ts
+++ b/src/plugins/data/public/search/search_source/create_search_source.test.ts
@@ -16,27 +16,28 @@
* specific language governing permissions and limitations
* under the License.
*/
+
import { createSearchSource as createSearchSourceFactory } from './create_search_source';
+import { SearchSourceDependencies } from './search_source';
import { IIndexPattern } from '../../../common/index_patterns';
import { IndexPatternsContract } from '../../index_patterns/index_patterns';
import { Filter } from '../../../common/es_query/filters';
-import { coreMock } from '../../../../../core/public/mocks';
import { dataPluginMock } from '../../mocks';
describe('createSearchSource', () => {
const indexPatternMock: IIndexPattern = {} as IIndexPattern;
let indexPatternContractMock: jest.Mocked;
- let dependencies: any;
+ let dependencies: SearchSourceDependencies;
let createSearchSource: ReturnType;
beforeEach(() => {
- const core = coreMock.createStart();
const data = dataPluginMock.createStartContract();
dependencies = {
- searchService: data.search,
- uiSettings: core.uiSettings,
- injectedMetadata: core.injectedMetadata,
+ getConfig: jest.fn(),
+ search: jest.fn(),
+ legacySearch: data.search.__LEGACY,
+ esShardTimeout: 30000,
};
indexPatternContractMock = ({
diff --git a/src/plugins/data/public/search/search_source/mocks.ts b/src/plugins/data/public/search/search_source/mocks.ts
index cf2d009e41b54..4e1c35557ffa6 100644
--- a/src/plugins/data/public/search/search_source/mocks.ts
+++ b/src/plugins/data/public/search/search_source/mocks.ts
@@ -17,10 +17,7 @@
* under the License.
*/
-import {
- injectedMetadataServiceMock,
- uiSettingsServiceMock,
-} from '../../../../../core/public/mocks';
+import { uiSettingsServiceMock } from '../../../../../core/public/mocks';
import { ISearchSource, SearchSource } from './search_source';
import { SearchSourceFields } from './types';
@@ -54,6 +51,8 @@ export const searchSourceMock = {
export const createSearchSourceMock = (fields?: SearchSourceFields) =>
new SearchSource(fields, {
+ getConfig: uiSettingsServiceMock.createStartContract().get,
+ esShardTimeout: 30000,
search: jest.fn(),
legacySearch: {
esClient: {
@@ -61,6 +60,4 @@ export const createSearchSourceMock = (fields?: SearchSourceFields) =>
msearch: jest.fn(),
},
},
- uiSettings: uiSettingsServiceMock.createStartContract(),
- injectedMetadata: injectedMetadataServiceMock.createStartContract(),
});
diff --git a/src/plugins/data/public/search/search_source/search_source.test.ts b/src/plugins/data/public/search/search_source/search_source.test.ts
index 6d53b8dfc4b4e..2f0fa0765e25a 100644
--- a/src/plugins/data/public/search/search_source/search_source.test.ts
+++ b/src/plugins/data/public/search/search_source/search_source.test.ts
@@ -16,13 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
+
import { Observable } from 'rxjs';
-import { SearchSource } from './search_source';
+import { GetConfigFn } from 'src/plugins/data/common';
+import { SearchSource, SearchSourceDependencies } from './search_source';
import { IndexPattern, SortDirection } from '../..';
import { fetchSoon } from '../legacy';
-import { IUiSettingsClient } from '../../../../../core/public';
import { dataPluginMock } from '../../../../data/public/mocks';
-import { coreMock } from '../../../../../core/public/mocks';
jest.mock('../legacy', () => ({
fetchSoon: jest.fn().mockResolvedValue({}),
@@ -51,10 +51,9 @@ const indexPattern2 = ({
describe('SearchSource', () => {
let mockSearchMethod: any;
- let searchSourceDependencies: any;
+ let searchSourceDependencies: SearchSourceDependencies;
beforeEach(() => {
- const core = coreMock.createStart();
const data = dataPluginMock.createStartContract();
mockSearchMethod = jest.fn(() => {
@@ -69,10 +68,10 @@ describe('SearchSource', () => {
});
searchSourceDependencies = {
+ getConfig: jest.fn(),
search: mockSearchMethod,
legacySearch: data.search.__LEGACY,
- injectedMetadata: core.injectedMetadata,
- uiSettings: core.uiSettings,
+ esShardTimeout: 30000,
};
});
@@ -184,16 +183,11 @@ describe('SearchSource', () => {
describe('#legacy fetch()', () => {
beforeEach(() => {
- const core = coreMock.createStart();
-
searchSourceDependencies = {
...searchSourceDependencies,
- uiSettings: {
- ...core.uiSettings,
- get: jest.fn(() => {
- return true; // batchSearches = true
- }),
- } as IUiSettingsClient,
+ getConfig: jest.fn(() => {
+ return true; // batchSearches = true
+ }) as GetConfigFn,
};
});
diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts
index 847dc8853d6ba..06ad13bfcfdf5 100644
--- a/src/plugins/data/public/search/search_source/search_source.ts
+++ b/src/plugins/data/public/search/search_source/search_source.ts
@@ -72,7 +72,6 @@
import { setWith } from '@elastic/safer-lodash-set';
import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash';
import { map } from 'rxjs/operators';
-import { CoreStart } from 'kibana/public';
import { normalizeSortRequest } from './normalize_sort_request';
import { filterDocvalueFields } from './filter_docvalue_fields';
import { fieldWildcardFilter } from '../../../../kibana_utils/common';
@@ -82,15 +81,32 @@ import { FetchOptions, RequestFailure, handleResponse, getSearchParamsFromReques
import { getEsQueryConfig, buildEsQuery, Filter, UI_SETTINGS } from '../../../common';
import { getHighlightRequest } from '../../../common/field_formats';
+import { GetConfigFn } from '../../../common/types';
import { fetchSoon } from '../legacy';
import { extractReferences } from './extract_references';
import { ISearchStartLegacy } from '../types';
+/** @internal */
+export const searchSourceRequiredUiSettings = [
+ 'dateFormat:tz',
+ UI_SETTINGS.COURIER_BATCH_SEARCHES,
+ UI_SETTINGS.COURIER_CUSTOM_REQUEST_PREFERENCE,
+ UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX,
+ UI_SETTINGS.COURIER_MAX_CONCURRENT_SHARD_REQUESTS,
+ UI_SETTINGS.COURIER_SET_REQUEST_PREFERENCE,
+ UI_SETTINGS.DOC_HIGHLIGHT,
+ UI_SETTINGS.META_FIELDS,
+ UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS,
+ UI_SETTINGS.QUERY_STRING_OPTIONS,
+ UI_SETTINGS.SEARCH_INCLUDE_FROZEN,
+ UI_SETTINGS.SORT_OPTIONS,
+];
+
export interface SearchSourceDependencies {
- uiSettings: CoreStart['uiSettings'];
+ getConfig: GetConfigFn;
search: ISearchGeneric;
legacySearch: ISearchStartLegacy;
- injectedMetadata: CoreStart['injectedMetadata'];
+ esShardTimeout: number;
}
/** @public **/
@@ -204,11 +220,11 @@ export class SearchSource {
* @return {Observable>}
*/
private fetch$(searchRequest: SearchRequest, signal?: AbortSignal) {
- const { search, injectedMetadata, uiSettings } = this.dependencies;
+ const { search, esShardTimeout, getConfig } = this.dependencies;
const params = getSearchParamsFromRequest(searchRequest, {
- injectedMetadata,
- uiSettings,
+ esShardTimeout,
+ getConfig,
});
return search({ params, indexType: searchRequest.indexType }, { signal }).pipe(
@@ -221,8 +237,7 @@ export class SearchSource {
* @return {Promise>}
*/
private async legacyFetch(searchRequest: SearchRequest, options: FetchOptions) {
- const { injectedMetadata, legacySearch, uiSettings } = this.dependencies;
- const esShardTimeout = injectedMetadata.getInjectedVar('esShardTimeout') as number;
+ const { esShardTimeout, legacySearch, getConfig } = this.dependencies;
return await fetchSoon(
searchRequest,
@@ -232,7 +247,7 @@ export class SearchSource {
},
{
legacySearchService: legacySearch,
- config: uiSettings,
+ config: { get: getConfig },
esShardTimeout,
}
);
@@ -243,14 +258,14 @@ export class SearchSource {
* @async
*/
async fetch(options: FetchOptions = {}) {
- const { uiSettings } = this.dependencies;
+ const { getConfig } = this.dependencies;
await this.requestIsStarting(options);
const searchRequest = await this.flatten();
this.history = [searchRequest];
let response;
- if (uiSettings.get(UI_SETTINGS.COURIER_BATCH_SEARCHES)) {
+ if (getConfig(UI_SETTINGS.COURIER_BATCH_SEARCHES)) {
response = await this.legacyFetch(searchRequest, options);
} else {
response = this.fetch$(searchRequest, options.abortSignal).toPromise();
@@ -342,7 +357,7 @@ export class SearchSource {
}
};
- const { uiSettings } = this.dependencies;
+ const { getConfig } = this.dependencies;
switch (key) {
case 'filter':
@@ -364,7 +379,7 @@ export class SearchSource {
const sort = normalizeSortRequest(
val,
this.getField('index'),
- uiSettings.get(UI_SETTINGS.SORT_OPTIONS)
+ getConfig(UI_SETTINGS.SORT_OPTIONS)
);
return addToBody(key, sort);
default:
@@ -418,14 +433,11 @@ export class SearchSource {
body._source = index.getSourceFiltering();
}
- const { uiSettings } = this.dependencies;
+ const { getConfig } = this.dependencies;
if (body._source) {
// exclude source fields for this index pattern specified by the user
- const filter = fieldWildcardFilter(
- body._source.excludes,
- uiSettings.get(UI_SETTINGS.META_FIELDS)
- );
+ const filter = fieldWildcardFilter(body._source.excludes, getConfig(UI_SETTINGS.META_FIELDS));
body.docvalue_fields = body.docvalue_fields.filter((docvalueField: any) =>
filter(docvalueField.field)
);
@@ -445,11 +457,11 @@ export class SearchSource {
);
}
- const esQueryConfigs = getEsQueryConfig(uiSettings);
+ const esQueryConfigs = getEsQueryConfig({ get: getConfig });
body.query = buildEsQuery(index, query, filters, esQueryConfigs);
if (highlightAll && body.query) {
- body.highlight = getHighlightRequest(body.query, uiSettings.get(UI_SETTINGS.DOC_HIGHLIGHT));
+ body.highlight = getHighlightRequest(body.query, getConfig(UI_SETTINGS.DOC_HIGHLIGHT));
delete searchRequest.highlightAll;
}
diff --git a/src/plugins/data/public/services.ts b/src/plugins/data/public/services.ts
index ba0b2de393bde..032bce6d8d2aa 100644
--- a/src/plugins/data/public/services.ts
+++ b/src/plugins/data/public/services.ts
@@ -31,8 +31,6 @@ export const [getUiSettings, setUiSettings] = createGetterSetter('Http');
-
export const [getFieldFormats, setFieldFormats] = createGetterSetter(
'FieldFormats'
);
@@ -47,10 +45,6 @@ export const [getQueryService, setQueryService] = createGetterSetter<
DataPublicPluginStart['query']
>('Query');
-export const [getInjectedMetadata, setInjectedMetadata] = createGetterSetter<
- CoreStart['injectedMetadata']
->('InjectedMetadata');
-
export const [getSearchService, setSearchService] = createGetterSetter<
DataPublicPluginStart['search']
>('Search');
diff --git a/src/plugins/data/server/field_formats/converters/date_nanos_server.ts b/src/plugins/data/server/field_formats/converters/date_nanos_server.ts
index 299b2aac93d49..b99febf0c7e73 100644
--- a/src/plugins/data/server/field_formats/converters/date_nanos_server.ts
+++ b/src/plugins/data/server/field_formats/converters/date_nanos_server.ts
@@ -24,7 +24,7 @@ import {
DateNanosFormat,
formatWithNanos,
} from '../../../common/field_formats/converters/date_nanos_shared';
-import { TextContextTypeConvert } from '../../../common';
+import { TextContextTypeConvert } from '../../../common/field_formats/types';
class DateNanosFormatServer extends DateNanosFormat {
textConvert: TextContextTypeConvert = (val) => {
diff --git a/src/plugins/data/server/field_formats/converters/date_server.ts b/src/plugins/data/server/field_formats/converters/date_server.ts
index 85eb65dfc6a8d..19c4adc90bade 100644
--- a/src/plugins/data/server/field_formats/converters/date_server.ts
+++ b/src/plugins/data/server/field_formats/converters/date_server.ts
@@ -23,11 +23,13 @@ import moment from 'moment-timezone';
import {
FieldFormat,
KBN_FIELD_TYPES,
- TextContextTypeConvert,
FIELD_FORMAT_IDS,
FieldFormatsGetConfigFn,
- IFieldFormatMetaParams,
} from '../../../common';
+import {
+ IFieldFormatMetaParams,
+ TextContextTypeConvert,
+} from '../../../common/field_formats/types';
export class DateFormat extends FieldFormat {
static id = FIELD_FORMAT_IDS.DATE;
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index e259a7878398d..f0a0d2763ff23 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -419,10 +419,11 @@ export const fieldFormats: {
TruncateFormat: typeof TruncateFormat;
};
+// Warning: (ae-forgotten-export) The symbol "GetConfigFn" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "FieldFormatsGetConfigFn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export type FieldFormatsGetConfigFn = (key: string, defaultOverride?: T) => T;
+export type FieldFormatsGetConfigFn = GetConfigFn;
// Warning: (ae-missing-release-tag) "Filter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
diff --git a/src/plugins/discover/public/application/angular/directives/render_complete.ts b/src/plugins/discover/public/application/angular/directives/render_complete.ts
index 635cf68f12fcb..72f97138790b2 100644
--- a/src/plugins/discover/public/application/angular/directives/render_complete.ts
+++ b/src/plugins/discover/public/application/angular/directives/render_complete.ts
@@ -17,14 +17,14 @@
* under the License.
*/
import { IScope } from 'angular';
-import { RenderCompleteHelper } from '../../../../../kibana_utils/public';
+import { RenderCompleteListener } from '../../../../../kibana_utils/public';
export function createRenderCompleteDirective() {
return {
controller($scope: IScope, $element: JQLite) {
const el = $element[0];
- const renderCompleteHelper = new RenderCompleteHelper(el);
- $scope.$on('$destroy', renderCompleteHelper.destroy);
+ const renderCompleteListener = new RenderCompleteListener(el);
+ $scope.$on('$destroy', renderCompleteListener.destroy);
},
};
}
diff --git a/src/plugins/embeddable/kibana.json b/src/plugins/embeddable/kibana.json
index c9694ad7b9423..6a8e6079232aa 100644
--- a/src/plugins/embeddable/kibana.json
+++ b/src/plugins/embeddable/kibana.json
@@ -12,6 +12,7 @@
],
"requiredBundles": [
"savedObjects",
- "kibanaReact"
+ "kibanaReact",
+ "kibanaUtils"
]
}
diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx
index fcecf117d7d52..ffe8a5bf6e7dc 100644
--- a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx
+++ b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx
@@ -19,6 +19,8 @@
import { cloneDeep, isEqual } from 'lodash';
import * as Rx from 'rxjs';
+import { distinctUntilChanged, map } from 'rxjs/operators';
+import { RenderCompleteDispatcher } from '../../../../kibana_utils/public';
import { Adapters, ViewMode } from '../types';
import { IContainer } from '../containers';
import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable';
@@ -47,6 +49,8 @@ export abstract class Embeddable<
private readonly input$: Rx.BehaviorSubject;
private readonly output$: Rx.BehaviorSubject;
+ protected renderComplete = new RenderCompleteDispatcher();
+
// Listener to parent changes, if this embeddable exists in a parent, in order
// to update input when the parent changes.
private parentSubscription?: Rx.Subscription;
@@ -77,6 +81,15 @@ export abstract class Embeddable<
this.onResetInput(newInput);
});
}
+
+ this.getOutput$()
+ .pipe(
+ map(({ title }) => title || ''),
+ distinctUntilChanged()
+ )
+ .subscribe((title) => {
+ this.renderComplete.setTitle(title);
+ });
}
public getIsContainer(): this is IContainer {
@@ -105,8 +118,8 @@ export abstract class Embeddable<
return this.input;
}
- public getTitle() {
- return this.output.title;
+ public getTitle(): string {
+ return this.output.title || '';
}
/**
@@ -133,7 +146,10 @@ export abstract class Embeddable<
}
}
- public render(domNode: HTMLElement | Element): void {
+ public render(el: HTMLElement): void {
+ this.renderComplete.setEl(el);
+ this.renderComplete.setTitle(this.output.title || '');
+
if (this.destroyed) {
throw new Error('Embeddable has been destroyed');
}
diff --git a/src/plugins/embeddable/public/mocks.tsx b/src/plugins/embeddable/public/mocks.tsx
index 7ec03ba659cda..2064236e9ae7f 100644
--- a/src/plugins/embeddable/public/mocks.tsx
+++ b/src/plugins/embeddable/public/mocks.tsx
@@ -25,6 +25,9 @@ import {
EmbeddableStateTransfer,
IEmbeddable,
EmbeddablePanel,
+ EmbeddableInput,
+ SavedObjectEmbeddableInput,
+ ReferenceOrValueEmbeddable,
} from '.';
import { EmbeddablePublicPlugin } from './plugin';
import { coreMock } from '../../../core/public/mocks';
@@ -35,7 +38,6 @@ import { dataPluginMock } from '../../data/public/mocks';
import { inspectorPluginMock } from '../../inspector/public/mocks';
import { uiActionsPluginMock } from '../../ui_actions/public/mocks';
-import { SavedObjectEmbeddableInput, ReferenceOrValueEmbeddable, EmbeddableInput } from './lib';
export type Setup = jest.Mocked;
export type Start = jest.Mocked;
diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts b/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts
index 419e80ad1608f..8c66a87adbaa1 100644
--- a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts
+++ b/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts
@@ -23,6 +23,7 @@ import collapsingTests from './utils_string_collapsing.txt';
import expandingTests from './utils_string_expanding.txt';
import * as utils from '../index';
+import { extractJSONStringValues } from '../parser';
describe('JSON to XJSON conversion tools', () => {
it('will collapse multiline strings', () => {
@@ -34,6 +35,32 @@ describe('JSON to XJSON conversion tools', () => {
const multiline = '{ "foo": """bar\r\nbaz""" }';
expect(utils.collapseLiteralStrings(multiline)).toEqual('{ "foo": "bar\\r\\nbaz" }');
});
+
+ describe('JSON string values parser', () => {
+ test('correctly extracts JSON string values', () => {
+ const json = {
+ myString: 'string',
+ notAString: 1,
+ myStringArray: ['a', 1, 'test', { nestedString: 'string' }],
+ };
+ const jsonString = JSON.stringify(json);
+ const { stringValues } = extractJSONStringValues(jsonString);
+ expect(stringValues.length).toBe(4);
+
+ expect(jsonString.substring(stringValues[0].startIndex, stringValues[0].endIndex + 1)).toBe(
+ '"string"'
+ );
+ expect(jsonString.substring(stringValues[1].startIndex, stringValues[1].endIndex + 1)).toBe(
+ '"a"'
+ );
+ expect(jsonString.substring(stringValues[2].startIndex, stringValues[2].endIndex + 1)).toBe(
+ '"test"'
+ );
+ expect(jsonString.substring(stringValues[3].startIndex, stringValues[3].endIndex + 1)).toBe(
+ '"string"'
+ );
+ });
+ });
});
_.each(collapsingTests.split(/^=+$/m), function (fixture) {
diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_expanding.txt b/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_expanding.txt
index 7de874c244e74..d157ed73817c2 100644
--- a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_expanding.txt
+++ b/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_expanding.txt
@@ -1,8 +1,9 @@
+
==========
Scripts in requests
-------------------------------------
{
- "f": { "script": { "source": "\ntest\ntest\\\\\\\\\\\\\\\\2\n" } },
+ "f": { "script": { "source": "\ntest\ntest\\2\n" } },
"g": { "script": "second + \"\\\";" },
"a": "short with \\",
"\\\\h": 1,
@@ -12,7 +13,7 @@ Scripts in requests
{
"f": { "script": { "source": """
test
-test\\\\\\\\2
+test\2
""" } },
"g": { "script": """second + "\";""" },
"a": """short with \""",
@@ -23,11 +24,11 @@ test\\\\\\\\2
Preserve triple quotes
-------------------------------------
{
- "content\\\": "tri\"ple",
+ "content\\": "tri\"ple",
}
-------------------------------------
{
- "content\\\": """tri"ple""",
+ "content\\": """tri"ple""",
}
==========
Correctly parse with JSON embedded inside values
@@ -82,3 +83,13 @@ Single quotes escaped special case, end
{
"query": "test\""
}
+==========
+Strings in Arrays
+-------------------------------------
+{
+ "array": ["expand \\ me", "do not expand", "do expand \\"]
+}
+-------------------------------------
+{
+ "array": ["""expand \ me""", "do not expand", """do expand \"""]
+}
diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/index.ts b/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/index.ts
index 28f1aca95efab..86fe9535a9619 100644
--- a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/index.ts
+++ b/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/index.ts
@@ -17,6 +17,8 @@
* under the License.
*/
+import { extractJSONStringValues } from './parser';
+
export function collapseLiteralStrings(data: string) {
const splitData = data.split(`"""`);
for (let idx = 1; idx < splitData.length - 1; idx += 2) {
@@ -25,47 +27,60 @@ export function collapseLiteralStrings(data: string) {
return splitData.join('');
}
-/*
- The following regex describes global match on:
- 1. one colon followed by any number of space characters
- 2. one double quote (not escaped, special case for JSON in JSON).
- 3. greedily match any non double quote and non newline char OR any escaped double quote char (non-capturing).
- 4. handle a special case where an escaped slash may be the last character
- 5. one double quote
-
- For instance: `: "some characters \" here"`
- Will match and be expanded to: `"""some characters " here"""`
+// 5 megabytes
+const MAX_EXPANDABLE_JSON_SIZE = 5 * 1024 * 1024;
+/**
+ * Takes in a string representing some JSON data and expands strings,
+ * where needed, to a string literal representation.
+ *
+ * For example; given a value like: "{ "my_string": "\nhey!\n" }"
+ *
+ * Will return: "{ "my_string": """
+ * hey!
+ * """
+ * }"
*/
+export function expandLiteralStrings(data: string) {
+ // Assuming 1 byte per char
+ if (data.length > MAX_EXPANDABLE_JSON_SIZE) {
+ return data;
+ }
-const LITERAL_STRING_CANDIDATES = /((:[\s\r\n]*)([^\\])"(\\"|[^"\n])*\\?")/g;
+ const { stringValues } = extractJSONStringValues(data);
-export function expandLiteralStrings(data: string) {
- return data.replace(LITERAL_STRING_CANDIDATES, (match, string) => {
- // Expand to triple quotes if there are _any_ slashes
- if (string.match(/\\./)) {
- const firstDoubleQuoteIdx = string.indexOf('"');
- const lastDoubleQuoteIdx = string.lastIndexOf('"');
+ if (stringValues.length === 0) {
+ return data;
+ }
- // Handle a special case where we may have a value like "\"test\"". We don't
- // want to expand this to """"test"""" - so we terminate before processing the string
- // further if we detect this either at the start or end of the double quote section.
+ // Include JSON before our first string value
+ let result = data.substring(0, stringValues[0].startIndex);
- if (string[firstDoubleQuoteIdx + 1] === '\\' && string[firstDoubleQuoteIdx + 2] === '"') {
- return string;
- }
+ for (let x = 0; x < stringValues.length; x++) {
+ const { startIndex, endIndex } = stringValues[x];
+ const candidate = data.substring(startIndex, endIndex + 1);
- if (string[lastDoubleQuoteIdx - 1] === '"' && string[lastDoubleQuoteIdx - 2] === '\\') {
- return string;
- }
+ // Handle a special case where we may have a value like "\"test\"". We don't
+ // want to expand this to """"test"""" - so we terminate before processing the string
+ // further if we detect this either at the start or end of the double quote section.
+ const skip =
+ (candidate[1] === '\\' && candidate[2] === '"') ||
+ (candidate[candidate.length - 2] === '"' && candidate[candidate.length - 3] === '\\');
- const colonAndAnySpacing = string.slice(0, firstDoubleQuoteIdx);
- const rawStringifiedValue = string.slice(firstDoubleQuoteIdx, string.length);
- // Remove one level of JSON stringification
- const jsonValue = JSON.parse(rawStringifiedValue);
- return `${colonAndAnySpacing}"""${jsonValue}"""`;
+ if (!skip && candidate.match(/\\./)) {
+ result += `"""${JSON.parse(candidate)}"""`;
} else {
- return string;
+ result += candidate;
+ }
+
+ if (stringValues[x + 1]) {
+ // Add any JSON between string values
+ result += data.substring(endIndex + 1, stringValues[x + 1].startIndex);
}
- });
+ }
+
+ // Add any remaining JSON after all string values
+ result += data.substring(stringValues[stringValues.length - 1].endIndex + 1);
+
+ return result;
}
diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/parser.ts b/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/parser.ts
new file mode 100644
index 0000000000000..2cbd886860a07
--- /dev/null
+++ b/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/parser.ts
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+type StringValues = Array<{ startIndex: number; endIndex: number }>;
+
+interface ParseResult {
+ stringValues: StringValues;
+}
+
+const JSON_COLON = ':';
+const JSON_STRING_DELIMITER = '"';
+const JSON_STRING_ESCAPE = '\\';
+
+/**
+ * Accepts JSON (as a string) and extracts the positions of all JSON string
+ * values.
+ *
+ * For example:
+ *
+ * '{ "my_string_value": "is this", "my_number_value": 42 }'
+ *
+ * Would extract one result:
+ *
+ * [ { startIndex: 21, endIndex: 29 } ]
+ *
+ * This result maps to `"is this"` from the example JSON.
+ *
+ */
+export const extractJSONStringValues = (input: string): ParseResult => {
+ let position = 0;
+ let currentStringStartPos: number;
+ let isInsideString = false;
+ const stringValues: StringValues = [];
+
+ function read() {
+ return input[position];
+ }
+
+ function peekNextNonWhitespace(): string | undefined {
+ let peekPosition = position + 1;
+
+ while (peekPosition < input.length) {
+ const peekChar = input[peekPosition];
+ if (peekChar.match(/[^\s\r\n]/)) {
+ return peekChar;
+ }
+ ++peekPosition;
+ }
+ }
+
+ function advance() {
+ ++position;
+ }
+
+ while (position < input.length) {
+ const char = read();
+ if (!isInsideString) {
+ if (char === JSON_STRING_DELIMITER) {
+ currentStringStartPos = position;
+ isInsideString = true;
+ }
+ // else continue scanning for JSON_STRING_DELIMITER
+ } else {
+ if (char === JSON_STRING_ESCAPE) {
+ // skip ahead - we are still inside of a string
+ advance();
+ } else if (char === JSON_STRING_DELIMITER) {
+ if (peekNextNonWhitespace() !== JSON_COLON) {
+ stringValues.push({
+ startIndex: currentStringStartPos!,
+ endIndex: position,
+ });
+ }
+ isInsideString = false;
+ }
+ }
+ advance();
+ }
+
+ return { stringValues };
+};
diff --git a/src/plugins/kibana_utils/public/render_complete/index.ts b/src/plugins/kibana_utils/public/render_complete/index.ts
index ec86144f65a35..8f14b6ca8f0ab 100644
--- a/src/plugins/kibana_utils/public/render_complete/index.ts
+++ b/src/plugins/kibana_utils/public/render_complete/index.ts
@@ -17,19 +17,5 @@
* under the License.
*/
-const dispatchCustomEvent = (el: HTMLElement, eventName: string) => {
- // we're using the native events so that we aren't tied to the jQuery custom events,
- // otherwise we have to use jQuery(element).on(...) because jQuery's events sit on top
- // of the native events per https://github.com/jquery/jquery/issues/2476
- el.dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
-};
-
-export function dispatchRenderComplete(el: HTMLElement) {
- dispatchCustomEvent(el, 'renderComplete');
-}
-
-export function dispatchRenderStart(el: HTMLElement) {
- dispatchCustomEvent(el, 'renderStart');
-}
-
-export * from './render_complete_helper';
+export * from './render_complete_listener';
+export * from './render_complete_dispatcher';
diff --git a/src/plugins/kibana_utils/public/render_complete/render_complete_dispatcher.ts b/src/plugins/kibana_utils/public/render_complete/render_complete_dispatcher.ts
new file mode 100644
index 0000000000000..386bf1eb62568
--- /dev/null
+++ b/src/plugins/kibana_utils/public/render_complete/render_complete_dispatcher.ts
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+const dispatchEvent = (el: HTMLElement, eventName: string) => {
+ el.dispatchEvent(new CustomEvent(eventName, { bubbles: true }));
+};
+
+export function dispatchRenderComplete(el: HTMLElement) {
+ dispatchEvent(el, 'renderComplete');
+}
+
+export function dispatchRenderStart(el: HTMLElement) {
+ dispatchEvent(el, 'renderStart');
+}
+
+/**
+ * Should call `dispatchComplete()` when UI block has finished loading its data and has
+ * completely rendered. Should `dispatchInProgress()` every time UI block
+ * starts loading data again. At the start it is assumed that UI block is loading
+ * so it dispatches "in progress" automatically, so you need to call `setRenderComplete`
+ * at least once.
+ *
+ * This is used for reporting to know that UI block is ready, so
+ * it can take a screenshot. It is also used in functional tests to know that
+ * page has stabilized.
+ */
+export class RenderCompleteDispatcher {
+ private count: number = 0;
+ private el?: HTMLElement;
+
+ constructor(el?: HTMLElement) {
+ this.setEl(el);
+ }
+
+ public setEl(el?: HTMLElement) {
+ this.el = el;
+ this.count = 0;
+ if (el) this.dispatchInProgress();
+ }
+
+ public dispatchInProgress() {
+ if (!this.el) return;
+ this.el.setAttribute('data-render-complete', 'false');
+ this.el.setAttribute('data-rendering-count', String(this.count));
+ dispatchRenderStart(this.el);
+ }
+
+ public dispatchComplete() {
+ if (!this.el) return;
+ this.count++;
+ this.el.setAttribute('data-render-complete', 'true');
+ this.el.setAttribute('data-rendering-count', String(this.count));
+ dispatchRenderComplete(this.el);
+ }
+
+ public dispatchError() {
+ if (!this.el) return;
+ this.count++;
+ this.el.setAttribute('data-render-complete', 'false');
+ this.el.setAttribute('data-rendering-count', String(this.count));
+ }
+
+ public setTitle(title: string) {
+ if (!this.el) return;
+ this.el.setAttribute('data-title', title);
+ }
+}
diff --git a/src/plugins/kibana_utils/public/render_complete/render_complete_helper.ts b/src/plugins/kibana_utils/public/render_complete/render_complete_listener.ts
similarity index 80%
rename from src/plugins/kibana_utils/public/render_complete/render_complete_helper.ts
rename to src/plugins/kibana_utils/public/render_complete/render_complete_listener.ts
index 1230638a1d709..d9289b20d4bfa 100644
--- a/src/plugins/kibana_utils/public/render_complete/render_complete_helper.ts
+++ b/src/plugins/kibana_utils/public/render_complete/render_complete_listener.ts
@@ -17,9 +17,9 @@
* under the License.
*/
-const attributeName = 'data-render-complete';
+export class RenderCompleteListener {
+ private readonly attributeName = 'data-render-complete';
-export class RenderCompleteHelper {
constructor(private readonly element: HTMLElement) {
this.setup();
}
@@ -30,23 +30,23 @@ export class RenderCompleteHelper {
};
public setup = () => {
- this.element.setAttribute(attributeName, 'false');
+ this.element.setAttribute(this.attributeName, 'false');
this.element.addEventListener('renderStart', this.start);
this.element.addEventListener('renderComplete', this.complete);
};
public disable = () => {
- this.element.setAttribute(attributeName, 'disabled');
+ this.element.setAttribute(this.attributeName, 'disabled');
this.destroy();
};
private start = () => {
- this.element.setAttribute(attributeName, 'false');
+ this.element.setAttribute(this.attributeName, 'false');
return true;
};
private complete = () => {
- this.element.setAttribute(attributeName, 'true');
+ this.element.setAttribute(this.attributeName, 'true');
return true;
};
}
diff --git a/src/plugins/vis_type_vega/public/data_model/search_api.ts b/src/plugins/vis_type_vega/public/data_model/search_api.ts
index a213b59be2ad0..d2ce8c95b9f90 100644
--- a/src/plugins/vis_type_vega/public/data_model/search_api.ts
+++ b/src/plugins/vis_type_vega/public/data_model/search_api.ts
@@ -51,8 +51,10 @@ export class SearchAPI {
searchRequests.map((request) => {
const requestId = request.name;
const params = getSearchParamsFromRequest(request, {
- uiSettings: this.dependencies.uiSettings,
- injectedMetadata: this.dependencies.injectedMetadata,
+ esShardTimeout: this.dependencies.injectedMetadata.getInjectedVar(
+ 'esShardTimeout'
+ ) as number,
+ getConfig: this.dependencies.uiSettings.get.bind(this.dependencies.uiSettings),
});
if (this.inspectorAdapters) {
diff --git a/src/plugins/vis_type_vega/public/vega_type.ts b/src/plugins/vis_type_vega/public/vega_type.ts
index f49816017b684..46fd2fbc5587e 100644
--- a/src/plugins/vis_type_vega/public/vega_type.ts
+++ b/src/plugins/vis_type_vega/public/vega_type.ts
@@ -58,7 +58,6 @@ export const createVegaTypeDefinition = (dependencies: VegaVisualizationDependen
getSupportedTriggers: () => {
return [VIS_EVENT_TO_TRIGGER.applyFilter];
},
- stage: 'experimental',
inspectorAdapters: createInspectorAdapters,
};
};
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
index c4d5f5206ee90..a434bf9756b64 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -36,7 +36,6 @@ import {
IContainer,
Adapters,
} from '../../../../plugins/embeddable/public';
-import { dispatchRenderComplete } from '../../../../plugins/kibana_utils/public';
import {
IExpressionLoaderParams,
ExpressionsStart,
@@ -85,7 +84,6 @@ export class VisualizeEmbeddable extends Embeddable;
private subscriptions: Subscription[] = [];
@@ -158,7 +156,7 @@ export class VisualizeEmbeddable extends Embeddable Boolean(this.getInspectorAdapters());
onContainerLoading = () => {
- this.domNode.setAttribute('data-render-complete', 'false');
+ this.renderComplete.dispatchInProgress();
this.updateOutput({ loading: true, error: undefined });
};
- onContainerRender = (count: number) => {
- this.domNode.setAttribute('data-render-complete', 'true');
- this.domNode.setAttribute('data-rendering-count', count.toString());
+ onContainerRender = () => {
+ this.renderComplete.dispatchComplete();
this.updateOutput({ loading: false, error: undefined });
- dispatchRenderComplete(this.domNode);
};
onContainerError = (error: ExpressionRenderError) => {
if (this.abortController) {
this.abortController.abort();
}
- this.domNode.setAttribute(
- 'data-rendering-count',
- this.domNode.getAttribute('data-rendering-count') + 1
- );
- this.domNode.setAttribute('data-render-complete', 'false');
+ this.renderComplete.dispatchError();
this.updateOutput({ loading: false, error });
};
@@ -274,7 +256,6 @@ export class VisualizeEmbeddable extends Embeddable;
diff --git a/src/plugins/visualize/kibana.json b/src/plugins/visualize/kibana.json
index a6cc8d8f8af60..29fcd30184cb2 100644
--- a/src/plugins/visualize/kibana.json
+++ b/src/plugins/visualize/kibana.json
@@ -10,6 +10,7 @@
"savedObjects",
"visualizations",
"embeddable",
+ "dashboard",
"uiActions"
],
"optionalPlugins": ["home", "share"],
diff --git a/src/plugins/visualize/public/application/types.ts b/src/plugins/visualize/public/application/types.ts
index 65b88485b2f06..0a12dbc22a744 100644
--- a/src/plugins/visualize/public/application/types.ts
+++ b/src/plugins/visualize/public/application/types.ts
@@ -44,7 +44,7 @@ import { SharePluginStart } from 'src/plugins/share/public';
import { SavedObjectsStart, SavedObject } from 'src/plugins/saved_objects/public';
import { EmbeddableStart } from 'src/plugins/embeddable/public';
import { KibanaLegacyStart } from 'src/plugins/kibana_legacy/public';
-import { ConfigSchema } from '../../config';
+import { DashboardStart } from '../../../dashboard/public';
export type PureVisState = SavedVisState;
@@ -111,7 +111,7 @@ export interface VisualizeServices extends CoreStart {
createVisEmbeddableFromObject: VisualizationsStart['__LEGACY']['createVisEmbeddableFromObject'];
restorePreviousUrl: () => void;
scopedHistory: ScopedHistory;
- featureFlagConfig: ConfigSchema;
+ dashboard: DashboardStart;
}
export interface SavedVisInstance {
diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
index 87a6437192aa9..43121f2cffc41 100644
--- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
+++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
@@ -75,7 +75,7 @@ export const getTopNavConfig = (
toastNotifications,
visualizeCapabilities,
i18n: { Context: I18nContext },
- featureFlagConfig,
+ dashboard,
}: VisualizeServices
) => {
const { vis, embeddableHandler } = visInstance;
@@ -212,7 +212,7 @@ export const getTopNavConfig = (
};
if (
originatingApp === 'dashboards' &&
- featureFlagConfig.showNewVisualizeFlow &&
+ dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables &&
!savedVis
) {
return createVisReference();
@@ -292,7 +292,7 @@ export const getTopNavConfig = (
const isSaveAsButton = anchorElement.classList.contains('saveAsButton');
if (
originatingApp === 'dashboards' &&
- featureFlagConfig.showNewVisualizeFlow &&
+ dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables &&
!isSaveAsButton
) {
createVisReference();
diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts
index 8794593d6c958..29d6f978bd05e 100644
--- a/src/plugins/visualize/public/plugin.ts
+++ b/src/plugins/visualize/public/plugin.ts
@@ -48,6 +48,7 @@ import { VisualizeServices } from './application/types';
import { DEFAULT_APP_CATEGORIES } from '../../../core/public';
import { SavedObjectsStart } from '../../saved_objects/public';
import { EmbeddableStart } from '../../embeddable/public';
+import { DashboardStart } from '../../dashboard/public';
import { UiActionsStart, VISUALIZE_FIELD_TRIGGER } from '../../ui_actions/public';
import {
setUISettings,
@@ -67,6 +68,7 @@ export interface VisualizePluginStartDependencies {
embeddable: EmbeddableStart;
kibanaLegacy: KibanaLegacyStart;
savedObjects: SavedObjectsStart;
+ dashboard: DashboardStart;
uiActions: UiActionsStart;
}
@@ -77,10 +79,6 @@ export interface VisualizePluginSetupDependencies {
share?: SharePluginSetup;
}
-export interface FeatureFlagConfig {
- showNewVisualizeFlow: boolean;
-}
-
export class VisualizePlugin
implements
Plugin {
@@ -171,7 +169,6 @@ export class VisualizePlugin
* this should be replaced to use only scoped history after moving legacy apps to browser routing
*/
const history = createHashHistory();
-
const services: VisualizeServices = {
...coreStart,
history,
@@ -198,7 +195,7 @@ export class VisualizePlugin
savedObjectsPublic: pluginsStart.savedObjects,
scopedHistory: params.history,
restorePreviousUrl,
- featureFlagConfig: this.initializerContext.config.get(),
+ dashboard: pluginsStart.dashboard,
};
params.element.classList.add('visAppWrapper');
diff --git a/src/plugins/visualize/server/index.ts b/src/plugins/visualize/server/index.ts
index 6da0a513b1475..5cebef71d8d22 100644
--- a/src/plugins/visualize/server/index.ts
+++ b/src/plugins/visualize/server/index.ts
@@ -17,17 +17,8 @@
* under the License.
*/
-import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server';
+import { PluginInitializerContext } from 'kibana/server';
import { VisualizeServerPlugin } from './plugin';
-import { ConfigSchema, configSchema } from '../config';
-
-export const config: PluginConfigDescriptor = {
- exposeToBrowser: {
- showNewVisualizeFlow: true,
- },
- schema: configSchema,
-};
-
export const plugin = (initContext: PluginInitializerContext) =>
new VisualizeServerPlugin(initContext);
diff --git a/test/functional/apps/dashboard/embeddable_rendering.js b/test/functional/apps/dashboard/embeddable_rendering.js
index c00f01d060f4a..73c36c7562e8b 100644
--- a/test/functional/apps/dashboard/embeddable_rendering.js
+++ b/test/functional/apps/dashboard/embeddable_rendering.js
@@ -33,6 +33,7 @@ export default function ({ getService, getPageObjects }) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const pieChart = getService('pieChart');
+ const security = getService('security');
const dashboardExpect = getService('dashboardExpect');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects([
@@ -100,6 +101,7 @@ export default function ({ getService, getPageObjects }) {
describe('dashboard embeddable rendering', function describeIndexTests() {
before(async () => {
+ await security.testUser.setRoles(['kibana_admin', 'animals', 'test_logstash_reader']);
await esArchiver.load('dashboard/current/kibana');
await kibanaServer.uiSettings.replace({
defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c',
@@ -118,6 +120,7 @@ export default function ({ getService, getPageObjects }) {
const currentUrl = await browser.getCurrentUrl();
const newUrl = currentUrl.replace(/\?.*$/, '');
await browser.get(newUrl, false);
+ await security.testUser.restoreDefaults();
});
it('adding visualizations', async () => {
diff --git a/test/functional/apps/dashboard/legacy_urls.ts b/test/functional/apps/dashboard/legacy_urls.ts
index e606649c1df9f..6bb8d808e8daa 100644
--- a/test/functional/apps/dashboard/legacy_urls.ts
+++ b/test/functional/apps/dashboard/legacy_urls.ts
@@ -35,6 +35,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const dashboardAddPanel = getService('dashboardAddPanel');
const listingTable = getService('listingTable');
const esArchiver = getService('esArchiver');
+ const security = getService('security');
let kibanaLegacyBaseUrl: string;
let kibanaVisualizeBaseUrl: string;
@@ -42,6 +43,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('legacy urls', function describeIndexTests() {
before(async function () {
+ await security.testUser.setRoles(['kibana_admin', 'animals']);
await esArchiver.load('dashboard/current/kibana');
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
@@ -61,6 +63,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
after(async function () {
await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.deleteItem('legacyTest', testDashboardId);
+ await security.testUser.restoreDefaults();
});
describe('kibana link redirect', () => {
diff --git a/test/functional/config.js b/test/functional/config.js
index 95e0c689089ef..15097d9346471 100644
--- a/test/functional/config.js
+++ b/test/functional/config.js
@@ -264,7 +264,7 @@ export default async function ({ readConfigFile }) {
cluster: [],
indices: [
{
- names: ['animals-*'],
+ names: ['animals-*', 'dogbreeds'],
privileges: ['read', 'view_index_metadata'],
field_security: { grant: ['*'], except: [] },
},
diff --git a/test/functional/page_objects/vega_chart_page.ts b/test/functional/page_objects/vega_chart_page.ts
index 1173c35af3384..6c64f9dda2efd 100644
--- a/test/functional/page_objects/vega_chart_page.ts
+++ b/test/functional/page_objects/vega_chart_page.ts
@@ -33,7 +33,6 @@ export function VegaChartPageProvider({
const find = getService('find');
const testSubjects = getService('testSubjects');
const browser = getService('browser');
- const { common } = getPageObjects(['common']);
const retry = getService('retry');
class VegaChartPage {
@@ -49,6 +48,15 @@ export function VegaChartPageProvider({
return find.byCssSelector('div.vgaVis__controls');
}
+ public getYAxisContainer() {
+ return find.byCssSelector('[aria-label^="Y-axis"]');
+ }
+
+ public async getAceGutterContainer() {
+ const editor = await this.getEditor();
+ return editor.findByClassName('ace_gutter');
+ }
+
public async getRawSpec() {
// Adapted from console_page.js:getVisibleTextFromAceEditor(). Is there a common utilities file?
const editor = await this.getEditor();
@@ -83,20 +91,16 @@ export function VegaChartPageProvider({
}
public async typeInSpec(text: string) {
- await this.focusEditor();
+ const aceGutter = await this.getAceGutterContainer();
- let repeats = 20;
- while (--repeats > 0) {
- await browser.pressKeys(Key.ARROW_UP);
- await common.sleep(50);
- }
- await browser.pressKeys(Key.ARROW_RIGHT);
+ await aceGutter.doubleClick();
+ await browser.pressKeys(Key.LEFT);
+ await browser.pressKeys(Key.RIGHT);
await browser.pressKeys(text);
}
public async cleanSpec() {
- const editor = await this.getEditor();
- const aceGutter = await editor.findByClassName('ace_gutter');
+ const aceGutter = await this.getAceGutterContainer();
await retry.try(async () => {
await aceGutter.doubleClick();
@@ -107,11 +111,11 @@ export function VegaChartPageProvider({
}
public async getYAxisLabels() {
- const chart = await testSubjects.find('visualizationLoader');
- const yAxis = await chart.findByCssSelector('[aria-label^="Y-axis"]');
+ const yAxis = await this.getYAxisContainer();
const tickGroup = await yAxis.findByClassName('role-axis-label');
const labels = await tickGroup.findAllByCssSelector('text');
const labelTexts: string[] = [];
+
for (const label of labels) {
labelTexts.push(await label.getVisibleText());
}
diff --git a/test/new_visualize_flow/config.js b/test/new_visualize_flow/config.js
index a6440d16481d5..c4790a35404c9 100644
--- a/test/new_visualize_flow/config.js
+++ b/test/new_visualize_flow/config.js
@@ -37,7 +37,6 @@ export default async function ({ readConfigFile }) {
...commonConfig.get('kbnTestServer.serverArgs'),
'--oss',
'--telemetry.optIn=false',
- '--visualize.showNewVisualizeFlow=true',
],
},
diff --git a/x-pack/package.json b/x-pack/package.json
index 5333c67f6ac0f..a70373db36603 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -203,7 +203,7 @@
"@babel/core": "^7.11.1",
"@babel/register": "^7.10.5",
"@babel/runtime": "^7.11.2",
- "@elastic/apm-rum-react": "^1.2.2",
+ "@elastic/apm-rum-react": "^1.2.3",
"@elastic/datemath": "5.0.3",
"@elastic/ems-client": "7.9.3",
"@elastic/eui": "27.4.0",
diff --git a/x-pack/plugins/ingest_manager/server/routes/setup/handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/setup/handlers.test.ts
new file mode 100644
index 0000000000000..ce826e78c454d
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/routes/setup/handlers.test.ts
@@ -0,0 +1,83 @@
+/*
+ * 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 { xpackMocks } from '../../../../../../x-pack/mocks';
+import { httpServerMock } from 'src/core/server/mocks';
+import { PostIngestSetupResponse } from '../../../common';
+import { RegistryError } from '../../errors';
+import { createAppContextStartContractMock } from '../../mocks';
+import { ingestManagerSetupHandler } from './handlers';
+import { appContextService } from '../../services/app_context';
+import { setupIngestManager } from '../../services/setup';
+
+jest.mock('../../services/setup', () => {
+ return {
+ setupIngestManager: jest.fn(),
+ };
+});
+
+const mockSetupIngestManager = setupIngestManager as jest.MockedFunction;
+
+describe('ingestManagerSetupHandler', () => {
+ let context: ReturnType;
+ let response: ReturnType;
+ let request: ReturnType;
+
+ beforeEach(async () => {
+ context = xpackMocks.createRequestHandlerContext();
+ response = httpServerMock.createResponseFactory();
+ request = httpServerMock.createKibanaRequest({
+ method: 'post',
+ path: '/api/ingest_manager/setup',
+ });
+ // prevents `Logger not set.` and other appContext errors
+ appContextService.start(createAppContextStartContractMock());
+ });
+
+ afterEach(async () => {
+ jest.clearAllMocks();
+ appContextService.stop();
+ });
+
+ it('POST /setup succeeds w/200 and body of resolved value', async () => {
+ mockSetupIngestManager.mockImplementation(() => Promise.resolve({ isIntialized: true }));
+ await ingestManagerSetupHandler(context, request, response);
+
+ const expectedBody: PostIngestSetupResponse = { isInitialized: true };
+ expect(response.customError).toHaveBeenCalledTimes(0);
+ expect(response.ok).toHaveBeenCalledWith({ body: expectedBody });
+ });
+
+ it('POST /setup fails w/500 on custom error', async () => {
+ mockSetupIngestManager.mockImplementation(() =>
+ Promise.reject(new Error('SO method mocked to throw'))
+ );
+ await ingestManagerSetupHandler(context, request, response);
+
+ expect(response.customError).toHaveBeenCalledTimes(1);
+ expect(response.customError).toHaveBeenCalledWith({
+ statusCode: 500,
+ body: {
+ message: 'SO method mocked to throw',
+ },
+ });
+ });
+
+ it('POST /setup fails w/502 on RegistryError', async () => {
+ mockSetupIngestManager.mockImplementation(() =>
+ Promise.reject(new RegistryError('Registry method mocked to throw'))
+ );
+
+ await ingestManagerSetupHandler(context, request, response);
+ expect(response.customError).toHaveBeenCalledTimes(1);
+ expect(response.customError).toHaveBeenCalledWith({
+ statusCode: 502,
+ body: {
+ message: 'Registry method mocked to throw',
+ },
+ });
+ });
+});
diff --git a/x-pack/plugins/ingest_manager/server/routes/setup/index.ts b/x-pack/plugins/ingest_manager/server/routes/setup/index.ts
index 1d1e7a2d721c9..fe51abec45b23 100644
--- a/x-pack/plugins/ingest_manager/server/routes/setup/index.ts
+++ b/x-pack/plugins/ingest_manager/server/routes/setup/index.ts
@@ -14,8 +14,7 @@ import {
} from './handlers';
import { PostFleetSetupRequestSchema } from '../../types';
-export const registerRoutes = (router: IRouter, config: IngestManagerConfigType) => {
- // Ingest manager setup
+export const registerIngestManagerSetupRoute = (router: IRouter) => {
router.post(
{
path: SETUP_API_ROUTE,
@@ -26,12 +25,20 @@ export const registerRoutes = (router: IRouter, config: IngestManagerConfigType)
},
ingestManagerSetupHandler
);
+};
- if (!config.fleet.enabled) {
- return;
- }
+export const registerCreateFleetSetupRoute = (router: IRouter) => {
+ router.post(
+ {
+ path: FLEET_SETUP_API_ROUTES.CREATE_PATTERN,
+ validate: PostFleetSetupRequestSchema,
+ options: { tags: [`access:${PLUGIN_ID}-all`] },
+ },
+ createFleetSetupHandler
+ );
+};
- // Get Fleet setup
+export const registerGetFleetStatusRoute = (router: IRouter) => {
router.get(
{
path: FLEET_SETUP_API_ROUTES.INFO_PATTERN,
@@ -40,14 +47,19 @@ export const registerRoutes = (router: IRouter, config: IngestManagerConfigType)
},
getFleetStatusHandler
);
+};
+
+export const registerRoutes = (router: IRouter, config: IngestManagerConfigType) => {
+ // Ingest manager setup
+ registerIngestManagerSetupRoute(router);
+
+ if (!config.fleet.enabled) {
+ return;
+ }
+
+ // Get Fleet setup
+ registerGetFleetStatusRoute(router);
// Create Fleet setup
- router.post(
- {
- path: FLEET_SETUP_API_ROUTES.CREATE_PATTERN,
- validate: PostFleetSetupRequestSchema,
- options: { tags: [`access:${PLUGIN_ID}-all`] },
- },
- createFleetSetupHandler
- );
+ registerCreateFleetSetupRoute(router);
};
diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/requests.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/requests.test.ts
new file mode 100644
index 0000000000000..f836a133a78a0
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/requests.test.ts
@@ -0,0 +1,108 @@
+/*
+ * 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 { fetchUrl } from './requests';
+import { RegistryError } from '../../../errors';
+jest.mock('node-fetch');
+
+const { Response, FetchError } = jest.requireActual('node-fetch');
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const fetchMock = require('node-fetch') as jest.Mock;
+
+jest.setTimeout(120 * 1000);
+describe('setupIngestManager', () => {
+ beforeEach(async () => {});
+
+ afterEach(async () => {
+ jest.clearAllMocks();
+ });
+
+ describe('fetchUrl / getResponse errors', () => {
+ it('regular Errors do not retry. Becomes RegistryError', async () => {
+ fetchMock.mockImplementationOnce(() => {
+ throw new Error('mocked');
+ });
+ const promise = fetchUrl('');
+ await expect(promise).rejects.toThrow(RegistryError);
+ expect(fetchMock).toHaveBeenCalledTimes(1);
+ });
+
+ it('TypeErrors do not retry. Becomes RegistryError', async () => {
+ fetchMock.mockImplementationOnce(() => {
+ // @ts-expect-error
+ null.f();
+ });
+ const promise = fetchUrl('');
+ await expect(promise).rejects.toThrow(RegistryError);
+ expect(fetchMock).toHaveBeenCalledTimes(1);
+ });
+
+ describe('only system errors retry (like ECONNRESET)', () => {
+ it('they eventually succeed', async () => {
+ const successValue = JSON.stringify({ name: 'attempt 4 works', version: '1.2.3' });
+ fetchMock
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 1', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 2', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 3', 'system', { code: 'ESOMETHING' });
+ })
+ // this one succeeds
+ .mockImplementationOnce(() => Promise.resolve(new Response(successValue)))
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 5', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 6', 'system', { code: 'ESOMETHING' });
+ });
+
+ const promise = fetchUrl('');
+ await expect(promise).resolves.toEqual(successValue);
+ // doesn't retry after success
+ expect(fetchMock).toHaveBeenCalledTimes(4);
+ const actualResultsOrder = fetchMock.mock.results.map(({ type }: { type: string }) => type);
+ expect(actualResultsOrder).toEqual(['throw', 'throw', 'throw', 'return']);
+ });
+
+ it('or error after 1 failure & 5 retries with RegistryError', async () => {
+ fetchMock
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 1', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 2', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 3', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 4', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 5', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 6', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 7', 'system', { code: 'ESOMETHING' });
+ })
+ .mockImplementationOnce(() => {
+ throw new FetchError('message 8', 'system', { code: 'ESOMETHING' });
+ });
+
+ const promise = fetchUrl('');
+ await expect(promise).rejects.toThrow(RegistryError);
+ // doesn't retry after 1 failure & 5 failed retries
+ expect(fetchMock).toHaveBeenCalledTimes(6);
+ const actualResultsOrder = fetchMock.mock.results.map(({ type }: { type: string }) => type);
+ expect(actualResultsOrder).toEqual(['throw', 'throw', 'throw', 'throw', 'throw', 'throw']);
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/requests.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/requests.ts
index abf77ddddfd7a..5939dc204aae6 100644
--- a/x-pack/plugins/ingest_manager/server/services/epm/registry/requests.ts
+++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/requests.ts
@@ -4,20 +4,49 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import fetch, { Response } from 'node-fetch';
+import fetch, { FetchError, Response } from 'node-fetch';
+import pRetry from 'p-retry';
import { streamToString } from './streams';
import { RegistryError } from '../../../errors';
+type FailedAttemptErrors = pRetry.FailedAttemptError | FetchError | Error;
+
+// not sure what to call this function, but we're not exporting it
+async function registryFetch(url: string) {
+ const response = await fetch(url);
+
+ if (response.ok) {
+ return response;
+ } else {
+ // 4xx & 5xx responses
+ // exit without retry & throw RegistryError
+ throw new pRetry.AbortError(
+ new RegistryError(`Error connecting to package registry at ${url}: ${response.statusText}`)
+ );
+ }
+}
+
export async function getResponse(url: string): Promise {
try {
- const response = await fetch(url);
- if (response.ok) {
- return response;
- } else {
- throw new RegistryError(
- `Error connecting to package registry at ${url}: ${response.statusText}`
- );
- }
+ // we only want to retry certain failures like network issues
+ // the rest should only try the one time then fail as they do now
+ const response = await pRetry(() => registryFetch(url), {
+ factor: 2,
+ retries: 5,
+ onFailedAttempt: (error) => {
+ // we only want to retry certain types of errors, like `ECONNREFUSED` and other operational errors
+ // and let the others through without retrying
+ //
+ // throwing in onFailedAttempt will abandon all retries & fail the request
+ // we only want to retry system errors, so throw a RegistryError for everything else
+ if (!isSystemError(error)) {
+ throw new RegistryError(
+ `Error connecting to package registry at ${url}: ${error.message}`
+ );
+ }
+ },
+ });
+ return response;
} catch (e) {
throw new RegistryError(`Error connecting to package registry at ${url}: ${e.message}`);
}
@@ -31,3 +60,14 @@ export async function getResponseStream(url: string): Promise {
return getResponseStream(url).then(streamToString);
}
+
+// node-fetch throws a FetchError for those types of errors and
+// "All errors originating from Node.js core are marked with error.type = 'system'"
+// https://github.com/node-fetch/node-fetch/blob/master/docs/ERROR-HANDLING.md#error-handling-with-node-fetch
+function isFetchError(error: FailedAttemptErrors): error is FetchError {
+ return error instanceof FetchError || error.name === 'FetchError';
+}
+
+function isSystemError(error: FailedAttemptErrors): boolean {
+ return isFetchError(error) && error.type === 'system';
+}
diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/streams.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/streams.ts
index 97d6f7b40a588..3801303cf726f 100644
--- a/x-pack/plugins/ingest_manager/server/services/epm/registry/streams.ts
+++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/streams.ts
@@ -11,7 +11,8 @@ export function bufferToStream(buffer: Buffer): PassThrough {
return stream;
}
-export function streamToString(stream: NodeJS.ReadableStream): Promise {
+export function streamToString(stream: NodeJS.ReadableStream | Buffer): Promise {
+ if (stream instanceof Buffer) return Promise.resolve(stream.toString());
return new Promise((resolve, reject) => {
const body: string[] = [];
stream.on('data', (chunk: string) => body.push(chunk));
diff --git a/x-pack/plugins/ingest_manager/server/services/setup.test.ts b/x-pack/plugins/ingest_manager/server/services/setup.test.ts
index 474b2fde23c81..bb01862aaf317 100644
--- a/x-pack/plugins/ingest_manager/server/services/setup.test.ts
+++ b/x-pack/plugins/ingest_manager/server/services/setup.test.ts
@@ -4,41 +4,59 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { xpackMocks } from '../../../../../x-pack/mocks';
+import { createAppContextStartContractMock } from '../mocks';
+import { appContextService } from './app_context';
import { setupIngestManager } from './setup';
-import { savedObjectsClientMock } from 'src/core/server/mocks';
-describe('setupIngestManager', () => {
- it('returned promise should reject if errors thrown', async () => {
- const { savedObjectsClient, callClusterMock } = makeErrorMocks();
- const setupPromise = setupIngestManager(savedObjectsClient, callClusterMock);
- await expect(setupPromise).rejects.toThrow('mocked');
+const mockedMethodThrowsError = () =>
+ jest.fn().mockImplementation(() => {
+ throw new Error('SO method mocked to throw');
});
-});
-function makeErrorMocks() {
- jest.mock('./app_context'); // else fails w/"Logger not set."
- jest.mock('./epm/registry/registry_url', () => {
- return {
- fetchUrl: () => {
- throw new Error('mocked registry#fetchUrl');
- },
- };
+class CustomTestError extends Error {}
+const mockedMethodThrowsCustom = () =>
+ jest.fn().mockImplementation(() => {
+ throw new CustomTestError('method mocked to throw');
});
- const callClusterMock = jest.fn();
- const savedObjectsClient = savedObjectsClientMock.create();
- savedObjectsClient.find = jest.fn().mockImplementation(() => {
- throw new Error('mocked SO#find');
- });
- savedObjectsClient.get = jest.fn().mockImplementation(() => {
- throw new Error('mocked SO#get');
+describe('setupIngestManager', () => {
+ let context: ReturnType;
+
+ beforeEach(async () => {
+ context = xpackMocks.createRequestHandlerContext();
+ // prevents `Logger not set.` and other appContext errors
+ appContextService.start(createAppContextStartContractMock());
});
- savedObjectsClient.update = jest.fn().mockImplementation(() => {
- throw new Error('mocked SO#update');
+
+ afterEach(async () => {
+ jest.clearAllMocks();
+ appContextService.stop();
});
- return {
- savedObjectsClient,
- callClusterMock,
- };
-}
+ describe('should reject with any error thrown underneath', () => {
+ it('SO client throws plain Error', async () => {
+ const soClient = context.core.savedObjects.client;
+ soClient.create = mockedMethodThrowsError();
+ soClient.find = mockedMethodThrowsError();
+ soClient.get = mockedMethodThrowsError();
+ soClient.update = mockedMethodThrowsError();
+
+ const setupPromise = setupIngestManager(soClient, jest.fn());
+ await expect(setupPromise).rejects.toThrow('SO method mocked to throw');
+ await expect(setupPromise).rejects.toThrow(Error);
+ });
+
+ it('SO client throws other error', async () => {
+ const soClient = context.core.savedObjects.client;
+ soClient.create = mockedMethodThrowsCustom();
+ soClient.find = mockedMethodThrowsCustom();
+ soClient.get = mockedMethodThrowsCustom();
+ soClient.update = mockedMethodThrowsCustom();
+
+ const setupPromise = setupIngestManager(soClient, jest.fn());
+ await expect(setupPromise).rejects.toThrow('method mocked to throw');
+ await expect(setupPromise).rejects.toThrow(CustomTestError);
+ });
+ });
+});
diff --git a/x-pack/plugins/ingest_manager/server/services/setup.ts b/x-pack/plugins/ingest_manager/server/services/setup.ts
index 727b49cebc608..fb4430f8cf727 100644
--- a/x-pack/plugins/ingest_manager/server/services/setup.ts
+++ b/x-pack/plugins/ingest_manager/server/services/setup.ts
@@ -26,116 +26,101 @@ import { packagePolicyService } from './package_policy';
import { generateEnrollmentAPIKey } from './api_keys';
import { settingsService } from '.';
import { appContextService } from './app_context';
+import { awaitIfPending } from './setup_utils';
const FLEET_ENROLL_USERNAME = 'fleet_enroll';
const FLEET_ENROLL_ROLE = 'fleet_enroll';
-// the promise which tracks the setup
-let setupIngestStatus: Promise | undefined;
-// default resolve & reject to guard against "undefined is not a function" errors
-let onSetupResolve = () => {};
-let onSetupReject = (error: Error) => {};
+export interface SetupStatus {
+ isIntialized: true | undefined;
+}
export async function setupIngestManager(
soClient: SavedObjectsClientContract,
callCluster: CallESAsCurrentUser
-) {
- // installation in progress
- if (setupIngestStatus) {
- await setupIngestStatus;
- } else {
- // create the initial promise
- setupIngestStatus = new Promise((res, rej) => {
- onSetupResolve = res;
- onSetupReject = rej;
- });
+): Promise {
+ return awaitIfPending(async () => createSetupSideEffects(soClient, callCluster));
+}
+
+async function createSetupSideEffects(
+ soClient: SavedObjectsClientContract,
+ callCluster: CallESAsCurrentUser
+): Promise {
+ const [installedPackages, defaultOutput, defaultAgentPolicy] = await Promise.all([
+ // packages installed by default
+ ensureInstalledDefaultPackages(soClient, callCluster),
+ outputService.ensureDefaultOutput(soClient),
+ agentPolicyService.ensureDefaultAgentPolicy(soClient),
+ ensureDefaultIndices(callCluster),
+ settingsService.getSettings(soClient).catch((e: any) => {
+ if (e.isBoom && e.output.statusCode === 404) {
+ const http = appContextService.getHttpSetup();
+ const serverInfo = http.getServerInfo();
+ const basePath = http.basePath;
+
+ const cloud = appContextService.getCloud();
+ const cloudId = cloud?.isCloudEnabled && cloud.cloudId;
+ const cloudUrl = cloudId && decodeCloudId(cloudId)?.kibanaUrl;
+ const flagsUrl = appContextService.getConfig()?.fleet?.kibana?.host;
+ const defaultUrl = url.format({
+ protocol: serverInfo.protocol,
+ hostname: serverInfo.hostname,
+ port: serverInfo.port,
+ pathname: basePath.serverBasePath,
+ });
+
+ return settingsService.saveSettings(soClient, {
+ agent_auto_upgrade: true,
+ package_auto_upgrade: true,
+ kibana_url: cloudUrl || flagsUrl || defaultUrl,
+ });
+ }
+
+ return Promise.reject(e);
+ }),
+ ]);
+
+ // ensure default packages are added to the default conifg
+ const agentPolicyWithPackagePolicies = await agentPolicyService.get(
+ soClient,
+ defaultAgentPolicy.id,
+ true
+ );
+ if (!agentPolicyWithPackagePolicies) {
+ throw new Error('Policy not found');
+ }
+ if (
+ agentPolicyWithPackagePolicies.package_policies.length &&
+ typeof agentPolicyWithPackagePolicies.package_policies[0] === 'string'
+ ) {
+ throw new Error('Policy not found');
}
- try {
- const [installedPackages, defaultOutput, defaultAgentPolicy] = await Promise.all([
- // packages installed by default
- ensureInstalledDefaultPackages(soClient, callCluster),
- outputService.ensureDefaultOutput(soClient),
- agentPolicyService.ensureDefaultAgentPolicy(soClient),
- ensureDefaultIndices(callCluster),
- settingsService.getSettings(soClient).catch((e: any) => {
- if (e.isBoom && e.output.statusCode === 404) {
- const http = appContextService.getHttpSetup();
- const serverInfo = http.getServerInfo();
- const basePath = http.basePath;
-
- const cloud = appContextService.getCloud();
- const cloudId = cloud?.isCloudEnabled && cloud.cloudId;
- const cloudUrl = cloudId && decodeCloudId(cloudId)?.kibanaUrl;
- const flagsUrl = appContextService.getConfig()?.fleet?.kibana?.host;
- const defaultUrl = url.format({
- protocol: serverInfo.protocol,
- hostname: serverInfo.hostname,
- port: serverInfo.port,
- pathname: basePath.serverBasePath,
- });
-
- return settingsService.saveSettings(soClient, {
- agent_auto_upgrade: true,
- package_auto_upgrade: true,
- kibana_url: cloudUrl || flagsUrl || defaultUrl,
- });
- }
-
- return Promise.reject(e);
- }),
- ]);
-
- // ensure default packages are added to the default conifg
- const agentPolicyWithPackagePolicies = await agentPolicyService.get(
- soClient,
- defaultAgentPolicy.id,
- true
+ for (const installedPackage of installedPackages) {
+ const packageShouldBeInstalled = DEFAULT_AGENT_POLICIES_PACKAGES.some(
+ (packageName) => installedPackage.name === packageName
);
- if (!agentPolicyWithPackagePolicies) {
- throw new Error('Policy not found');
- }
- if (
- agentPolicyWithPackagePolicies.package_policies.length &&
- typeof agentPolicyWithPackagePolicies.package_policies[0] === 'string'
- ) {
- throw new Error('Policy not found');
+ if (!packageShouldBeInstalled) {
+ continue;
}
- for (const installedPackage of installedPackages) {
- const packageShouldBeInstalled = DEFAULT_AGENT_POLICIES_PACKAGES.some(
- (packageName) => installedPackage.name === packageName
- );
- if (!packageShouldBeInstalled) {
- continue;
+
+ const isInstalled = agentPolicyWithPackagePolicies.package_policies.some(
+ (d: PackagePolicy | string) => {
+ return typeof d !== 'string' && d.package?.name === installedPackage.name;
}
+ );
- const isInstalled = agentPolicyWithPackagePolicies.package_policies.some(
- (d: PackagePolicy | string) => {
- return typeof d !== 'string' && d.package?.name === installedPackage.name;
- }
+ if (!isInstalled) {
+ await addPackageToAgentPolicy(
+ soClient,
+ callCluster,
+ installedPackage,
+ agentPolicyWithPackagePolicies,
+ defaultOutput
);
-
- if (!isInstalled) {
- await addPackageToAgentPolicy(
- soClient,
- callCluster,
- installedPackage,
- agentPolicyWithPackagePolicies,
- defaultOutput
- );
- }
}
-
- // if everything works, resolve/succeed
- onSetupResolve();
- } catch (error) {
- // if anything errors, reject/fail
- onSetupReject(error);
}
- // be sure to return the promise because it has the resolved/rejected status attached to it
- // otherwise, we effectively return success every time even if there are errors
- // because `return undefined` -> `Promise.resolve(undefined)` in an `async` function
- return setupIngestStatus;
+ return { isIntialized: true };
}
export async function setupFleet(
diff --git a/x-pack/plugins/ingest_manager/server/services/setup_utils.test.ts b/x-pack/plugins/ingest_manager/server/services/setup_utils.test.ts
new file mode 100644
index 0000000000000..8d71fc48a2129
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/services/setup_utils.test.ts
@@ -0,0 +1,149 @@
+/*
+ * 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 { awaitIfPending } from './setup_utils';
+
+async function sleep(ms: number) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+describe('awaitIfPending', () => {
+ it('first promise called blocks others', async () => {
+ const fnA = jest.fn().mockImplementation(async () => {});
+ const fnB = jest.fn().mockImplementation(async () => {});
+ const fnC = jest.fn().mockImplementation(async () => {});
+ const fnD = jest.fn().mockImplementation(async () => {});
+ const promises = [
+ awaitIfPending(fnA),
+ awaitIfPending(fnB),
+ awaitIfPending(fnC),
+ awaitIfPending(fnD),
+ ];
+ await Promise.all(promises);
+
+ expect(fnA).toHaveBeenCalledTimes(1);
+ expect(fnB).toHaveBeenCalledTimes(0);
+ expect(fnC).toHaveBeenCalledTimes(0);
+ expect(fnD).toHaveBeenCalledTimes(0);
+ });
+
+ describe('first promise created, not necessarily first fulfilled, sets value for all in queue', () => {
+ it('succeeds', async () => {
+ const fnA = jest.fn().mockImplementation(async () => {
+ await sleep(1000);
+ return 'called first';
+ });
+ const fnB = jest.fn().mockImplementation(async () => 'called second');
+ const fnC = jest.fn().mockImplementation(async () => 'called third');
+ const fnD = jest.fn().mockImplementation(async () => 'called fourth');
+ const promises = [
+ awaitIfPending(fnA),
+ awaitIfPending(fnB),
+ awaitIfPending(fnC),
+ awaitIfPending(fnD),
+ ];
+
+ expect(fnA).toHaveBeenCalledTimes(1);
+ expect(fnB).toHaveBeenCalledTimes(0);
+ expect(fnC).toHaveBeenCalledTimes(0);
+ expect(fnD).toHaveBeenCalledTimes(0);
+ await expect(Promise.all(promises)).resolves.toEqual([
+ 'called first',
+ 'called first',
+ 'called first',
+ 'called first',
+ ]);
+ });
+
+ it('throws', async () => {
+ const expectedError = new Error('error is called first');
+ const fnA = jest.fn().mockImplementation(async () => {
+ await sleep(1000);
+ throw expectedError;
+ });
+ const fnB = jest.fn().mockImplementation(async () => 'called second');
+ const fnC = jest.fn().mockImplementation(async () => 'called third');
+ const fnD = jest.fn().mockImplementation(async () => 'called fourth');
+ const promises = [
+ awaitIfPending(fnA),
+ awaitIfPending(fnB),
+ awaitIfPending(fnC),
+ awaitIfPending(fnD),
+ ];
+
+ await expect(Promise.all(promises)).rejects.toThrow(expectedError);
+ await expect(Promise.allSettled(promises)).resolves.toEqual([
+ { status: 'rejected', reason: expectedError },
+ { status: 'rejected', reason: expectedError },
+ { status: 'rejected', reason: expectedError },
+ { status: 'rejected', reason: expectedError },
+ ]);
+
+ expect(fnA).toHaveBeenCalledTimes(1);
+ expect(fnB).toHaveBeenCalledTimes(0);
+ expect(fnC).toHaveBeenCalledTimes(0);
+ expect(fnD).toHaveBeenCalledTimes(0);
+ });
+ });
+
+ it('does not block other calls after batch is fulfilled. can call again for a new result', async () => {
+ const fnA = jest
+ .fn()
+ .mockImplementationOnce(async () => 'fnA first')
+ .mockImplementationOnce(async () => 'fnA second')
+ .mockImplementation(async () => 'fnA default/2+');
+ const fnB = jest.fn().mockImplementation(async () => {});
+ const fnC = jest.fn().mockImplementation(async () => {});
+ const fnD = jest.fn().mockImplementation(async () => {});
+ let promises = [
+ awaitIfPending(fnA),
+ awaitIfPending(fnB),
+ awaitIfPending(fnC),
+ awaitIfPending(fnD),
+ ];
+ let results = await Promise.all(promises);
+
+ expect(fnA).toHaveBeenCalledTimes(1);
+ expect(fnB).toHaveBeenCalledTimes(0);
+ expect(fnC).toHaveBeenCalledTimes(0);
+ expect(fnD).toHaveBeenCalledTimes(0);
+ expect(results).toEqual(['fnA first', 'fnA first', 'fnA first', 'fnA first']);
+
+ promises = [awaitIfPending(fnA), awaitIfPending(fnB), awaitIfPending(fnC), awaitIfPending(fnD)];
+ results = await Promise.all(promises);
+ expect(fnA).toHaveBeenCalledTimes(2);
+ expect(fnB).toHaveBeenCalledTimes(0);
+ expect(fnC).toHaveBeenCalledTimes(0);
+ expect(fnD).toHaveBeenCalledTimes(0);
+ expect(results).toEqual(['fnA second', 'fnA second', 'fnA second', 'fnA second']);
+
+ promises = [awaitIfPending(fnA), awaitIfPending(fnB), awaitIfPending(fnC), awaitIfPending(fnD)];
+ results = await Promise.all(promises);
+ expect(fnA).toHaveBeenCalledTimes(3);
+ expect(fnB).toHaveBeenCalledTimes(0);
+ expect(fnC).toHaveBeenCalledTimes(0);
+ expect(fnD).toHaveBeenCalledTimes(0);
+ expect(results).toEqual([
+ 'fnA default/2+',
+ 'fnA default/2+',
+ 'fnA default/2+',
+ 'fnA default/2+',
+ ]);
+
+ promises = [awaitIfPending(fnA), awaitIfPending(fnB), awaitIfPending(fnC), awaitIfPending(fnD)];
+ results = await Promise.all(promises);
+ expect(fnA).toHaveBeenCalledTimes(4);
+ expect(fnB).toHaveBeenCalledTimes(0);
+ expect(fnC).toHaveBeenCalledTimes(0);
+ expect(fnD).toHaveBeenCalledTimes(0);
+ expect(results).toEqual([
+ 'fnA default/2+',
+ 'fnA default/2+',
+ 'fnA default/2+',
+ 'fnA default/2+',
+ ]);
+ });
+});
diff --git a/x-pack/plugins/ingest_manager/server/services/setup_utils.ts b/x-pack/plugins/ingest_manager/server/services/setup_utils.ts
new file mode 100644
index 0000000000000..3c752bd410c5a
--- /dev/null
+++ b/x-pack/plugins/ingest_manager/server/services/setup_utils.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+// the promise which tracks the setup
+let status: Promise | undefined;
+let isPending = false;
+// default resolve to guard against "undefined is not a function" errors
+let onResolve = (value?: unknown) => {};
+let onReject = (reason: any) => {};
+
+export async function awaitIfPending(asyncFunction: Function): Promise {
+ // pending successful or failed attempt
+ if (isPending) {
+ // don't run concurrent installs
+ // return a promise which will eventually resolve/reject
+ return status;
+ } else {
+ // create the initial promise
+ status = new Promise((res, rej) => {
+ isPending = true;
+ onResolve = res;
+ onReject = rej;
+ });
+ }
+ try {
+ const result = await asyncFunction().catch(onReject);
+ onResolve(result);
+ } catch (error) {
+ // if something fails
+ onReject(error);
+ }
+ isPending = false;
+ return status;
+}
diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.d.ts b/x-pack/plugins/maps/common/elasticsearch_geo_utils.d.ts
similarity index 70%
rename from x-pack/plugins/maps/public/elasticsearch_geo_utils.d.ts
rename to x-pack/plugins/maps/common/elasticsearch_geo_utils.d.ts
index 964afb589187f..44250360e9d00 100644
--- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.d.ts
+++ b/x-pack/plugins/maps/common/elasticsearch_geo_utils.d.ts
@@ -4,8 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { MapExtent } from '../common/descriptor_types';
+import { MapExtent } from './descriptor_types';
export function scaleBounds(bounds: MapExtent, scaleFactor: number): MapExtent;
export function turfBboxToBounds(turfBbox: unknown): MapExtent;
+
+export function clampToLatBounds(lat: number): number;
+
+export function clampToLonBounds(lon: number): number;
diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/plugins/maps/common/elasticsearch_geo_utils.js
similarity index 98%
rename from x-pack/plugins/maps/public/elasticsearch_geo_utils.js
rename to x-pack/plugins/maps/common/elasticsearch_geo_utils.js
index b32125e6eb614..f2bf83ae18bb0 100644
--- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js
+++ b/x-pack/plugins/maps/common/elasticsearch_geo_utils.js
@@ -16,10 +16,12 @@ import {
LON_INDEX,
LAT_INDEX,
} from '../common/constants';
-import { getEsSpatialRelationLabel } from '../common/i18n_getters';
-import { SPATIAL_FILTER_TYPE } from './kibana_services';
+import { getEsSpatialRelationLabel } from './i18n_getters';
+import { FILTERS } from '../../../../src/plugins/data/common';
import turfCircle from '@turf/circle';
+const SPATIAL_FILTER_TYPE = FILTERS.SPATIAL_FILTER;
+
function ensureGeoField(type) {
const expectedTypes = [ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE];
if (!expectedTypes.includes(type)) {
diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/common/elasticsearch_geo_utils.test.js
similarity index 97%
rename from x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js
rename to x-pack/plugins/maps/common/elasticsearch_geo_utils.test.js
index 6a4b4b78c829e..a8d5d650740cd 100644
--- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js
+++ b/x-pack/plugins/maps/common/elasticsearch_geo_utils.test.js
@@ -4,14 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-jest.mock('ui/new_platform');
-
-jest.mock('./kibana_services', () => {
- return {
- SPATIAL_FILTER_TYPE: 'spatial_filter',
- };
-});
-
import {
hitsToGeoJson,
geoPointToGeometry,
@@ -22,7 +14,7 @@ import {
makeESBbox,
scaleBounds,
} from './elasticsearch_geo_utils';
-import { indexPatterns } from '../../../../src/plugins/data/public';
+import _ from 'lodash';
const geoFieldName = 'location';
@@ -173,19 +165,14 @@ describe('hitsToGeoJson', () => {
});
describe('dot in geoFieldName', () => {
- const indexPatternMock = {
- fields: {
- getByName: (name) => {
- const fields = {
- ['my.location']: {
- type: 'geo_point',
- },
- };
- return fields[name];
- },
- },
+ // This essentially should test the implmentation of index-pattern.flattenHit, rather than anything in geo_utils.
+ // Leaving this here for reference.
+ const geoFieldName = 'my.location';
+ const indexPatternFlattenHit = (hit) => {
+ return {
+ [geoFieldName]: _.get(hit._source, geoFieldName),
+ };
};
- const indexPatternFlattenHit = indexPatterns.flattenHitWrapper(indexPatternMock);
it('Should handle geoField being an object', () => {
const hits = [
diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts
index a22e8d582bc5e..6f5ed680ac64f 100644
--- a/x-pack/plugins/maps/public/actions/data_request_actions.ts
+++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts
@@ -41,7 +41,7 @@ import { ILayer } from '../classes/layers/layer';
import { IVectorLayer } from '../classes/layers/vector_layer/vector_layer';
import { DataMeta, MapExtent, MapFilters } from '../../common/descriptor_types';
import { DataRequestAbortError } from '../classes/util/data_request';
-import { scaleBounds, turfBboxToBounds } from '../elasticsearch_geo_utils';
+import { scaleBounds, turfBboxToBounds } from '../../common/elasticsearch_geo_utils';
const FIT_TO_BOUNDS_SCALE_FACTOR = 0.1;
diff --git a/x-pack/plugins/maps/public/actions/map_actions.ts b/x-pack/plugins/maps/public/actions/map_actions.ts
index 7ba58307e1952..f408896853155 100644
--- a/x-pack/plugins/maps/public/actions/map_actions.ts
+++ b/x-pack/plugins/maps/public/actions/map_actions.ts
@@ -54,7 +54,7 @@ import {
MapRefreshConfig,
} from '../../common/descriptor_types';
import { INITIAL_LOCATION } from '../../common/constants';
-import { scaleBounds } from '../elasticsearch_geo_utils';
+import { scaleBounds } from '../../common/elasticsearch_geo_utils';
export function setMapInitError(errorMessage: string) {
return {
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.js
index a95a8be4b24c8..35dbebdfd3c8a 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.js
@@ -8,7 +8,7 @@ import _ from 'lodash';
import { RENDER_AS } from '../../../../common/constants';
import { getTileBoundingBox } from './geo_tile_utils';
import { extractPropertiesFromBucket } from '../../util/es_agg_utils';
-import { clamp } from '../../../elasticsearch_geo_utils';
+import { clamp } from '../../../../common/elasticsearch_geo_utils';
const GRID_BUCKET_KEYS_TO_IGNORE = ['key', 'gridCentroid'];
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js
index a4dba71307b71..a6322ff3ba784 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js
@@ -21,7 +21,7 @@ import { getDataSourceLabel } from '../../../../common/i18n_getters';
import { AbstractESAggSource, DEFAULT_METRIC } from '../es_agg_source';
import { DataRequestAbortError } from '../../util/data_request';
import { registerSource } from '../source_registry';
-import { makeESBbox } from '../../../elasticsearch_geo_utils';
+import { makeESBbox } from '../../../../common/elasticsearch_geo_utils';
export const MAX_GEOTILE_LEVEL = 29;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.js
index 251e33b9579cb..89b24522e4275 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.js
@@ -6,7 +6,7 @@
import _ from 'lodash';
import { DECIMAL_DEGREES_PRECISION } from '../../../../common/constants';
-import { clampToLatBounds } from '../../../elasticsearch_geo_utils';
+import { clampToLatBounds } from '../../../../common/elasticsearch_geo_utils';
const ZOOM_TILE_KEY_INDEX = 0;
const X_TILE_KEY_INDEX = 1;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js
index 79eccf09b2888..92b0c717f6724 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js
@@ -16,7 +16,7 @@ import { getDataSourceLabel } from '../../../../common/i18n_getters';
import { convertToLines } from './convert_to_lines';
import { AbstractESAggSource, DEFAULT_METRIC } from '../es_agg_source';
import { registerSource } from '../source_registry';
-import { turfBboxToBounds } from '../../../elasticsearch_geo_utils';
+import { turfBboxToBounds } from '../../../../common/elasticsearch_geo_utils';
import { DataRequestAbortError } from '../../util/data_request';
const MAX_GEOTILE_LEVEL = 29;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.js b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.js
index 256becf70ffb0..6d61c4a7455b2 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.js
@@ -9,7 +9,7 @@ import React from 'react';
import { AbstractESSource } from '../es_source';
import { getSearchService } from '../../../kibana_services';
-import { hitsToGeoJson } from '../../../elasticsearch_geo_utils';
+import { hitsToGeoJson } from '../../../../common/elasticsearch_geo_utils';
import { UpdateSourceEditor } from './update_source_editor';
import {
SOURCE_TYPES,
diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
index 866e3c76c2a3f..8cc2aa018979b 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
@@ -11,7 +11,7 @@ import {
getTimeFilter,
getSearchService,
} from '../../../kibana_services';
-import { createExtentFilter } from '../../../elasticsearch_geo_utils';
+import { createExtentFilter } from '../../../../common/elasticsearch_geo_utils';
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import uuid from 'uuid/v4';
diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js
index e1779c1afbf47..b0ce52b4db7ab 100644
--- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js
+++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js
@@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { URL_MAX_LENGTH } from '../../../../../../../src/core/public';
-import { createSpatialFilterWithGeometry } from '../../../elasticsearch_geo_utils';
+import { createSpatialFilterWithGeometry } from '../../../../common/elasticsearch_geo_utils';
import { GEO_JSON_TYPE } from '../../../../common/constants';
import { GeometryFilterForm } from '../../../components/geometry_filter_form';
diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js
index 2daa4b2c900f5..6de936fa4a8f1 100644
--- a/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js
+++ b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js
@@ -15,7 +15,7 @@ import {
createSpatialFilterWithGeometry,
getBoundingBoxGeometry,
roundCoordinates,
-} from '../../../../elasticsearch_geo_utils';
+} from '../../../../../common/elasticsearch_geo_utils';
import { DrawTooltip } from './draw_tooltip';
const DRAW_RECTANGLE = 'draw_rectangle';
diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js
index d85959c3a08a4..5a38f6039ae4b 100644
--- a/x-pack/plugins/maps/public/connected_components/map/mb/view.js
+++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js
@@ -19,7 +19,7 @@ import sprites1 from '@elastic/maki/dist/sprite@1.png';
import sprites2 from '@elastic/maki/dist/sprite@2.png';
import { DrawControl } from './draw_control';
import { TooltipControl } from './tooltip_control';
-import { clampToLatBounds, clampToLonBounds } from '../../../elasticsearch_geo_utils';
+import { clampToLatBounds, clampToLonBounds } from '../../../../common/elasticsearch_geo_utils';
import { getInitialView } from './get_initial_view';
import { getPreserveDrawingBuffer } from '../../../kibana_services';
diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts
index f8f89ebaed102..239a2898a06fc 100644
--- a/x-pack/plugins/maps/public/kibana_services.ts
+++ b/x-pack/plugins/maps/public/kibana_services.ts
@@ -5,14 +5,11 @@
*/
import _ from 'lodash';
-import { esFilters } from '../../../../src/plugins/data/public';
import { MapsLegacyConfigType } from '../../../../src/plugins/maps_legacy/public';
import { MapsConfigType } from '../config';
import { MapsPluginStartDependencies } from './plugin';
import { CoreStart } from '../../../../src/core/public';
-export const SPATIAL_FILTER_TYPE = esFilters.FILTERS.SPATIAL_FILTER;
-
let licenseId: string | undefined;
export const setLicenseId = (latestLicenseId: string | undefined) => (licenseId = latestLicenseId);
export const getLicenseId = () => licenseId;
diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts
index 40ffda3f31c26..d48ee24027561 100644
--- a/x-pack/plugins/maps/public/selectors/map_selectors.ts
+++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts
@@ -32,7 +32,7 @@ import {
SPATIAL_FILTERS_LAYER_ID,
} from '../../common/constants';
// @ts-ignore
-import { extractFeaturesFromFilters } from '../elasticsearch_geo_utils';
+import { extractFeaturesFromFilters } from '../../common/elasticsearch_geo_utils';
import { MapStoreState } from '../reducers/store';
import {
DataRequestDescriptor,
diff --git a/x-pack/plugins/monitoring/server/license_service.ts b/x-pack/plugins/monitoring/server/license_service.ts
index fb45abc22afa4..ecadf9d2d85db 100644
--- a/x-pack/plugins/monitoring/server/license_service.ts
+++ b/x-pack/plugins/monitoring/server/license_service.ts
@@ -33,14 +33,13 @@ export class LicenseService {
let rawLicense: Readonly | undefined;
let licenseSubscription: Subscription | undefined = license$.subscribe((nextRawLicense) => {
rawLicense = nextRawLicense;
+ if (!rawLicense?.isAvailable) {
+ log.warn(
+ `X-Pack Monitoring Cluster Alerts will not be available: ${rawLicense?.getUnavailableReason()}`
+ );
+ }
});
- if (!rawLicense?.isAvailable) {
- log.warn(
- `X-Pack Monitoring Cluster Alerts will not be available: ${rawLicense?.getUnavailableReason()}`
- );
- }
-
return {
refresh,
license$,
diff --git a/x-pack/plugins/monitoring/server/plugin.test.ts b/x-pack/plugins/monitoring/server/plugin.test.ts
new file mode 100644
index 0000000000000..13da0a3c9242e
--- /dev/null
+++ b/x-pack/plugins/monitoring/server/plugin.test.ts
@@ -0,0 +1,140 @@
+/*
+ * 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 { Plugin } from './plugin';
+import { combineLatest } from 'rxjs';
+// @ts-ignore
+import { initBulkUploader } from './kibana_monitoring';
+import { AlertsFactory } from './alerts';
+
+jest.mock('rxjs', () => ({
+ // @ts-ignore
+ ...jest.requireActual('rxjs'),
+ combineLatest: jest.fn(),
+}));
+
+jest.mock('./es_client/instantiate_client', () => ({
+ instantiateClient: jest.fn(),
+}));
+
+jest.mock('./license_service', () => ({
+ LicenseService: jest.fn().mockImplementation(() => ({
+ setup: jest.fn().mockImplementation(() => ({
+ refresh: jest.fn(),
+ })),
+ })),
+}));
+
+jest.mock('./kibana_monitoring', () => ({
+ initBulkUploader: jest.fn(),
+}));
+
+describe('Monitoring plugin', () => {
+ const initializerContext = {
+ logger: {
+ get: jest.fn().mockImplementation(() => ({
+ info: jest.fn(),
+ })),
+ },
+ config: {
+ create: jest.fn().mockImplementation(() => ({
+ pipe: jest.fn().mockImplementation(() => ({
+ toPromise: jest.fn(),
+ })),
+ })),
+ legacy: {
+ globalConfig$: {},
+ },
+ },
+ env: {
+ packageInfo: {
+ version: '1.0.0',
+ },
+ },
+ };
+
+ const coreSetup = {
+ http: {
+ createRouter: jest.fn(),
+ getServerInfo: jest.fn().mockImplementation(() => ({
+ port: 5601,
+ })),
+ basePath: {
+ serverBasePath: '',
+ },
+ },
+ uuid: {
+ getInstanceUuid: jest.fn(),
+ },
+ elasticsearch: {
+ legacy: {
+ client: {},
+ createClient: jest.fn(),
+ },
+ },
+ };
+
+ const setupPlugins = {
+ usageCollection: {
+ getCollectorByType: jest.fn(),
+ makeStatsCollector: jest.fn(),
+ registerCollector: jest.fn(),
+ },
+ alerts: {
+ registerType: jest.fn(),
+ },
+ };
+
+ let config = {};
+ const defaultConfig = {
+ ui: {
+ elasticsearch: {},
+ },
+ kibana: {
+ collection: {
+ interval: 30000,
+ },
+ },
+ };
+
+ beforeEach(() => {
+ config = defaultConfig;
+ (combineLatest as jest.Mock).mockImplementation(() => {
+ return {
+ pipe: jest.fn().mockImplementation(() => {
+ return {
+ toPromise: jest.fn().mockImplementation(() => {
+ return [config, 2];
+ }),
+ };
+ }),
+ };
+ });
+ });
+
+ afterEach(() => {
+ (setupPlugins.alerts.registerType as jest.Mock).mockReset();
+ });
+
+ it('always create the bulk uploader', async () => {
+ const setKibanaStatusGetter = jest.fn();
+ (initBulkUploader as jest.Mock).mockImplementation(() => {
+ return {
+ setKibanaStatusGetter,
+ };
+ });
+ const plugin = new Plugin(initializerContext as any);
+ const contract = await plugin.setup(coreSetup as any, setupPlugins as any);
+ contract.registerLegacyAPI(null as any);
+ expect(setKibanaStatusGetter).toHaveBeenCalled();
+ });
+
+ it('should register all alerts', async () => {
+ const alerts = AlertsFactory.getAll();
+ const plugin = new Plugin(initializerContext as any);
+ await plugin.setup(coreSetup as any, setupPlugins as any);
+ expect(setupPlugins.alerts.registerType).toHaveBeenCalledTimes(alerts.length);
+ });
+});
diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts
index 3aedb6831e7ab..043435c48a211 100644
--- a/x-pack/plugins/monitoring/server/plugin.ts
+++ b/x-pack/plugins/monitoring/server/plugin.ts
@@ -71,7 +71,7 @@ export class Plugin {
private licenseService = {} as MonitoringLicenseService;
private monitoringCore = {} as MonitoringCore;
private legacyShimDependencies = {} as LegacyShimDependencies;
- private bulkUploader = {} as IBulkUploader;
+ private bulkUploader: IBulkUploader = {} as IBulkUploader;
constructor(initializerContext: PluginInitializerContext) {
this.initializerContext = initializerContext;
@@ -152,28 +152,28 @@ export class Plugin {
registerCollectors(plugins.usageCollection, config);
}
- // If collection is enabled, create the bulk uploader
+ // Always create the bulk uploader
const kibanaMonitoringLog = this.getLogger(KIBANA_MONITORING_LOGGING_TAG);
+ const bulkUploader = (this.bulkUploader = initBulkUploader({
+ elasticsearch: core.elasticsearch,
+ config,
+ log: kibanaMonitoringLog,
+ kibanaStats: {
+ uuid: core.uuid.getInstanceUuid(),
+ name: serverInfo.name,
+ index: get(legacyConfig, 'kibana.index'),
+ host: serverInfo.hostname,
+ locale: i18n.getLocale(),
+ port: serverInfo.port.toString(),
+ transport_address: `${serverInfo.hostname}:${serverInfo.port}`,
+ version: this.initializerContext.env.packageInfo.version,
+ snapshot: snapshotRegex.test(this.initializerContext.env.packageInfo.version),
+ },
+ }));
+
+ // If collection is enabled, start it
const kibanaCollectionEnabled = config.kibana.collection.enabled;
if (kibanaCollectionEnabled) {
- // Start kibana internal collection
- const bulkUploader = (this.bulkUploader = initBulkUploader({
- elasticsearch: core.elasticsearch,
- config,
- log: kibanaMonitoringLog,
- kibanaStats: {
- uuid: core.uuid.getInstanceUuid(),
- name: serverInfo.name,
- index: get(legacyConfig, 'kibana.index'),
- host: serverInfo.hostname,
- locale: i18n.getLocale(),
- port: serverInfo.port.toString(),
- transport_address: `${serverInfo.hostname}:${serverInfo.port}`,
- version: this.initializerContext.env.packageInfo.version,
- snapshot: snapshotRegex.test(this.initializerContext.env.packageInfo.version),
- },
- }));
-
// Do not use `this.licenseService` as that looks at the monitoring cluster
// whereas we want to check the production cluster here
if (plugins.licensing) {
@@ -188,6 +188,10 @@ export class Plugin {
bulkUploader.handleNotEnabled();
}
});
+ } else {
+ kibanaMonitoringLog.warn(
+ 'Internal collection for Kibana monitoring is disabled due to missing license information.'
+ );
}
} else {
kibanaMonitoringLog.info(
diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts
index 1cf879adc5415..ca191602dcf44 100644
--- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts
+++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts
@@ -135,26 +135,36 @@ const expectPrivilegeCheck = async (fn: Function, args: Record) =>
);
};
-const expectObjectNamespaceFiltering = async (fn: Function, args: Record) => {
- clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementationOnce(
- getMockCheckPrivilegesSuccess // privilege check for authorization
- );
+const expectObjectNamespaceFiltering = async (
+ fn: Function,
+ args: Record,
+ privilegeChecks = 1
+) => {
+ for (let i = 0; i < privilegeChecks; i++) {
+ clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementationOnce(
+ getMockCheckPrivilegesSuccess // privilege check for authorization
+ );
+ }
clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementation(
getMockCheckPrivilegesFailure // privilege check for namespace filtering
);
- const authorizedNamespace = args.options.namespace || 'default';
+ const authorizedNamespace = args.options?.namespace || 'default';
const namespaces = ['some-other-namespace', authorizedNamespace];
const returnValue = { namespaces, foo: 'bar' };
// we don't know which base client method will be called; mock them all
clientOpts.baseClient.create.mockReturnValue(returnValue as any);
clientOpts.baseClient.get.mockReturnValue(returnValue as any);
clientOpts.baseClient.update.mockReturnValue(returnValue as any);
+ clientOpts.baseClient.addToNamespaces.mockReturnValue(returnValue as any);
+ clientOpts.baseClient.deleteFromNamespaces.mockReturnValue(returnValue as any);
const result = await fn.bind(client)(...Object.values(args));
expect(result).toEqual(expect.objectContaining({ namespaces: [authorizedNamespace, '?'] }));
- expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledTimes(2);
+ expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledTimes(
+ privilegeChecks + 1
+ );
expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenLastCalledWith(
'login:',
namespaces
@@ -369,6 +379,11 @@ describe('#addToNamespaces', () => {
undefined // default namespace
);
});
+
+ test(`filters namespaces that the user doesn't have access to`, async () => {
+ // this operation is unique because it requires two privilege checks before it executes
+ await expectObjectNamespaceFiltering(client.addToNamespaces, { type, id, namespaces }, 2);
+ });
});
describe('#bulkCreate', () => {
@@ -682,6 +697,10 @@ describe('#deleteFromNamespaces', () => {
namespaces
);
});
+
+ test(`filters namespaces that the user doesn't have access to`, async () => {
+ await expectObjectNamespaceFiltering(client.deleteFromNamespaces, { type, id, namespaces });
+ });
});
describe('#update', () => {
diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
index 621299a0f025e..9fd8a732c4eab 100644
--- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
+++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts
@@ -164,7 +164,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
// result in a 404 error.
await this.ensureAuthorized(type, 'update', namespace, args, 'addToNamespacesUpdate');
- return await this.baseClient.addToNamespaces(type, id, namespaces, options);
+ const result = await this.baseClient.addToNamespaces(type, id, namespaces, options);
+ return await this.redactSavedObjectNamespaces(result);
}
public async deleteFromNamespaces(
@@ -177,7 +178,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra
// To un-share an object, the user must have the "delete" permission in each of the target namespaces.
await this.ensureAuthorized(type, 'delete', namespaces, args, 'deleteFromNamespaces');
- return await this.baseClient.deleteFromNamespaces(type, id, namespaces, options);
+ const result = await this.baseClient.deleteFromNamespaces(type, id, namespaces, options);
+ return await this.redactSavedObjectNamespaces(result);
}
public async bulkUpdate(
diff --git a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts
index cd4573817cc27..5f2de69689865 100644
--- a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts
@@ -10,6 +10,7 @@ import {
FIELDS_BROWSER_SELECTED_CATEGORY_TITLE,
} from '../screens/fields_browser';
import {
+ EVENTS_PAGE,
HEADER_SUBTITLE,
HOST_GEO_CITY_NAME_HEADER,
HOST_GEO_COUNTRY_NAME_HEADER,
@@ -153,7 +154,7 @@ describe('Events Viewer', () => {
});
});
- context.skip('Events columns', () => {
+ context('Events columns', () => {
before(() => {
loginAndWaitForPage(HOSTS_URL);
openEvents();
@@ -171,6 +172,7 @@ describe('Events Viewer', () => {
const expectedOrderAfterDragAndDrop =
'message@timestamphost.nameevent.moduleevent.datasetevent.actionuser.namesource.ipdestination.ip';
+ cy.get(EVENTS_PAGE).scrollTo('bottom');
cy.get(HEADERS_GROUP).invoke('text').should('equal', originalColumnOrder);
dragAndDropColumn({ column: 0, newPosition: 1 });
cy.get(HEADERS_GROUP).invoke('text').should('equal', expectedOrderAfterDragAndDrop);
diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts
index 2fb265c55e3ad..383ebe2220585 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts
@@ -13,8 +13,7 @@ import { TABLE_COLUMN_EVENTS_MESSAGE } from '../screens/hosts/external_events';
import { waitsForEventsToBeLoaded, openEventsViewerFieldsBrowser } from '../tasks/hosts/events';
import { removeColumn, resetFields } from '../tasks/timeline';
-// FLAKY: https://github.com/elastic/kibana/issues/72339
-describe.skip('persistent timeline', () => {
+describe('persistent timeline', () => {
before(() => {
loginAndWaitForPage(HOSTS_URL);
openEvents();
diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts
index 4b1ca19bd96fe..05f517b5de662 100644
--- a/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts
@@ -6,6 +6,8 @@
export const CLOSE_MODAL = '[data-test-subj="modal-inspect-close"]';
+export const EVENTS_PAGE = '[data-test-subj="pageContainer"]';
+
export const EVENTS_VIEWER_FIELDS_BUTTON =
'[data-test-subj="events-viewer-panel"] [data-test-subj="show-field-browser"]';
diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts
index 68425731f149a..135dea35ca0d8 100644
--- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts
@@ -11,6 +11,8 @@ export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]';
export const DRAGGABLE_HEADER =
'[data-test-subj="events-viewer-panel"] [data-test-subj="headers-group"] [data-test-subj="draggable-header"]';
+export const HEADER = '[data-test-subj="header"]';
+
export const HEADERS_GROUP = '[data-test-subj="headers-group"]';
export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]';
@@ -23,8 +25,7 @@ export const PIN_EVENT = '[data-test-subj="pin"]';
export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]';
-export const REMOVE_COLUMN =
- '[data-test-subj="events-viewer-panel"] [data-test-subj="remove-column"]';
+export const REMOVE_COLUMN = '[data-test-subj="remove-column"]';
export const RESET_FIELDS =
'[data-test-subj="events-viewer-panel"] [data-test-subj="reset-fields"]';
diff --git a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts
index 1d2c4aa8d0834..226178cd92f18 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts
@@ -68,8 +68,6 @@ export const dragAndDropColumn = ({
.eq(column)
.then((header) => drag(header));
- cy.wait(5000); // wait for DOM updates before moving
-
cy.get(DRAGGABLE_HEADER)
.eq(newPosition)
.then((targetPosition) => {
diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
index d5106f34cc9c5..9eeb9fc8bdf8a 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
@@ -9,6 +9,7 @@ import { DATE_PICKER_APPLY_BUTTON_TIMELINE } from '../screens/date_picker';
import {
CLOSE_TIMELINE_BTN,
CREATE_NEW_TIMELINE,
+ HEADER,
ID_FIELD,
ID_HEADER_FIELD,
ID_TOGGLE_FIELD,
@@ -114,7 +115,7 @@ export const dragAndDropIdToggleFieldToTimeline = () => {
};
export const removeColumn = (column: number) => {
- cy.get(REMOVE_COLUMN).first().should('exist');
+ cy.get(HEADER).eq(column).click();
cy.get(REMOVE_COLUMN).eq(column).click({ force: true });
};
diff --git a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts
index 4d0d75cd4595c..51c59212bef16 100644
--- a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts
+++ b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts
@@ -423,7 +423,7 @@ const ERROR_NAMESPACE_SPECIFIED = 'Spaces currently determines the namespaces';
test(`supplements options with the current namespace`, async () => {
const { client, baseClient } = await createSpacesSavedObjectsClient();
- const expectedReturnValue = createMockResponse();
+ const expectedReturnValue = { namespaces: ['foo', 'bar'] };
baseClient.addToNamespaces.mockReturnValue(Promise.resolve(expectedReturnValue));
const type = Symbol();
@@ -453,7 +453,7 @@ const ERROR_NAMESPACE_SPECIFIED = 'Spaces currently determines the namespaces';
test(`supplements options with the current namespace`, async () => {
const { client, baseClient } = await createSpacesSavedObjectsClient();
- const expectedReturnValue = createMockResponse();
+ const expectedReturnValue = { namespaces: ['foo', 'bar'] };
baseClient.deleteFromNamespaces.mockReturnValue(Promise.resolve(expectedReturnValue));
const type = Symbol();
diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts
index 70b6a8fe512e1..3c8fc78b7f872 100644
--- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts
+++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts
@@ -77,7 +77,8 @@ export default function servicenowTest({ getService }: FtrProviderContext) {
let proxyServer: any;
let proxyHaveBeenCalled = false;
- describe('ServiceNow', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/75522
+ describe.skip('ServiceNow', () => {
before(() => {
servicenowSimulatorURL = kibanaServer.resolveUrl(
getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW)
diff --git a/x-pack/test/api_integration/apis/ml/annotations/create_annotations.ts b/x-pack/test/api_integration/apis/ml/annotations/create_annotations.ts
index aff1150997496..6b8fbe8350a43 100644
--- a/x-pack/test/api_integration/apis/ml/annotations/create_annotations.ts
+++ b/x-pack/test/api_integration/apis/ml/annotations/create_annotations.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { USER } from '../../../../functional/services/ml/security_common';
import { Annotation } from '../../../../../plugins/ml/common/types/annotations';
import { createJobConfig, createAnnotationRequestBody } from './common_jobs';
diff --git a/x-pack/test/api_integration/apis/ml/annotations/delete_annotations.ts b/x-pack/test/api_integration/apis/ml/annotations/delete_annotations.ts
index d3451c4d7da0c..5e55fb616cbfb 100644
--- a/x-pack/test/api_integration/apis/ml/annotations/delete_annotations.ts
+++ b/x-pack/test/api_integration/apis/ml/annotations/delete_annotations.ts
@@ -6,7 +6,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { USER } from '../../../../functional/services/ml/security_common';
import { testSetupJobConfigs, jobIds, testSetupAnnotations } from './common_jobs';
diff --git a/x-pack/test/api_integration/apis/ml/annotations/get_annotations.ts b/x-pack/test/api_integration/apis/ml/annotations/get_annotations.ts
index 29ad905bd3f2d..b720477abb350 100644
--- a/x-pack/test/api_integration/apis/ml/annotations/get_annotations.ts
+++ b/x-pack/test/api_integration/apis/ml/annotations/get_annotations.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { omit } from 'lodash';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { USER } from '../../../../functional/services/ml/security_common';
import { testSetupJobConfigs, jobIds, testSetupAnnotations } from './common_jobs';
diff --git a/x-pack/test/api_integration/apis/ml/annotations/update_annotations.ts b/x-pack/test/api_integration/apis/ml/annotations/update_annotations.ts
index bcfb7ab0825b8..a1e4bcac31807 100644
--- a/x-pack/test/api_integration/apis/ml/annotations/update_annotations.ts
+++ b/x-pack/test/api_integration/apis/ml/annotations/update_annotations.ts
@@ -6,7 +6,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { USER } from '../../../../functional/services/ml/security_common';
import { ANNOTATION_TYPE } from '../../../../../plugins/ml/common/constants/annotations';
import { Annotation } from '../../../../../plugins/ml/common/types/annotations';
diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts
index 71703ed019dc5..8eb4d7f756fe5 100644
--- a/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts
+++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts
index e17870d8c6408..58202ea9b35b3 100644
--- a/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts
+++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/get.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/calendars/create_calendars.ts b/x-pack/test/api_integration/apis/ml/calendars/create_calendars.ts
index 82f4eee8cc328..e351eafea6e7f 100644
--- a/x-pack/test/api_integration/apis/ml/calendars/create_calendars.ts
+++ b/x-pack/test/api_integration/apis/ml/calendars/create_calendars.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
diff --git a/x-pack/test/api_integration/apis/ml/calendars/delete_calendars.ts b/x-pack/test/api_integration/apis/ml/calendars/delete_calendars.ts
index eef8479b811b4..575c7c99dde17 100644
--- a/x-pack/test/api_integration/apis/ml/calendars/delete_calendars.ts
+++ b/x-pack/test/api_integration/apis/ml/calendars/delete_calendars.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
diff --git a/x-pack/test/api_integration/apis/ml/calendars/get_calendars.ts b/x-pack/test/api_integration/apis/ml/calendars/get_calendars.ts
index 0b4f4a8f73ede..a5e65028196fe 100644
--- a/x-pack/test/api_integration/apis/ml/calendars/get_calendars.ts
+++ b/x-pack/test/api_integration/apis/ml/calendars/get_calendars.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
diff --git a/x-pack/test/api_integration/apis/ml/calendars/update_calendars.ts b/x-pack/test/api_integration/apis/ml/calendars/update_calendars.ts
index 65832ac9ca81e..69a8eb3e06ee8 100644
--- a/x-pack/test/api_integration/apis/ml/calendars/update_calendars.ts
+++ b/x-pack/test/api_integration/apis/ml/calendars/update_calendars.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts
index 2a39bc14fbb7f..c6043b7a282d4 100644
--- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts
+++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts
@@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
import { DataFrameAnalyticsConfig } from '../../../../../plugins/ml/public/application/data_frame_analytics/common';
import { DeepPartial } from '../../../../../plugins/ml/common/types/common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/get.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/get.ts
index d3e4788ee41da..ebae5a80c2337 100644
--- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/get.ts
+++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/get.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { DataFrameAnalyticsConfig } from '../../../../../plugins/ml/public/application/data_frame_analytics/common';
import { DeepPartial } from '../../../../../plugins/ml/common/types/common';
diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/update.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/update.ts
index 5dc781657619d..f4964308cd8c9 100644
--- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/update.ts
+++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/update.ts
@@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
import { DataFrameAnalyticsConfig } from '../../../../../plugins/ml/public/application/data_frame_analytics/common';
import { DeepPartial } from '../../../../../plugins/ml/common/types/common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_histograms.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_histograms.ts
index 1a71894f8423d..299f5f93fd281 100644
--- a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_histograms.ts
+++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_histograms.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts
index 5373da6a794c7..5795eac9637b1 100644
--- a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts
+++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts
index d87ab16d71c18..fa83807be161a 100644
--- a/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts
+++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts b/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts
index ced4d937863ee..627d9454beeb6 100644
--- a/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts
+++ b/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts b/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts
index 2128b1fe8d9e1..b1c086ddbb456 100644
--- a/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts
+++ b/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/filters/create_filters.ts b/x-pack/test/api_integration/apis/ml/filters/create_filters.ts
index 233c95b190f02..dfec7798ffc0c 100644
--- a/x-pack/test/api_integration/apis/ml/filters/create_filters.ts
+++ b/x-pack/test/api_integration/apis/ml/filters/create_filters.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
diff --git a/x-pack/test/api_integration/apis/ml/filters/delete_filters.ts b/x-pack/test/api_integration/apis/ml/filters/delete_filters.ts
index d0323360400be..7a55c2308eb9d 100644
--- a/x-pack/test/api_integration/apis/ml/filters/delete_filters.ts
+++ b/x-pack/test/api_integration/apis/ml/filters/delete_filters.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
diff --git a/x-pack/test/api_integration/apis/ml/filters/get_filters.ts b/x-pack/test/api_integration/apis/ml/filters/get_filters.ts
index f0aa7aac7b9e4..5d7900ea5e9d9 100644
--- a/x-pack/test/api_integration/apis/ml/filters/get_filters.ts
+++ b/x-pack/test/api_integration/apis/ml/filters/get_filters.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
diff --git a/x-pack/test/api_integration/apis/ml/filters/update_filters.ts b/x-pack/test/api_integration/apis/ml/filters/update_filters.ts
index 87eec99906c34..fbbb94d54c035 100644
--- a/x-pack/test/api_integration/apis/ml/filters/update_filters.ts
+++ b/x-pack/test/api_integration/apis/ml/filters/update_filters.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertestWithoutAuth');
diff --git a/x-pack/test/api_integration/apis/ml/job_validation/bucket_span_estimator.ts b/x-pack/test/api_integration/apis/ml/job_validation/bucket_span_estimator.ts
index c556a6c28554b..a03432232a7d8 100644
--- a/x-pack/test/api_integration/apis/ml/job_validation/bucket_span_estimator.ts
+++ b/x-pack/test/api_integration/apis/ml/job_validation/bucket_span_estimator.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/job_validation/calculate_model_memory_limit.ts b/x-pack/test/api_integration/apis/ml/job_validation/calculate_model_memory_limit.ts
index 409bd161e601b..30a387fe33b0e 100644
--- a/x-pack/test/api_integration/apis/ml/job_validation/calculate_model_memory_limit.ts
+++ b/x-pack/test/api_integration/apis/ml/job_validation/calculate_model_memory_limit.ts
@@ -6,7 +6,7 @@
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/job_validation/cardinality.ts b/x-pack/test/api_integration/apis/ml/job_validation/cardinality.ts
index ed61f234a671d..ddf752bd876cb 100644
--- a/x-pack/test/api_integration/apis/ml/job_validation/cardinality.ts
+++ b/x-pack/test/api_integration/apis/ml/job_validation/cardinality.ts
@@ -6,7 +6,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/job_validation/validate.ts b/x-pack/test/api_integration/apis/ml/job_validation/validate.ts
index 5e9b2d68bd6df..dab69498efc77 100644
--- a/x-pack/test/api_integration/apis/ml/job_validation/validate.ts
+++ b/x-pack/test/api_integration/apis/ml/job_validation/validate.ts
@@ -6,7 +6,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import pkg from '../../../../../../package.json';
export default ({ getService }: FtrProviderContext) => {
diff --git a/x-pack/test/api_integration/apis/ml/jobs/categorization_field_examples.ts b/x-pack/test/api_integration/apis/ml/jobs/categorization_field_examples.ts
index b99a4965adb9d..ccab65e1576a2 100644
--- a/x-pack/test/api_integration/apis/ml/jobs/categorization_field_examples.ts
+++ b/x-pack/test/api_integration/apis/ml/jobs/categorization_field_examples.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
const start = 1554463535770;
const end = 1574316073914;
diff --git a/x-pack/test/api_integration/apis/ml/jobs/close_jobs.ts b/x-pack/test/api_integration/apis/ml/jobs/close_jobs.ts
index f411595aca995..5b9c5393e81d9 100644
--- a/x-pack/test/api_integration/apis/ml/jobs/close_jobs.ts
+++ b/x-pack/test/api_integration/apis/ml/jobs/close_jobs.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { USER } from '../../../../functional/services/ml/security_common';
import { JOB_STATE, DATAFEED_STATE } from '../../../../../plugins/ml/common/constants/states';
import { MULTI_METRIC_JOB_CONFIG, SINGLE_METRIC_JOB_CONFIG, DATAFEED_CONFIG } from './common_jobs';
diff --git a/x-pack/test/api_integration/apis/ml/jobs/delete_jobs.ts b/x-pack/test/api_integration/apis/ml/jobs/delete_jobs.ts
index 4976b6441c37a..e8f733d2fc6ad 100644
--- a/x-pack/test/api_integration/apis/ml/jobs/delete_jobs.ts
+++ b/x-pack/test/api_integration/apis/ml/jobs/delete_jobs.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { USER } from '../../../../functional/services/ml/security_common';
import { MULTI_METRIC_JOB_CONFIG, SINGLE_METRIC_JOB_CONFIG } from './common_jobs';
diff --git a/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts b/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts
index 0a6e1ed75020a..f7ff5b118f6ae 100644
--- a/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts
+++ b/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { USER } from '../../../../functional/services/ml/security_common';
import { MULTI_METRIC_JOB_CONFIG, SINGLE_METRIC_JOB_CONFIG } from './common_jobs';
diff --git a/x-pack/test/api_integration/apis/ml/modules/get_module.ts b/x-pack/test/api_integration/apis/ml/modules/get_module.ts
index e2a5d3cd425dc..a3d060bb1faca 100644
--- a/x-pack/test/api_integration/apis/ml/modules/get_module.ts
+++ b/x-pack/test/api_integration/apis/ml/modules/get_module.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
const moduleIds = [
'apache_ecs',
diff --git a/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts b/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts
index 6634c4e2ed16c..d50148ec583a0 100644
--- a/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts
+++ b/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts
index 6c3eda197f892..bc7fc691bc60d 100644
--- a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts
+++ b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts
@@ -12,7 +12,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
import { JOB_STATE, DATAFEED_STATE } from '../../../../../plugins/ml/common/constants/states';
import { Job } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/results/get_anomalies_table_data.ts b/x-pack/test/api_integration/apis/ml/results/get_anomalies_table_data.ts
index f769d0d878cb2..d1d7d2e8d78ce 100644
--- a/x-pack/test/api_integration/apis/ml/results/get_anomalies_table_data.ts
+++ b/x-pack/test/api_integration/apis/ml/results/get_anomalies_table_data.ts
@@ -7,7 +7,7 @@ import expect from '@kbn/expect';
import { USER } from '../../../../functional/services/ml/security_common';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { Datafeed, Job } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts b/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts
index a9d863b7526f9..d2a0625cf4e24 100644
--- a/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts
+++ b/x-pack/test/api_integration/apis/ml/results/get_categorizer_stats.ts
@@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
import { Datafeed } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs';
import { AnomalyCategorizerStatsDoc } from '../../../../../plugins/ml/common/types/anomalies';
diff --git a/x-pack/test/api_integration/apis/ml/results/get_stopped_partitions.ts b/x-pack/test/api_integration/apis/ml/results/get_stopped_partitions.ts
index 424bc8c333aab..a46c8861258e9 100644
--- a/x-pack/test/api_integration/apis/ml/results/get_stopped_partitions.ts
+++ b/x-pack/test/api_integration/apis/ml/results/get_stopped_partitions.ts
@@ -8,7 +8,7 @@ import expect from '@kbn/expect';
import { Datafeed, Job } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { USER } from '../../../../functional/services/ml/security_common';
-import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
export default ({ getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
diff --git a/x-pack/test/api_integration/apis/transform/delete_transforms.ts b/x-pack/test/api_integration/apis/transform/delete_transforms.ts
index 136bb85dd5ac2..7f01d2741ad15 100644
--- a/x-pack/test/api_integration/apis/transform/delete_transforms.ts
+++ b/x-pack/test/api_integration/apis/transform/delete_transforms.ts
@@ -6,7 +6,7 @@
import expect from '@kbn/expect';
import { TransformEndpointRequest } from '../../../../plugins/transform/common';
import { FtrProviderContext } from '../../ftr_provider_context';
-import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common';
+import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api';
import { USER } from '../../../functional/services/transform/security_common';
async function asyncForEach(array: any[], callback: Function) {
diff --git a/x-pack/test/api_integration/services/ml.ts b/x-pack/test/api_integration/services/ml.ts
index be3b9732f83bf..a8780473fad67 100644
--- a/x-pack/test/api_integration/services/ml.ts
+++ b/x-pack/test/api_integration/services/ml.ts
@@ -7,16 +7,19 @@
import { FtrProviderContext } from '../../functional/ftr_provider_context';
import { MachineLearningAPIProvider } from '../../functional/services/ml/api';
+import { MachineLearningCommonAPIProvider } from '../../functional/services/ml/common_api';
import { MachineLearningSecurityCommonProvider } from '../../functional/services/ml/security_common';
import { MachineLearningTestResourcesProvider } from '../../functional/services/ml/test_resources';
export function MachineLearningProvider(context: FtrProviderContext) {
const api = MachineLearningAPIProvider(context);
+ const commonAPI = MachineLearningCommonAPIProvider(context);
const securityCommon = MachineLearningSecurityCommonProvider(context);
const testResources = MachineLearningTestResourcesProvider(context);
return {
api,
+ commonAPI,
securityCommon,
testResources,
};
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/advanced_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/advanced_job.ts
index a8836a463e652..18a7a4b26a752 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/advanced_job.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/advanced_job.ts
@@ -33,60 +33,6 @@ interface PickFieldsConfig {
summaryCountField?: string;
}
-// type guards
-// Detector
-const isDetectorWithField = (arg: any): arg is Required> => {
- return arg.hasOwnProperty('field');
-};
-const isDetectorWithByField = (arg: any): arg is Required> => {
- return arg.hasOwnProperty('byField');
-};
-const isDetectorWithOverField = (arg: any): arg is Required> => {
- return arg.hasOwnProperty('overField');
-};
-const isDetectorWithPartitionField = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('partitionField');
-};
-const isDetectorWithExcludeFrequent = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('excludeFrequent');
-};
-const isDetectorWithDescription = (arg: any): arg is Required> => {
- return arg.hasOwnProperty('description');
-};
-
-// DatafeedConfig
-const isDatafeedConfigWithQueryDelay = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('queryDelay');
-};
-const isDatafeedConfigWithFrequency = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('frequency');
-};
-const isDatafeedConfigWithScrollSize = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('scrollSize');
-};
-
-// PickFieldsConfig
-const isPickFieldsConfigWithCategorizationField = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('categorizationField');
-};
-const isPickFieldsConfigWithSummaryCountField = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('summaryCountField');
-};
-
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');
@@ -290,88 +236,80 @@ export default function ({ getService }: FtrProviderContext) {
for (const testData of testDataList) {
describe(`${testData.suiteTitle}`, function () {
- it('job creation loads the job management page', async () => {
+ it('job creation loads the advanced wizard for the source data', async () => {
+ await ml.testExecution.logTestStep('job creation navigates to job management');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
- });
- it('job creation loads the new job source selection page', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation loads the new job source selection page'
+ );
await ml.jobManagement.navigateToNewJobSourceSelection();
- });
- it('job creation loads the job type selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(testData.jobSource);
- });
- it('job creation loads the advanced job wizard page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the advanced job wizard page');
await ml.jobTypeSelection.selectAdvancedJob();
});
- it('job creation displays the configure datafeed step', async () => {
+ it('job creation navigates through the advanced wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job creation displays the configure datafeed step');
await ml.jobWizardCommon.assertConfigureDatafeedSectionExists();
- });
- it('job creation pre-fills the datafeed query editor', async () => {
+ await ml.testExecution.logTestStep('job creation pre-fills the datafeed query editor');
await ml.jobWizardAdvanced.assertDatafeedQueryEditorExists();
await ml.jobWizardAdvanced.assertDatafeedQueryEditorValue(defaultValues.datafeedQuery);
- });
- it('job creation inputs the query delay', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the query delay');
await ml.jobWizardAdvanced.assertQueryDelayInputExists();
await ml.jobWizardAdvanced.assertQueryDelayValue(defaultValues.queryDelay);
- if (isDatafeedConfigWithQueryDelay(testData.datafeedConfig)) {
- await ml.jobWizardAdvanced.setQueryDelay(testData.datafeedConfig.queryDelay);
+ if (testData.datafeedConfig.hasOwnProperty('queryDelay')) {
+ await ml.jobWizardAdvanced.setQueryDelay(testData.datafeedConfig.queryDelay!);
}
- });
- it('job creation inputs the frequency', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the frequency');
await ml.jobWizardAdvanced.assertFrequencyInputExists();
await ml.jobWizardAdvanced.assertFrequencyValue(defaultValues.frequency);
- if (isDatafeedConfigWithFrequency(testData.datafeedConfig)) {
- await ml.jobWizardAdvanced.setFrequency(testData.datafeedConfig.frequency);
+ if (testData.datafeedConfig.hasOwnProperty('frequency')) {
+ await ml.jobWizardAdvanced.setFrequency(testData.datafeedConfig.frequency!);
}
- });
- it('job creation inputs the scroll size', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the scroll size');
await ml.jobWizardAdvanced.assertScrollSizeInputExists();
await ml.jobWizardAdvanced.assertScrollSizeValue(defaultValues.scrollSize);
- if (isDatafeedConfigWithScrollSize(testData.datafeedConfig)) {
- await ml.jobWizardAdvanced.setScrollSize(testData.datafeedConfig.scrollSize);
+ if (testData.datafeedConfig.hasOwnProperty('scrollSize')) {
+ await ml.jobWizardAdvanced.setScrollSize(testData.datafeedConfig.scrollSize!);
}
- });
- it('job creation pre-fills the time field', async () => {
+ await ml.testExecution.logTestStep('job creation pre-fills the time field');
await ml.jobWizardAdvanced.assertTimeFieldInputExists();
await ml.jobWizardAdvanced.assertTimeFieldSelection([testData.expected.wizard.timeField]);
- });
- it('job creation displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job creation selects the categorization field', async () => {
+ await ml.testExecution.logTestStep('job creation selects the categorization field');
await ml.jobWizardAdvanced.assertCategorizationFieldInputExists();
- if (isPickFieldsConfigWithCategorizationField(testData.pickFieldsConfig)) {
+ if (testData.pickFieldsConfig.hasOwnProperty('categorizationField')) {
await ml.jobWizardAdvanced.selectCategorizationField(
- testData.pickFieldsConfig.categorizationField
+ testData.pickFieldsConfig.categorizationField!
);
} else {
await ml.jobWizardAdvanced.assertCategorizationFieldSelection([]);
}
- });
- it('job creation selects the summary count field', async () => {
+ await ml.testExecution.logTestStep('job creation selects the summary count field');
await ml.jobWizardAdvanced.assertSummaryCountFieldInputExists();
- if (isPickFieldsConfigWithSummaryCountField(testData.pickFieldsConfig)) {
+ if (testData.pickFieldsConfig.hasOwnProperty('summaryCountField')) {
await ml.jobWizardAdvanced.selectSummaryCountField(
- testData.pickFieldsConfig.summaryCountField
+ testData.pickFieldsConfig.summaryCountField!
);
} else {
await ml.jobWizardAdvanced.assertSummaryCountFieldSelection([]);
}
- });
- it('job creation adds detectors', async () => {
+ await ml.testExecution.logTestStep('job creation adds detectors');
for (const detector of testData.pickFieldsConfig.detectors) {
await ml.jobWizardAdvanced.openCreateDetectorModal();
await ml.jobWizardAdvanced.assertDetectorFunctionInputExists();
@@ -390,128 +328,118 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobWizardAdvanced.assertDetectorDescriptionValue('');
await ml.jobWizardAdvanced.selectDetectorFunction(detector.function);
- if (isDetectorWithField(detector)) {
- await ml.jobWizardAdvanced.selectDetectorField(detector.field);
+ if (detector.hasOwnProperty('field')) {
+ await ml.jobWizardAdvanced.selectDetectorField(detector.field!);
}
- if (isDetectorWithByField(detector)) {
- await ml.jobWizardAdvanced.selectDetectorByField(detector.byField);
+ if (detector.hasOwnProperty('byField')) {
+ await ml.jobWizardAdvanced.selectDetectorByField(detector.byField!);
}
- if (isDetectorWithOverField(detector)) {
- await ml.jobWizardAdvanced.selectDetectorOverField(detector.overField);
+ if (detector.hasOwnProperty('overField')) {
+ await ml.jobWizardAdvanced.selectDetectorOverField(detector.overField!);
}
- if (isDetectorWithPartitionField(detector)) {
- await ml.jobWizardAdvanced.selectDetectorPartitionField(detector.partitionField);
+ if (detector.hasOwnProperty('partitionField')) {
+ await ml.jobWizardAdvanced.selectDetectorPartitionField(detector.partitionField!);
}
- if (isDetectorWithExcludeFrequent(detector)) {
- await ml.jobWizardAdvanced.selectDetectorExcludeFrequent(detector.excludeFrequent);
+ if (detector.hasOwnProperty('excludeFrequent')) {
+ await ml.jobWizardAdvanced.selectDetectorExcludeFrequent(detector.excludeFrequent!);
}
- if (isDetectorWithDescription(detector)) {
- await ml.jobWizardAdvanced.setDetectorDescription(detector.description);
+ if (detector.hasOwnProperty('description')) {
+ await ml.jobWizardAdvanced.setDetectorDescription(detector.description!);
}
await ml.jobWizardAdvanced.confirmAddDetectorModal();
}
- });
- it('job creation displays detector entries', async () => {
+ await ml.testExecution.logTestStep('job creation displays detector entries');
for (const [index, detector] of testData.pickFieldsConfig.detectors.entries()) {
await ml.jobWizardAdvanced.assertDetectorEntryExists(
index,
detector.identifier,
- isDetectorWithDescription(detector) ? detector.description : undefined
+ detector.hasOwnProperty('description') ? detector.description! : undefined
);
}
- });
- it('job creation inputs the bucket span', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(testData.pickFieldsConfig.bucketSpan);
- });
- it('job creation inputs influencers', async () => {
+ await ml.testExecution.logTestStep('job creation inputs influencers');
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection([]);
for (const influencer of testData.pickFieldsConfig.influencers) {
await ml.jobWizardCommon.addInfluencer(influencer);
}
- });
- it('job creation inputs the model memory limit', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.assertModelMemoryLimitInputExists({
withAdvancedSection: false,
});
await ml.jobWizardCommon.setModelMemoryLimit(testData.pickFieldsConfig.memoryLimit, {
withAdvancedSection: false,
});
- });
- it('job creation displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job creation inputs the job id', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(testData.jobId);
- });
- it('job creation inputs the job description', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(testData.jobDescription);
- });
- it('job creation inputs job groups', async () => {
+ await ml.testExecution.logTestStep('job creation inputs job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
for (const jobGroup of testData.jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroups);
- });
- it('job creation opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job creation adds a new custom url', async () => {
+ await ml.testExecution.logTestStep('job creation adds a new custom url');
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
- });
- it('job creation assigns calendars', async () => {
+ await ml.testExecution.logTestStep('job creation assigns calendars');
await ml.jobWizardCommon.addCalendar(calendarId);
- });
- it('job creation displays the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job creation displays the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists({ withAdvancedSection: false });
- });
- it('job creation enables the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job creation enables the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists({ withAdvancedSection: false });
await ml.jobWizardCommon.activateDedicatedIndexSwitch({ withAdvancedSection: false });
- });
- it('job creation displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job creation displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job creation creates the job and finishes processing', async () => {
+ it('job creation runs the job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation creates the job and finishes processing'
+ );
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardAdvanced.createJob();
await ml.jobManagement.assertStartDatafeedModalExists();
await ml.jobManagement.confirmStartDatafeedModal();
await ml.jobManagement.waitForJobCompletion(testData.jobId);
- });
- it('job creation displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays the created job in the job list'
+ );
await ml.jobTable.refreshJobList();
await ml.jobTable.filterWithSearchString(testData.jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === testData.jobId)).to.have.length(1);
- });
- it('job creation displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(testData.jobId, {
id: testData.jobId,
description: testData.jobDescription,
@@ -530,84 +458,78 @@ export default function ({ getService }: FtrProviderContext) {
...testData.expected.modelSizeStats,
}
);
- });
- it('job creation has detector results', async () => {
+ await ml.testExecution.logTestStep('job creation has detector results');
for (let i = 0; i < testData.pickFieldsConfig.detectors.length; i++) {
await ml.api.assertDetectorResultsExist(testData.jobId, i);
}
});
- it('job cloning clicks the clone action and loads the advanced wizard', async () => {
+ it('job cloning opens the existing job in the advanced wizard', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning clicks the clone action and loads the advanced wizard'
+ );
await ml.jobTable.clickCloneJobAction(testData.jobId);
await ml.jobTypeSelection.assertAdvancedJobWizardOpen();
});
- it('job cloning displays the configure datafeed step', async () => {
+ it('job cloning navigates through the advanced wizard, checks and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the configure datafeed step');
await ml.jobWizardCommon.assertConfigureDatafeedSectionExists();
- });
- it('job cloning pre-fills the datafeed query editor', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the datafeed query editor');
await ml.jobWizardAdvanced.assertDatafeedQueryEditorExists();
await ml.jobWizardAdvanced.assertDatafeedQueryEditorValue(defaultValues.datafeedQuery);
- });
- it('job cloning pre-fills the query delay', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the query delay');
await ml.jobWizardAdvanced.assertQueryDelayInputExists();
- if (isDatafeedConfigWithQueryDelay(testData.datafeedConfig)) {
- await ml.jobWizardAdvanced.assertQueryDelayValue(testData.datafeedConfig.queryDelay);
+ if (testData.datafeedConfig.hasOwnProperty('queryDelay')) {
+ await ml.jobWizardAdvanced.assertQueryDelayValue(testData.datafeedConfig.queryDelay!);
}
- });
- it('job cloning pre-fills the frequency', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the frequency');
await ml.jobWizardAdvanced.assertFrequencyInputExists();
- if (isDatafeedConfigWithFrequency(testData.datafeedConfig)) {
- await ml.jobWizardAdvanced.assertFrequencyValue(testData.datafeedConfig.frequency);
+ if (testData.datafeedConfig.hasOwnProperty('frequency')) {
+ await ml.jobWizardAdvanced.assertFrequencyValue(testData.datafeedConfig.frequency!);
}
- });
- it('job cloning pre-fills the scroll size', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the scroll size');
await ml.jobWizardAdvanced.assertScrollSizeInputExists();
await ml.jobWizardAdvanced.assertScrollSizeValue(
- isDatafeedConfigWithScrollSize(testData.datafeedConfig)
- ? testData.datafeedConfig.scrollSize
+ testData.datafeedConfig.hasOwnProperty('scrollSize')
+ ? testData.datafeedConfig.scrollSize!
: defaultValues.scrollSize
);
- });
- it('job creation pre-fills the time field', async () => {
+ await ml.testExecution.logTestStep('job creation pre-fills the time field');
await ml.jobWizardAdvanced.assertTimeFieldInputExists();
await ml.jobWizardAdvanced.assertTimeFieldSelection([testData.expected.wizard.timeField]);
- });
- it('job cloning displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job cloning pre-fills the categorization field', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the categorization field');
await ml.jobWizardAdvanced.assertCategorizationFieldInputExists();
await ml.jobWizardAdvanced.assertCategorizationFieldSelection(
- isPickFieldsConfigWithCategorizationField(testData.pickFieldsConfig)
- ? [testData.pickFieldsConfig.categorizationField]
+ testData.pickFieldsConfig.hasOwnProperty('categorizationField')
+ ? [testData.pickFieldsConfig.categorizationField!]
: []
);
- });
- it('job cloning pre-fills the summary count field', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the summary count field');
await ml.jobWizardAdvanced.assertSummaryCountFieldInputExists();
await ml.jobWizardAdvanced.assertSummaryCountFieldSelection(
- isPickFieldsConfigWithSummaryCountField(testData.pickFieldsConfig)
- ? [testData.pickFieldsConfig.summaryCountField]
+ testData.pickFieldsConfig.hasOwnProperty('summaryCountField')
+ ? [testData.pickFieldsConfig.summaryCountField!]
: []
);
- });
- it('job cloning pre-fills detectors', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills detectors');
for (const [index, detector] of testData.pickFieldsConfig.detectors.entries()) {
await ml.jobWizardAdvanced.assertDetectorEntryExists(
index,
detector.identifier,
- isDetectorWithDescription(detector) ? detector.description : undefined
+ detector.hasOwnProperty('description') ? detector.description! : undefined
);
await ml.jobWizardAdvanced.clickEditDetector(index);
@@ -621,134 +543,121 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobWizardAdvanced.assertDetectorFunctionSelection([detector.function]);
await ml.jobWizardAdvanced.assertDetectorFieldSelection(
- isDetectorWithField(detector) ? [detector.field] : []
+ detector.hasOwnProperty('field') ? [detector.field!] : []
);
await ml.jobWizardAdvanced.assertDetectorByFieldSelection(
- isDetectorWithByField(detector) ? [detector.byField] : []
+ detector.hasOwnProperty('byField') ? [detector.byField!] : []
);
await ml.jobWizardAdvanced.assertDetectorOverFieldSelection(
- isDetectorWithOverField(detector) ? [detector.overField] : []
+ detector.hasOwnProperty('overField') ? [detector.overField!] : []
);
await ml.jobWizardAdvanced.assertDetectorPartitionFieldSelection(
- isDetectorWithPartitionField(detector) ? [detector.partitionField] : []
+ detector.hasOwnProperty('partitionField') ? [detector.partitionField!] : []
);
await ml.jobWizardAdvanced.assertDetectorExcludeFrequentSelection(
- isDetectorWithExcludeFrequent(detector) ? [detector.excludeFrequent] : []
+ detector.hasOwnProperty('excludeFrequent') ? [detector.excludeFrequent!] : []
);
// Currently, a description different form the identifier is generated for detectors with partition field
await ml.jobWizardAdvanced.assertDetectorDescriptionValue(
- isDetectorWithDescription(detector)
- ? detector.description
+ detector.hasOwnProperty('description')
+ ? detector.description!
: detector.identifier.replace('partition_field_name', 'partitionfield')
);
await ml.jobWizardAdvanced.cancelAddDetectorModal();
}
- });
- it('job cloning pre-fills the bucket span', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.assertBucketSpanValue(testData.pickFieldsConfig.bucketSpan);
- });
- it('job cloning pre-fills influencers', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills influencers');
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection(testData.pickFieldsConfig.influencers);
- });
-
- // MML during clone has changed in #61589
- // TODO: adjust test code to reflect the new behavior
- it.skip('job cloning pre-fills the model memory limit', async () => {
- await ml.jobWizardCommon.assertModelMemoryLimitInputExists({
- withAdvancedSection: false,
- });
- await ml.jobWizardCommon.assertModelMemoryLimitValue(
- testData.pickFieldsConfig.memoryLimit,
- {
- withAdvancedSection: false,
- }
- );
- });
- it('job cloning displays the job details step', async () => {
+ // MML during clone has changed in #61589
+ // TODO: adjust test code to reflect the new behavior
+ // await ml.testExecution.logTestStep('job cloning pre-fills the model memory limit');
+ // await ml.jobWizardCommon.assertModelMemoryLimitInputExists({
+ // withAdvancedSection: false,
+ // });
+ // await ml.jobWizardCommon.assertModelMemoryLimitValue(
+ // testData.pickFieldsConfig.memoryLimit,
+ // {
+ // withAdvancedSection: false,
+ // }
+ // );
+
+ await ml.testExecution.logTestStep('job cloning displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job cloning does not pre-fill the job id', async () => {
+ await ml.testExecution.logTestStep('job cloning does not pre-fill the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.assertJobIdValue('');
- });
- it('job cloning inputs the clone job id', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job id');
await ml.jobWizardCommon.setJobId(testData.jobIdClone);
- });
- it('job cloning pre-fills the job description', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.assertJobDescriptionValue(testData.jobDescription);
- });
- it('job cloning pre-fills job groups', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroups);
- });
- it('job cloning inputs the clone job group', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job group');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.addJobGroup('clone');
await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroupsClone);
- });
- it('job cloning opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job cloning persists custom urls', async () => {
+ await ml.testExecution.logTestStep('job cloning persists custom urls');
await ml.customUrls.assertCustomUrlItem(0, 'check-kibana-dashboard');
- });
- it('job cloning persists assigned calendars', async () => {
+ await ml.testExecution.logTestStep('job cloning persists assigned calendars');
await ml.jobWizardCommon.assertCalendarsSelection([calendarId]);
- });
- it('job cloning pre-fills the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists({ withAdvancedSection: false });
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false, {
withAdvancedSection: false,
});
- });
- it('job cloning pre-fills the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists({ withAdvancedSection: false });
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true, {
withAdvancedSection: false,
});
- });
- it('job cloning displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job cloning displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job cloning creates the job and finishes processing', async () => {
+ it('job cloning runs the clone job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardAdvanced.createJob();
await ml.jobManagement.assertStartDatafeedModalExists();
await ml.jobManagement.confirmStartDatafeedModal();
await ml.jobManagement.waitForJobCompletion(testData.jobIdClone);
- });
- it('job cloning displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning displays the created job in the job list'
+ );
await ml.jobTable.refreshJobList();
await ml.jobTable.filterWithSearchString(testData.jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === testData.jobIdClone)).to.have.length(1);
- });
- it('job creation displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(testData.jobIdClone, {
id: testData.jobIdClone,
description: testData.jobDescription,
@@ -767,9 +676,8 @@ export default function ({ getService }: FtrProviderContext) {
...testData.expected.modelSizeStats,
}
);
- });
- it('job creation has detector results', async () => {
+ await ml.testExecution.logTestStep('job cloning has detector results');
for (let i = 0; i < testData.pickFieldsConfig.detectors.length; i++) {
await ml.api.assertDetectorResultsExist(testData.jobIdClone, i);
}
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts b/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts
index 202910622fb64..9e48c71ab0eba 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/annotations.ts
@@ -56,7 +56,8 @@ export default function ({ getService }: FtrProviderContext) {
await ml.api.cleanMlIndices();
});
- it('loads from job list row link', async () => {
+ it('displays error on broken annotation index and recovers after fix', async () => {
+ await ml.testExecution.logTestStep('loads from job list row link');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -66,43 +67,35 @@ export default function ({ getService }: FtrProviderContext) {
expect(rows.filter((row) => row.id === JOB_CONFIG.job_id)).to.have.length(1);
await ml.jobTable.clickOpenJobInSingleMetricViewerButton(JOB_CONFIG.job_id);
- await ml.common.waitForMlLoadingIndicatorToDisappear();
- });
+ await ml.commonUI.waitForMlLoadingIndicatorToDisappear();
- it('pre-fills the job selection', async () => {
+ await ml.testExecution.logTestStep('pre-fills the job selection');
await ml.jobSelection.assertJobSelection([JOB_CONFIG.job_id]);
- });
- it('pre-fills the detector input', async () => {
+ await ml.testExecution.logTestStep('pre-fills the detector input');
await ml.singleMetricViewer.assertDetectorInputExsist();
await ml.singleMetricViewer.assertDetectorInputValue('0');
- });
- it('should display the annotations section showing an error', async () => {
+ await ml.testExecution.logTestStep('should display the annotations section showing an error');
await ml.singleMetricViewer.assertAnnotationsExists('error');
- });
- it('should navigate to anomaly explorer', async () => {
+ await ml.testExecution.logTestStep('should navigate to anomaly explorer');
await ml.navigation.navigateToAnomalyExplorerViaSingleMetricViewer();
- });
- it('should display the annotations section showing an error', async () => {
+ await ml.testExecution.logTestStep('should display the annotations section showing an error');
await ml.anomalyExplorer.assertAnnotationsPanelExists('error');
- });
- it('should display the annotations section without an error', async () => {
+ await ml.testExecution.logTestStep('should display the annotations section without an error');
// restores the aliases to point to the original working annotations index
// so we can run tests against successfully loaded annotations sections.
await ml.testResources.restoreAnnotationsIndexState();
await ml.anomalyExplorer.refreshPage();
await ml.anomalyExplorer.assertAnnotationsPanelExists('loaded');
- });
- it('should navigate to single metric viewer', async () => {
+ await ml.testExecution.logTestStep('should navigate to single metric viewer');
await ml.navigation.navigateToSingleMetricViewerViaAnomalyExplorer();
- });
- it('should display the annotations section without an error', async () => {
+ await ml.testExecution.logTestStep('should display the annotations section without an error');
await ml.singleMetricViewer.assertAnnotationsExists('loaded');
});
});
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts b/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts
index cbee36abef78d..cfbebd478fcb8 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts
@@ -79,24 +79,26 @@ export default function ({ getService }: FtrProviderContext) {
await ml.api.cleanMlIndices();
});
- it('loads from job list row link', async () => {
+ it('opens a job from job list link', async () => {
+ await ml.testExecution.logTestStep('navigate to job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
+ await ml.testExecution.logTestStep('open job in anomaly explorer');
await ml.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(testData.jobConfig.job_id);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === testData.jobConfig.job_id)).to.have.length(1);
await ml.jobTable.clickOpenJobInAnomalyExplorerButton(testData.jobConfig.job_id);
- await ml.common.waitForMlLoadingIndicatorToDisappear();
+ await ml.commonUI.waitForMlLoadingIndicatorToDisappear();
});
- it('pre-fills the job selection', async () => {
+ it('displays job results', async () => {
+ await ml.testExecution.logTestStep('pre-fills the job selection');
await ml.jobSelection.assertJobSelection([testData.jobConfig.job_id]);
- });
- it('displays the influencers list', async () => {
+ await ml.testExecution.logTestStep('displays the influencers list');
await ml.anomalyExplorer.assertInfluencerListExists();
for (const influencerBlock of testData.expected.influencers) {
await ml.anomalyExplorer.assertInfluencerFieldExists(influencerBlock.field);
@@ -111,27 +113,26 @@ export default function ({ getService }: FtrProviderContext) {
);
}
}
- });
- it('displays the swimlanes', async () => {
+ await ml.testExecution.logTestStep('displays the swimlanes');
await ml.anomalyExplorer.assertOverallSwimlaneExists();
await ml.anomalyExplorer.assertSwimlaneViewByExists();
- });
- it('should display the annotations panel', async () => {
+ await ml.testExecution.logTestStep('should display the annotations panel');
await ml.anomalyExplorer.assertAnnotationsPanelExists('loaded');
- });
- it('displays the anomalies table', async () => {
+ await ml.testExecution.logTestStep('displays the anomalies table');
await ml.anomaliesTable.assertTableExists();
- });
- it('anomalies table is not empty', async () => {
+ await ml.testExecution.logTestStep('anomalies table is not empty');
await ml.anomaliesTable.assertTableNotEmpty();
});
- // should be the last step because it navigates away from the Anomaly Explorer page
- it('should allow to attach anomaly swimlane embeddable to the dashboard', async () => {
+ it('adds swim lane embeddable to a dashboard', async () => {
+ // should be the last step because it navigates away from the Anomaly Explorer page
+ await ml.testExecution.logTestStep(
+ 'should allow to attach anomaly swimlane embeddable to the dashboard'
+ );
await ml.anomalyExplorer.openAddToDashboardControl();
await ml.anomalyExplorer.addAndEditSwimlaneInDashboard('ML Test');
});
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/categorization_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/categorization_job.ts
index 1581bd54f5c44..c410aff292ffa 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/categorization_job.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/categorization_job.ts
@@ -89,49 +89,46 @@ export default function ({ getService }: FtrProviderContext) {
await ml.api.cleanMlIndices();
});
- it('job creation loads the job management page', async () => {
+ it('job creation loads the categorization wizard for the source data', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job management page');
+ await ml.testExecution.logTestStep('');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
- });
- it('job creation loads the new job source selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the new job source selection page');
await ml.jobManagement.navigateToNewJobSourceSelection();
- });
- it('job creation loads the job type selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_categorization');
- });
- it('job creation loads the categorization job wizard page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the categorization job wizard page');
await ml.jobTypeSelection.selectCategorizationJob();
});
- it('job creation displays the time range step', async () => {
+ it('job creation navigates through the categorization wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job creation sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Apr 5, 2019 @ 11:25:35.770',
'Nov 21, 2019 @ 06:01:13.914'
);
- });
- it('job creation displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job creation displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job creation displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it(`job creation selects ${detectorTypeIdentifier} detector type`, async () => {
+ await ml.testExecution.logTestStep(
+ `job creation selects ${detectorTypeIdentifier} detector type`
+ );
await ml.jobWizardCategorization.assertCategorizationDetectorTypeSelectionExists();
await ml.jobWizardCategorization.selectCategorizationDetectorType(detectorTypeIdentifier);
- });
- it(`job creation selects the categorization field`, async () => {
+ await ml.testExecution.logTestStep(`job creation selects the categorization field`);
await ml.jobWizardCategorization.assertCategorizationFieldInputExists();
await ml.jobWizardCategorization.selectCategorizationField(categorizationFieldIdentifier);
await ml.jobWizardCategorization.assertCategorizationExamplesCallout(
@@ -140,81 +137,67 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobWizardCategorization.assertCategorizationExamplesTable(
categorizationExampleCount
);
- });
- it('job creation inputs the bucket span', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
- });
- it('job creation displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job creation inputs the job id', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);
- });
- it('job creation inputs the job description', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
- });
- it('job creation inputs job groups', async () => {
+ await ml.testExecution.logTestStep('job creation inputs job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
for (const jobGroup of jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
- });
- it('job creation opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job creation adds a new custom url', async () => {
+ await ml.testExecution.logTestStep('job creation adds a new custom url');
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
- });
- it('job creation assigns calendars', async () => {
+ await ml.testExecution.logTestStep('job creation assigns calendars');
await ml.jobWizardCommon.addCalendar(calendarId);
- });
- it('job creation opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job creation displays the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job creation displays the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
await ml.jobWizardCommon.assertModelPlotSwitchEnabled(false);
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false);
- });
- it('job creation enables the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job creation enables the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
- });
- it('job creation inputs the model memory limit', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
- });
- it('job creation displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job creation displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job creation creates the job and finishes processing', async () => {
+ it('job creation runs the job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job creation creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job creation displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep('job creation displays the created job in the job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -222,9 +205,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobId)).to.have.length(1);
- });
- it('job creation displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId, jobGroups));
await ml.jobTable.assertJobRowDetailsCounts(
@@ -232,123 +216,106 @@ export default function ({ getService }: FtrProviderContext) {
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
- });
- it('job creation has detector results', async () => {
+ await ml.testExecution.logTestStep('job creation has detector results');
await ml.api.assertDetectorResultsExist(jobId, 0);
});
- it('job cloning clicks the clone action and loads the single metric wizard', async () => {
+ it('job cloning opens the existing job in the categorization wizard', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning clicks the clone action and loads the single metric wizard'
+ );
await ml.jobTable.clickCloneJobAction(jobId);
await ml.jobTypeSelection.assertCategorizationJobWizardOpen();
});
- it('job cloning displays the time range step', async () => {
+ it('job cloning navigates through the categorization wizard, checks and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job cloning sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Apr 5, 2019 @ 11:25:35.770',
'Nov 21, 2019 @ 06:01:13.914'
);
- });
- it('job cloning displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job cloning displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job cloning pre-fills field and aggregation', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills field and aggregation');
await ml.jobWizardCategorization.assertCategorizationDetectorTypeSelectionExists();
- });
- it('job cloning pre-fills the bucket span', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
- });
- it('job cloning displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job cloning does not pre-fill the job id', async () => {
+ await ml.testExecution.logTestStep('job cloning does not pre-fill the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.assertJobIdValue('');
- });
- it('job cloning inputs the clone job id', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job id');
await ml.jobWizardCommon.setJobId(jobIdClone);
- });
- it('job cloning pre-fills the job description', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
- });
- it('job cloning pre-fills job groups', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
- });
- it('job cloning inputs the clone job group', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job group');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.addJobGroup('clone');
await ml.jobWizardCommon.assertJobGroupSelection(jobGroupsClone);
- });
- it('job cloning opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job cloning persists custom urls', async () => {
+ await ml.testExecution.logTestStep('job cloning persists custom urls');
await ml.customUrls.assertCustomUrlItem(0, 'check-kibana-dashboard');
- });
- it('job cloning persists assigned calendars', async () => {
+ await ml.testExecution.logTestStep('job cloning persists assigned calendars');
await ml.jobWizardCommon.assertCalendarsSelection([calendarId]);
- });
- it('job cloning opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job cloning pre-fills the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
await ml.jobWizardCommon.assertModelPlotSwitchEnabled(false);
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false);
- });
- it('job cloning pre-fills the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true);
- });
- // MML during clone has changed in #61589
- // TODO: adjust test code to reflect the new behavior
- it.skip('job cloning pre-fills the model memory limit', async () => {
- await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
- await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
- });
+ // MML during clone has changed in #61589
+ // TODO: adjust test code to reflect the new behavior
+ // await ml.testExecution.logTestStep('job cloning pre-fills the model memory limit');
+ // await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
+ // await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
- it('job cloning displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job cloning displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job cloning creates the job and finishes processing', async () => {
+ it('job cloning runs the clone job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job cloning displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the created job in the job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -356,9 +323,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobIdClone)).to.have.length(1);
- });
- it('job cloning displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(jobIdClone, getExpectedRow(jobIdClone, jobGroupsClone));
await ml.jobTable.assertJobRowDetailsCounts(
@@ -366,32 +334,32 @@ export default function ({ getService }: FtrProviderContext) {
getExpectedCounts(jobIdClone),
getExpectedModelSizeStats(jobIdClone)
);
- });
- it('job cloning has detector results', async () => {
+ await ml.testExecution.logTestStep('job cloning has detector results');
await ml.api.assertDetectorResultsExist(jobId, 0);
});
- it('job deletion has results for the job before deletion', async () => {
+ it('deletes the cloned job', async () => {
+ await ml.testExecution.logTestStep('job deletion has results for the job before deletion');
await ml.api.assertJobResultsExist(jobIdClone);
- });
- it('job deletion triggers the delete action', async () => {
+ await ml.testExecution.logTestStep('job deletion triggers the delete action');
await ml.jobTable.clickDeleteJobAction(jobIdClone);
- });
- it('job deletion confirms the delete modal', async () => {
+ await ml.testExecution.logTestStep('job deletion confirms the delete modal');
await ml.jobTable.confirmDeleteJobModal();
- });
- it('job deletion does not display the deleted job in the job list any more', async () => {
+ await ml.testExecution.logTestStep(
+ 'job deletion does not display the deleted job in the job list any more'
+ );
await ml.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobIdClone)).to.have.length(0);
- });
- it('job deletion does not have results for the deleted job any more', async () => {
+ await ml.testExecution.logTestStep(
+ 'job deletion does not have results for the deleted job any more'
+ );
await ml.api.assertNoJobResultsExist(jobIdClone);
});
});
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/date_nanos_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/date_nanos_job.ts
index 50622604c4e5c..22b4c4a1fdfe3 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/date_nanos_job.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/date_nanos_job.ts
@@ -32,55 +32,6 @@ interface PickFieldsConfig {
summaryCountField?: string;
}
-// type guards
-// Detector
-const isDetectorWithField = (arg: any): arg is Required> => {
- return arg.hasOwnProperty('field');
-};
-const isDetectorWithByField = (arg: any): arg is Required> => {
- return arg.hasOwnProperty('byField');
-};
-const isDetectorWithOverField = (arg: any): arg is Required> => {
- return arg.hasOwnProperty('overField');
-};
-const isDetectorWithPartitionField = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('partitionField');
-};
-const isDetectorWithExcludeFrequent = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('excludeFrequent');
-};
-const isDetectorWithDescription = (arg: any): arg is Required> => {
- return arg.hasOwnProperty('description');
-};
-
-// DatafeedConfig
-const isDatafeedConfigWithQueryDelay = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('queryDelay');
-};
-const isDatafeedConfigWithFrequency = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('frequency');
-};
-const isDatafeedConfigWithScrollSize = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('scrollSize');
-};
-
-// PickFieldsConfig
-const isPickFieldsConfigWithSummaryCountField = (
- arg: any
-): arg is Required> => {
- return arg.hasOwnProperty('summaryCountField');
-};
-
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');
@@ -183,77 +134,70 @@ export default function ({ getService }: FtrProviderContext) {
for (const testData of testDataList) {
describe(`${testData.suiteTitle}`, function () {
- it('job creation loads the job management page', async () => {
+ it('loads the advanced wizard for the source data', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job management page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
- });
- it('job creation loads the new job source selection page', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation loads the new job source selection page'
+ );
await ml.jobManagement.navigateToNewJobSourceSelection();
- });
- it('job creation loads the job type selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(testData.jobSource);
- });
- it('job creation loads the advanced job wizard page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the advanced job wizard page');
await ml.jobTypeSelection.selectAdvancedJob();
});
- it('job creation displays the configure datafeed step', async () => {
+ it('navigates through the advanced wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job creation displays the configure datafeed step');
await ml.jobWizardCommon.assertConfigureDatafeedSectionExists();
- });
- it('job creation pre-fills the datafeed query editor', async () => {
+ await ml.testExecution.logTestStep('job creation pre-fills the datafeed query editor');
await ml.jobWizardAdvanced.assertDatafeedQueryEditorExists();
await ml.jobWizardAdvanced.assertDatafeedQueryEditorValue(defaultValues.datafeedQuery);
- });
- it('job creation inputs the query delay', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the query delay');
await ml.jobWizardAdvanced.assertQueryDelayInputExists();
await ml.jobWizardAdvanced.assertQueryDelayValue(defaultValues.queryDelay);
- if (isDatafeedConfigWithQueryDelay(testData.datafeedConfig)) {
- await ml.jobWizardAdvanced.setQueryDelay(testData.datafeedConfig.queryDelay);
+ if (testData.datafeedConfig.hasOwnProperty('queryDelay')) {
+ await ml.jobWizardAdvanced.setQueryDelay(testData.datafeedConfig.queryDelay!);
}
- });
- it('job creation inputs the frequency', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the frequency');
await ml.jobWizardAdvanced.assertFrequencyInputExists();
await ml.jobWizardAdvanced.assertFrequencyValue(defaultValues.frequency);
- if (isDatafeedConfigWithFrequency(testData.datafeedConfig)) {
- await ml.jobWizardAdvanced.setFrequency(testData.datafeedConfig.frequency);
+ if (testData.datafeedConfig.hasOwnProperty('frequency')) {
+ await ml.jobWizardAdvanced.setFrequency(testData.datafeedConfig.frequency!);
}
- });
- it('job creation inputs the scroll size', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the scroll size');
await ml.jobWizardAdvanced.assertScrollSizeInputExists();
await ml.jobWizardAdvanced.assertScrollSizeValue(defaultValues.scrollSize);
- if (isDatafeedConfigWithScrollSize(testData.datafeedConfig)) {
- await ml.jobWizardAdvanced.setScrollSize(testData.datafeedConfig.scrollSize);
+ if (testData.datafeedConfig.hasOwnProperty('scrollSize')) {
+ await ml.jobWizardAdvanced.setScrollSize(testData.datafeedConfig.scrollSize!);
}
- });
- it('job creation pre-fills the time field', async () => {
+ await ml.testExecution.logTestStep('ob creation pre-fills the time field');
await ml.jobWizardAdvanced.assertTimeFieldInputExists();
await ml.jobWizardAdvanced.assertTimeFieldSelection([testData.expected.wizard.timeField]);
- });
- it('job creation displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job creation selects the summary count field', async () => {
+ await ml.testExecution.logTestStep('job creation selects the summary count field');
await ml.jobWizardAdvanced.assertSummaryCountFieldInputExists();
- if (isPickFieldsConfigWithSummaryCountField(testData.pickFieldsConfig)) {
+ if (testData.pickFieldsConfig.hasOwnProperty('summaryCountField')) {
await ml.jobWizardAdvanced.selectSummaryCountField(
- testData.pickFieldsConfig.summaryCountField
+ testData.pickFieldsConfig.summaryCountField!
);
} else {
await ml.jobWizardAdvanced.assertSummaryCountFieldSelection([]);
}
- });
- it('job creation adds detectors', async () => {
+ await ml.testExecution.logTestStep('job creation adds detectors');
for (const detector of testData.pickFieldsConfig.detectors) {
await ml.jobWizardAdvanced.openCreateDetectorModal();
await ml.jobWizardAdvanced.assertDetectorFunctionInputExists();
@@ -272,120 +216,112 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobWizardAdvanced.assertDetectorDescriptionValue('');
await ml.jobWizardAdvanced.selectDetectorFunction(detector.function);
- if (isDetectorWithField(detector)) {
- await ml.jobWizardAdvanced.selectDetectorField(detector.field);
+ if (detector.hasOwnProperty('field')) {
+ await ml.jobWizardAdvanced.selectDetectorField(detector.field!);
}
- if (isDetectorWithByField(detector)) {
- await ml.jobWizardAdvanced.selectDetectorByField(detector.byField);
+ if (detector.hasOwnProperty('byField')) {
+ await ml.jobWizardAdvanced.selectDetectorByField(detector.byField!);
}
- if (isDetectorWithOverField(detector)) {
- await ml.jobWizardAdvanced.selectDetectorOverField(detector.overField);
+ if (detector.hasOwnProperty('overField')) {
+ await ml.jobWizardAdvanced.selectDetectorOverField(detector.overField!);
}
- if (isDetectorWithPartitionField(detector)) {
- await ml.jobWizardAdvanced.selectDetectorPartitionField(detector.partitionField);
+ if (detector.hasOwnProperty('partitionField')) {
+ await ml.jobWizardAdvanced.selectDetectorPartitionField(detector.partitionField!);
}
- if (isDetectorWithExcludeFrequent(detector)) {
- await ml.jobWizardAdvanced.selectDetectorExcludeFrequent(detector.excludeFrequent);
+ if (detector.hasOwnProperty('excludeFrequent')) {
+ await ml.jobWizardAdvanced.selectDetectorExcludeFrequent(detector.excludeFrequent!);
}
- if (isDetectorWithDescription(detector)) {
- await ml.jobWizardAdvanced.setDetectorDescription(detector.description);
+ if (detector.hasOwnProperty('description')) {
+ await ml.jobWizardAdvanced.setDetectorDescription(detector.description!);
}
await ml.jobWizardAdvanced.confirmAddDetectorModal();
}
- });
- it('job creation displays detector entries', async () => {
+ await ml.testExecution.logTestStep('job creation displays detector entries');
for (const [index, detector] of testData.pickFieldsConfig.detectors.entries()) {
await ml.jobWizardAdvanced.assertDetectorEntryExists(
index,
detector.identifier,
- isDetectorWithDescription(detector) ? detector.description : undefined
+ detector.hasOwnProperty('description') ? detector.description! : undefined
);
}
- });
- it('job creation inputs the bucket span', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(testData.pickFieldsConfig.bucketSpan);
- });
- it('job creation inputs influencers', async () => {
+ await ml.testExecution.logTestStep('job creation inputs influencers');
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection([]);
for (const influencer of testData.pickFieldsConfig.influencers) {
await ml.jobWizardCommon.addInfluencer(influencer);
}
- });
- it('job creation inputs the model memory limit', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.assertModelMemoryLimitInputExists({
withAdvancedSection: false,
});
await ml.jobWizardCommon.setModelMemoryLimit(testData.pickFieldsConfig.memoryLimit, {
withAdvancedSection: false,
});
- });
- it('job creation displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job creation inputs the job id', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(testData.jobId);
- });
- it('job creation inputs the job description', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(testData.jobDescription);
- });
- it('job creation inputs job groups', async () => {
+ await ml.testExecution.logTestStep('job creation inputs job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
for (const jobGroup of testData.jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroups);
- });
- it('job creation opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job creation displays the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job creation displays the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists({ withAdvancedSection: false });
- });
- it('job creation enables the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job creation enables the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists({ withAdvancedSection: false });
await ml.jobWizardCommon.activateDedicatedIndexSwitch({ withAdvancedSection: false });
- });
- it('job creation displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job creation displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job creation creates the job and finishes processing', async () => {
+ it('runs the job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation creates the job and finishes processing'
+ );
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardAdvanced.createJob();
await ml.jobManagement.assertStartDatafeedModalExists();
await ml.jobManagement.confirmStartDatafeedModal();
await ml.jobManagement.waitForJobCompletion(testData.jobId);
- });
- it('job creation displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays the created job in the job list'
+ );
await ml.jobTable.refreshJobList();
await ml.jobTable.filterWithSearchString(testData.jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === testData.jobId)).to.have.length(1);
- });
- it('job creation displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(testData.jobId, {
id: testData.jobId,
description: testData.jobDescription,
@@ -404,9 +340,8 @@ export default function ({ getService }: FtrProviderContext) {
...testData.expected.modelSizeStats,
}
);
- });
- it('job creation has detector results', async () => {
+ await ml.testExecution.logTestStep('job creation has detector results');
for (let i = 0; i < testData.pickFieldsConfig.detectors.length; i++) {
await ml.api.assertDetectorResultsExist(testData.jobId, i);
}
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/multi_metric_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/multi_metric_job.ts
index 85477b105abe9..8702cfd734454 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/multi_metric_job.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/multi_metric_job.ts
@@ -86,52 +86,50 @@ export default function ({ getService }: FtrProviderContext) {
await ml.api.cleanMlIndices();
});
- it('job creation loads the job management page', async () => {
+ it('job creation loads the multi metric wizard for the source data', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job management page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
- });
- it('job creation loads the new job source selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the new job source selection page');
await ml.jobManagement.navigateToNewJobSourceSelection();
- });
- it('job creation loads the job type selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_farequote');
- });
- it('job creation loads the multi metric job wizard page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the multi metric job wizard page');
await ml.jobTypeSelection.selectMultiMetricJob();
});
- it('job creation displays the time range step', async () => {
+ it('job creation navigates through the multi metric wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job creation sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
- });
- it('job creation displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job creation displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job creation displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job creation selects detectors and displays detector previews', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation selects detectors and displays detector previews'
+ );
for (const [index, aggAndFieldIdentifier] of aggAndFieldIdentifiers.entries()) {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, false);
await ml.jobWizardCommon.assertDetectorPreviewExists(aggAndFieldIdentifier, index, 'LINE');
}
- });
- it('job creation inputs the split field and displays split cards', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation inputs the split field and displays split cards'
+ );
await ml.jobWizardMultiMetric.assertSplitFieldInputExists();
await ml.jobWizardMultiMetric.selectSplitField(splitField);
@@ -140,84 +138,69 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobWizardMultiMetric.assertDetectorSplitNumberOfBackCards(9);
await ml.jobWizardCommon.assertInfluencerSelection([splitField]);
- });
- it('job creation displays the influencer field', async () => {
+ await ml.testExecution.logTestStep('job creation displays the influencer field');
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection([splitField]);
- });
- it('job creation inputs the bucket span', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
- });
- it('job creation displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job creation inputs the job id', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);
- });
- it('job creation inputs the job description', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
- });
- it('job creation inputs job groups', async () => {
+ await ml.testExecution.logTestStep('job creation inputs job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
for (const jobGroup of jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
- });
- it('job creation opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job creation adds a new custom url', async () => {
+ await ml.testExecution.logTestStep('job creation adds a new custom url');
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
- });
- it('job creation assigns calendars', async () => {
+ await ml.testExecution.logTestStep('job creation assigns calendars');
await ml.jobWizardCommon.addCalendar(calendarId);
- });
- it('job creation opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job creation displays the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job creation displays the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
- });
- it('job creation enables the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job creation enables the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
- });
- it('job creation inputs the model memory limit', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
- });
- it('job creation displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job creation displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job creation creates the job and finishes processing', async () => {
+ it('job creation runs the job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job creation creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job creation displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep('job creation displays the created job in the job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -225,9 +208,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobId)).to.have.length(1);
- });
- it('job creation displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId, jobGroups));
await ml.jobTable.assertJobRowDetailsCounts(
@@ -235,40 +219,41 @@ export default function ({ getService }: FtrProviderContext) {
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
- });
- it('job creation has detector results', async () => {
+ await ml.testExecution.logTestStep('job creation has detector results');
for (let i = 0; i < aggAndFieldIdentifiers.length; i++) {
await ml.api.assertDetectorResultsExist(jobId, i);
}
});
- it('job cloning clicks the clone action and loads the multi metric wizard', async () => {
+ it('job cloning opens the existing job in the multi metric wizard', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning clicks the clone action and loads the multi metric wizard'
+ );
await ml.jobTable.clickCloneJobAction(jobId);
await ml.jobTypeSelection.assertMultiMetricJobWizardOpen();
});
- it('job cloning displays the time range step', async () => {
+ it('job cloning navigates through the multi metric wizard, checks and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job cloning sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
- });
- it('job cloning displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job cloning displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job cloning pre-fills detectors and shows preview with split cards', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning pre-fills detectors and shows preview with split cards'
+ );
for (const [index, aggAndFieldIdentifier] of aggAndFieldIdentifiers.entries()) {
await ml.jobWizardCommon.assertDetectorPreviewExists(aggAndFieldIdentifier, index, 'LINE');
}
@@ -276,99 +261,81 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobWizardMultiMetric.assertDetectorSplitExists(splitField);
await ml.jobWizardMultiMetric.assertDetectorSplitFrontCardTitle('AAL');
await ml.jobWizardMultiMetric.assertDetectorSplitNumberOfBackCards(9);
- });
- it('job cloning pre-fills the split field', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the split field');
await ml.jobWizardMultiMetric.assertSplitFieldInputExists();
await ml.jobWizardMultiMetric.assertSplitFieldSelection([splitField]);
- });
- it('job cloning pre-fills influencers', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills influencers');
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection([splitField]);
- });
- it('job cloning pre-fills the bucket span', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
- });
- it('job cloning displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job cloning does not pre-fill the job id', async () => {
+ await ml.testExecution.logTestStep('job cloning does not pre-fill the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.assertJobIdValue('');
- });
- it('job cloning inputs the clone job id', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job id');
await ml.jobWizardCommon.setJobId(jobIdClone);
- });
- it('job cloning pre-fills the job description', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
- });
- it('job cloning pre-fills job groups', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
- });
- it('job cloning inputs the clone job group', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job group');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.addJobGroup('clone');
await ml.jobWizardCommon.assertJobGroupSelection(jobGroupsClone);
- });
- it('job cloning opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job cloning persists custom urls', async () => {
+ await ml.testExecution.logTestStep('job cloning persists custom urls');
await ml.customUrls.assertCustomUrlItem(0, 'check-kibana-dashboard');
- });
- it('job cloning persists assigned calendars', async () => {
+ await ml.testExecution.logTestStep('job cloning persists assigned calendars');
await ml.jobWizardCommon.assertCalendarsSelection([calendarId]);
- });
- it('job cloning opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job cloning pre-fills the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false);
- });
- it('job cloning pre-fills the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true);
- });
- // MML during clone has changed in #61589
- // TODO: adjust test code to reflect the new behavior
- it.skip('job cloning pre-fills the model memory limit', async () => {
- await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
- await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
- });
+ // MML during clone has changed in #61589
+ // TODO: adjust test code to reflect the new behavior
+ // await ml.testExecution.logTestStep('job cloning pre-fills the model memory limit');
+ // await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
+ // await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
- it('job cloning displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job cloning displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job cloning creates the job and finishes processing', async () => {
+ it('job cloning runs the clone job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job cloning displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the created job in the job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -376,9 +343,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobIdClone)).to.have.length(1);
- });
- it('job cloning displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(jobIdClone, getExpectedRow(jobIdClone, jobGroupsClone));
await ml.jobTable.assertJobRowDetailsCounts(
@@ -386,9 +354,8 @@ export default function ({ getService }: FtrProviderContext) {
getExpectedCounts(jobIdClone),
getExpectedModelSizeStats(jobIdClone)
);
- });
- it('job cloning has detector results', async () => {
+ await ml.testExecution.logTestStep('job cloning has detector results');
for (let i = 0; i < aggAndFieldIdentifiers.length; i++) {
await ml.api.assertDetectorResultsExist(jobId, i);
}
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/population_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/population_job.ts
index c6de7f8a2bd39..3ec78eccf3de4 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/population_job.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/population_job.ts
@@ -100,57 +100,54 @@ export default function ({ getService }: FtrProviderContext) {
await ml.api.cleanMlIndices();
});
- it('job creation loads the job management page', async () => {
+ it('job creation loads the population wizard for the source data', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job management page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
- });
- it('job creation loads the new job source selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the new job source selection page');
await ml.jobManagement.navigateToNewJobSourceSelection();
- });
- it('job creation loads the job type selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_ecommerce');
- });
- it('job creation loads the population job wizard page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the population job wizard page');
await ml.jobTypeSelection.selectPopulationJob();
});
- it('job creation displays the time range step', async () => {
+ it('job creation navigates through the population wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job creation sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Jun 12, 2019 @ 00:04:19.000',
'Jul 12, 2019 @ 23:45:36.000'
);
- });
- it('job creation displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job creation displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job creation displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job creation selects the population field', async () => {
+ await ml.testExecution.logTestStep('job creation selects the population field');
await ml.jobWizardPopulation.assertPopulationFieldInputExists();
await ml.jobWizardPopulation.selectPopulationField(populationField);
- });
- it('job creation selects detectors and displays detector previews', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation selects detectors and displays detector previews'
+ );
for (const [index, detector] of detectors.entries()) {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(detector.identifier, false);
await ml.jobWizardCommon.assertDetectorPreviewExists(detector.identifier, index, 'SCATTER');
}
- });
- it('job creation inputs detector split fields and displays split cards', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation inputs detector split fields and displays split cards'
+ );
for (const [index, detector] of detectors.entries()) {
await ml.jobWizardPopulation.assertDetectorSplitFieldInputExists(index);
await ml.jobWizardPopulation.selectDetectorSplitField(index, detector.splitField);
@@ -165,86 +162,71 @@ export default function ({ getService }: FtrProviderContext) {
detector.numberOfBackCards
);
}
- });
- it('job creation displays the influencer field', async () => {
+ await ml.testExecution.logTestStep('job creation displays the influencer field');
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection(
[populationField].concat(detectors.map((detector) => detector.splitField))
);
- });
- it('job creation inputs the bucket span', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
- });
- it('job creation displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job creation inputs the job id', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);
- });
- it('job creation inputs the job description', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
- });
- it('job creation inputs job groups', async () => {
+ await ml.testExecution.logTestStep('job creation inputs job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
for (const jobGroup of jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
- });
- it('job creation opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job creation adds a new custom url', async () => {
+ await ml.testExecution.logTestStep('job creation adds a new custom url');
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
- });
- it('job creation assigns calendars', async () => {
+ await ml.testExecution.logTestStep('job creation assigns calendars');
await ml.jobWizardCommon.addCalendar(calendarId);
- });
- it('job creation opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job creation displays the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job creation displays the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
- });
- it('job creation enables the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job creation enables the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
- });
- it('job creation inputs the model memory limit', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
- });
- it('job creation displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job creation displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job creation creates the job and finishes processing', async () => {
+ it('job creation runs the job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job creation creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job creation displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep('job creation displays the created job in the job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -252,9 +234,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobId)).to.have.length(1);
- });
- it('job creation displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId, jobGroups));
await ml.jobTable.assertJobRowDetailsCounts(
@@ -262,45 +245,45 @@ export default function ({ getService }: FtrProviderContext) {
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
- });
- it('job creation has detector results', async () => {
+ await ml.testExecution.logTestStep('job creation has detector results');
for (let i = 0; i < detectors.length; i++) {
await ml.api.assertDetectorResultsExist(jobId, i);
}
});
- it('job cloning clicks the clone action and loads the population wizard', async () => {
+ it('job cloning opens the existing job in the population wizard', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning clicks the clone action and loads the population wizard'
+ );
await ml.jobTable.clickCloneJobAction(jobId);
await ml.jobTypeSelection.assertPopulationJobWizardOpen();
});
- it('job cloning displays the time range step', async () => {
+ it('job cloning navigates through the population wizard, checks and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job cloning sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Jun 12, 2019 @ 00:04:19.000',
'Jul 12, 2019 @ 23:45:36.000'
);
- });
- it('job cloning displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job cloning displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job cloning pre-fills the population field', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the population field');
await ml.jobWizardPopulation.assertPopulationFieldInputExists();
await ml.jobWizardPopulation.assertPopulationFieldSelection([populationField]);
- });
- it('job cloning pre-fills detectors and shows preview with split cards', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning pre-fills detectors and shows preview with split cards'
+ );
for (const [index, detector] of detectors.entries()) {
await ml.jobWizardCommon.assertDetectorPreviewExists(detector.identifier, index, 'SCATTER');
@@ -317,96 +300,79 @@ export default function ({ getService }: FtrProviderContext) {
detector.numberOfBackCards
);
}
- });
- it('job cloning pre-fills influencers', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills influencers');
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection(
[populationField].concat(detectors.map((detector) => detector.splitField))
);
- });
- it('job cloning pre-fills the bucket span', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
- });
- it('job cloning displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job cloning does not pre-fill the job id', async () => {
+ await ml.testExecution.logTestStep('job cloning does not pre-fill the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.assertJobIdValue('');
- });
- it('job cloning inputs the clone job id', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job id');
await ml.jobWizardCommon.setJobId(jobIdClone);
- });
- it('job cloning pre-fills the job description', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
- });
- it('job cloning pre-fills job groups', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
- });
- it('job cloning inputs the clone job group', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job group');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.addJobGroup('clone');
await ml.jobWizardCommon.assertJobGroupSelection(jobGroupsClone);
- });
- it('job cloning opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job cloning persists custom urls', async () => {
+ await ml.testExecution.logTestStep('job cloning persists custom urls');
await ml.customUrls.assertCustomUrlItem(0, 'check-kibana-dashboard');
- });
- it('job cloning persists assigned calendars', async () => {
+ await ml.testExecution.logTestStep('job cloning persists assigned calendars');
await ml.jobWizardCommon.assertCalendarsSelection([calendarId]);
- });
- it('job cloning opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job cloning pre-fills the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(false);
- });
- it('job cloning pre-fills the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true);
- });
- // MML during clone has changed in #61589
- // TODO: adjust test code to reflect the new behavior
- it.skip('job cloning pre-fills the model memory limit', async () => {
- await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
- await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
- });
+ // MML during clone has changed in #61589
+ // TODO: adjust test code to reflect the new behavior
+ // await ml.testExecution.logTestStep('job cloning pre-fills the model memory limit');
+ // await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
+ // await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
- it('job cloning displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job cloning displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job cloning creates the job and finishes processing', async () => {
+ it('job cloning runs the clone job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job cloning displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the created job in the job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -414,9 +380,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobIdClone)).to.have.length(1);
- });
- it('job cloning displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(jobIdClone, getExpectedRow(jobIdClone, jobGroupsClone));
await ml.jobTable.assertJobRowDetailsCounts(
@@ -424,9 +391,8 @@ export default function ({ getService }: FtrProviderContext) {
getExpectedCounts(jobIdClone),
getExpectedModelSizeStats(jobIdClone)
);
- });
- it('job cloning has detector results', async () => {
+ await ml.testExecution.logTestStep('job cloning has detector results');
for (let i = 0; i < detectors.length; i++) {
await ml.api.assertDetectorResultsExist(jobId, i);
}
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/saved_search_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/saved_search_job.ts
index 6f40ec5427b74..170b88efd70f5 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/saved_search_job.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/saved_search_job.ts
@@ -286,44 +286,43 @@ export default function ({ getService }: FtrProviderContext) {
for (const testData of testDataList) {
describe(` ${testData.suiteTitle}`, function () {
- it('job creation loads the job management page', async () => {
+ it('job creation loads the multi metric wizard for the source data', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job management page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
- });
- it('job creation loads the new job source selection page', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation loads the new job source selection page'
+ );
await ml.jobManagement.navigateToNewJobSourceSelection();
- });
- it('job creation loads the job type selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob(testData.jobSource);
- });
- it('job creation loads the multi metric job wizard page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the multi metric job wizard page');
await ml.jobTypeSelection.selectMultiMetricJob();
});
- it('job creation displays the time range step', async () => {
+ it('job creation navigates through the multi metric wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job creation sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
- });
- it('job creation displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job creation displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job creation displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job creation selects detectors and displays detector previews', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation selects detectors and displays detector previews'
+ );
for (const [index, aggAndFieldIdentifier] of testData.aggAndFieldIdentifiers.entries()) {
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, false);
@@ -333,9 +332,10 @@ export default function ({ getService }: FtrProviderContext) {
'LINE'
);
}
- });
- it('job creation inputs the split field and displays split cards', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation inputs the split field and displays split cards'
+ );
await ml.jobWizardMultiMetric.assertSplitFieldInputExists();
await ml.jobWizardMultiMetric.selectSplitField(testData.splitField);
@@ -348,72 +348,64 @@ export default function ({ getService }: FtrProviderContext) {
);
await ml.jobWizardCommon.assertInfluencerSelection([testData.splitField]);
- });
- it('job creation displays the influencer field', async () => {
+ await ml.testExecution.logTestStep('job creation displays the influencer field');
await ml.jobWizardCommon.assertInfluencerInputExists();
await ml.jobWizardCommon.assertInfluencerSelection([testData.splitField]);
- });
- it('job creation inputs the bucket span', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(testData.bucketSpan);
- });
- it('job creation displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job creation inputs the job id', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(testData.jobId);
- });
- it('job creation inputs the job description', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(testData.jobDescription);
- });
- it('job creation inputs job groups', async () => {
+ await ml.testExecution.logTestStep('job creation inputs job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
for (const jobGroup of testData.jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.assertJobGroupSelection(testData.jobGroups);
- });
- it('job creation opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job creation displays the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job creation displays the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
- });
- it('job creation enables the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job creation enables the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
- });
- it('job creation inputs the model memory limit', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.setModelMemoryLimit(testData.memoryLimit);
- });
- it('job creation displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job creation displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job creation creates the job and finishes processing', async () => {
+ it('job creation runs the job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation creates the job and finishes processing'
+ );
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job creation displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays the created job in the job list'
+ );
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -421,9 +413,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(testData.jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === testData.jobId)).to.have.length(1);
- });
- it('job creation displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(testData.jobId, {
id: testData.jobId,
description: testData.jobDescription,
@@ -442,9 +435,8 @@ export default function ({ getService }: FtrProviderContext) {
...testData.expected.modelSizeStats,
}
);
- });
- it('has detector results', async () => {
+ await ml.testExecution.logTestStep('has detector results');
for (let i = 0; i < testData.aggAndFieldIdentifiers.length; i++) {
await ml.api.assertDetectorResultsExist(testData.jobId, i);
}
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job.ts
index 58f3960153bc6..ba5628661bfc2 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_job.ts
@@ -85,120 +85,101 @@ export default function ({ getService }: FtrProviderContext) {
await ml.api.cleanMlIndices();
});
- it('job creation loads the job management page', async () => {
+ it('job creation loads the single metric wizard for the source data', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job management page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
- });
- it('job creation loads the new job source selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the new job source selection page');
await ml.jobManagement.navigateToNewJobSourceSelection();
- });
- it('job creation loads the job type selection page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_farequote');
- });
- it('job creation loads the single metric job wizard page', async () => {
+ await ml.testExecution.logTestStep('job creation loads the single metric job wizard page');
await ml.jobTypeSelection.selectSingleMetricJob();
});
- it('job creation displays the time range step', async () => {
+ it('job creation navigates through the single metric wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job creation sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
- });
- it('job creation displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job creation displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job creation displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job creation selects field and aggregation', async () => {
+ await ml.testExecution.logTestStep('job creation selects field and aggregation');
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, true);
await ml.jobWizardCommon.assertAnomalyChartExists('LINE');
- });
- it('job creation inputs the bucket span', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);
- });
- it('job creation displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job creation inputs the job id', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);
- });
- it('job creation inputs the job description', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.setJobDescription(jobDescription);
- });
- it('job creation inputs job groups', async () => {
+ await ml.testExecution.logTestStep('job creation inputs job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
for (const jobGroup of jobGroups) {
await ml.jobWizardCommon.addJobGroup(jobGroup);
}
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
- });
- it('job creation opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job creation adds a new custom url', async () => {
+ await ml.testExecution.logTestStep('job creation adds a new custom url');
await ml.jobWizardCommon.addCustomUrl({ label: 'check-kibana-dashboard' });
- });
- it('job creation assigns calendars', async () => {
+ await ml.testExecution.logTestStep('job creation assigns calendars');
await ml.jobWizardCommon.addCalendar(calendarId);
- });
- it('job creation opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job creation opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job creation displays the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job creation displays the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
- });
- it('job creation enables the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job creation enables the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.activateDedicatedIndexSwitch();
- });
- it('job creation inputs the model memory limit', async () => {
+ await ml.testExecution.logTestStep('job creation inputs the model memory limit');
await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
await ml.jobWizardCommon.setModelMemoryLimit(memoryLimit);
- });
- it('job creation displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job creation displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job creation creates the job and finishes processing', async () => {
+ it('job creation runs the job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job creation creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job creation displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep('job creation displays the created job in the job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -206,9 +187,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(jobId);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobId)).to.have.length(1);
- });
- it('job creation displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job creation displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId, jobGroups));
await ml.jobTable.assertJobRowDetailsCounts(
@@ -216,124 +198,107 @@ export default function ({ getService }: FtrProviderContext) {
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
- });
- it('job creation has detector results', async () => {
+ await ml.testExecution.logTestStep('job creation has detector results');
await ml.api.assertDetectorResultsExist(jobId, 0);
});
- it('job cloning clicks the clone action and loads the single metric wizard', async () => {
+ it('job cloning opens the existing job in the single metric wizard', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning clicks the clone action and loads the single metric wizard'
+ );
await ml.jobTable.clickCloneJobAction(jobId);
await ml.jobTypeSelection.assertSingleMetricJobWizardOpen();
});
- it('job cloning displays the time range step', async () => {
+ it('job cloning navigates through the single metric wizard, checks and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();
- });
- it('job cloning sets the timerange', async () => {
+ await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);
- });
- it('job cloning displays the event rate chart', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();
- });
- it('job cloning displays the pick fields step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();
- });
- it('job cloning pre-fills field and aggregation', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills field and aggregation');
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.assertAggAndFieldSelection([aggAndFieldIdentifier]);
await ml.jobWizardCommon.assertAnomalyChartExists('LINE');
- });
- it('job cloning pre-fills the bucket span', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.assertBucketSpanValue(bucketSpan);
- });
- it('job cloning displays the job details step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();
- });
- it('job cloning does not pre-fill the job id', async () => {
+ await ml.testExecution.logTestStep('job cloning does not pre-fill the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.assertJobIdValue('');
- });
- it('job cloning inputs the clone job id', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job id');
await ml.jobWizardCommon.setJobId(jobIdClone);
- });
- it('job cloning pre-fills the job description', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the job description');
await ml.jobWizardCommon.assertJobDescriptionInputExists();
await ml.jobWizardCommon.assertJobDescriptionValue(jobDescription);
- });
- it('job cloning pre-fills job groups', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills job groups');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.assertJobGroupSelection(jobGroups);
- });
- it('job cloning inputs the clone job group', async () => {
+ await ml.testExecution.logTestStep('job cloning inputs the clone job group');
await ml.jobWizardCommon.assertJobGroupInputExists();
await ml.jobWizardCommon.addJobGroup('clone');
await ml.jobWizardCommon.assertJobGroupSelection(jobGroupsClone);
- });
- it('job cloning opens the additional settings section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the additional settings section');
await ml.jobWizardCommon.ensureAdditionalSettingsSectionOpen();
- });
- it('job cloning persists custom urls', async () => {
+ await ml.testExecution.logTestStep('job cloning persists custom urls');
await ml.customUrls.assertCustomUrlItem(0, 'check-kibana-dashboard');
- });
- it('job cloning persists assigned calendars', async () => {
+ await ml.testExecution.logTestStep('job cloning persists assigned calendars');
await ml.jobWizardCommon.assertCalendarsSelection([calendarId]);
- });
- it('job cloning opens the advanced section', async () => {
+ await ml.testExecution.logTestStep('job cloning opens the advanced section');
await ml.jobWizardCommon.ensureAdvancedSectionOpen();
- });
- it('job cloning pre-fills the model plot switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the model plot switch');
await ml.jobWizardCommon.assertModelPlotSwitchExists();
await ml.jobWizardCommon.assertModelPlotSwitchCheckedState(true);
- });
- it('job cloning pre-fills the dedicated index switch', async () => {
+ await ml.testExecution.logTestStep('job cloning pre-fills the dedicated index switch');
await ml.jobWizardCommon.assertDedicatedIndexSwitchExists();
await ml.jobWizardCommon.assertDedicatedIndexSwitchCheckedState(true);
- });
- // MML during clone has changed in #61589
- // TODO: adjust test code to reflect the new behavior
- it.skip('job cloning pre-fills the model memory limit', async () => {
- await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
- await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
- });
+ // MML during clone has changed in #61589
+ // TODO: adjust test code to reflect the new behavior
+ // await ml.testExecution.logTestStep('job cloning pre-fills the model memory limit');
+ // await ml.jobWizardCommon.assertModelMemoryLimitInputExists();
+ // await ml.jobWizardCommon.assertModelMemoryLimitValue(memoryLimit);
- it('job cloning displays the validation step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();
- });
- it('job cloning displays the summary step', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});
- it('job cloning creates the job and finishes processing', async () => {
+ it('job cloning runs the clone job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning creates the job and finishes processing');
await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobAndWaitForCompletion();
- });
- it('job cloning displays the created job in the job list', async () => {
+ await ml.testExecution.logTestStep('job cloning displays the created job in the job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
@@ -341,9 +306,10 @@ export default function ({ getService }: FtrProviderContext) {
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobIdClone)).to.have.length(1);
- });
- it('job cloning displays details for the created job in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'job cloning displays details for the created job in the job list'
+ );
await ml.jobTable.assertJobRowFields(jobIdClone, getExpectedRow(jobIdClone, jobGroupsClone));
await ml.jobTable.assertJobRowDetailsCounts(
@@ -351,32 +317,32 @@ export default function ({ getService }: FtrProviderContext) {
getExpectedCounts(jobIdClone),
getExpectedModelSizeStats(jobIdClone)
);
- });
- it('job cloning has detector results', async () => {
+ await ml.testExecution.logTestStep('job cloning has detector results');
await ml.api.assertDetectorResultsExist(jobId, 0);
});
- it('job deletion has results for the job before deletion', async () => {
+ it('deletes the cloned job', async () => {
+ await ml.testExecution.logTestStep('job deletion has results for the job before deletion');
await ml.api.assertJobResultsExist(jobIdClone);
- });
- it('job deletion triggers the delete action', async () => {
+ await ml.testExecution.logTestStep('job deletion triggers the delete action');
await ml.jobTable.clickDeleteJobAction(jobIdClone);
- });
- it('job deletion confirms the delete modal', async () => {
+ await ml.testExecution.logTestStep('job deletion confirms the delete modal');
await ml.jobTable.confirmDeleteJobModal();
- });
- it('job deletion does not display the deleted job in the job list any more', async () => {
+ await ml.testExecution.logTestStep(
+ 'job deletion does not display the deleted job in the job list any more'
+ );
await ml.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobIdClone);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === jobIdClone)).to.have.length(0);
- });
- it('job deletion does not have results for the deleted job any more', async () => {
+ await ml.testExecution.logTestStep(
+ 'job deletion does not have results for the deleted job any more'
+ );
await ml.api.assertNoJobResultsExist(jobIdClone);
});
});
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_viewer.ts b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_viewer.ts
index 3855bd0c884cd..e1ab3f8e092c3 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_viewer.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/single_metric_viewer.ts
@@ -53,41 +53,39 @@ export default function ({ getService }: FtrProviderContext) {
await ml.api.cleanMlIndices();
});
- it('loads from job list row link', async () => {
+ it('opens a job from job list link', async () => {
+ await ml.testExecution.logTestStep('navigate to job list');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();
+ await ml.testExecution.logTestStep('open job in single metric viewer');
await ml.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(JOB_CONFIG.job_id);
const rows = await ml.jobTable.parseJobTable();
expect(rows.filter((row) => row.id === JOB_CONFIG.job_id)).to.have.length(1);
await ml.jobTable.clickOpenJobInSingleMetricViewerButton(JOB_CONFIG.job_id);
- await ml.common.waitForMlLoadingIndicatorToDisappear();
+ await ml.commonUI.waitForMlLoadingIndicatorToDisappear();
});
- it('pre-fills the job selection', async () => {
+ it('displays job results', async () => {
+ await ml.testExecution.logTestStep('pre-fills the job selection');
await ml.jobSelection.assertJobSelection([JOB_CONFIG.job_id]);
- });
- it('pre-fills the detector input', async () => {
+ await ml.testExecution.logTestStep('pre-fills the detector input');
await ml.singleMetricViewer.assertDetectorInputExsist();
await ml.singleMetricViewer.assertDetectorInputValue('0');
- });
- it('displays the chart', async () => {
+ await ml.testExecution.logTestStep('displays the chart');
await ml.singleMetricViewer.assertChartExsist();
- });
- it('should display the annotations section', async () => {
+ await ml.testExecution.logTestStep('should display the annotations section');
await ml.singleMetricViewer.assertAnnotationsExists('loaded');
- });
- it('displays the anomalies table', async () => {
+ await ml.testExecution.logTestStep('displays the anomalies table');
await ml.anomaliesTable.assertTableExists();
- });
- it('anomalies table is not empty', async () => {
+ await ml.testExecution.logTestStep('anomalies table is not empty');
await ml.anomaliesTable.assertTableNotEmpty();
});
});
diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts
index a62bfdcde0572..6beefaafa3792 100644
--- a/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts
+++ b/x-pack/test/functional/apps/ml/data_frame_analytics/classification_creation.ts
@@ -56,108 +56,96 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex);
});
- it('loads the data frame analytics page', async () => {
+ it('loads the data frame analytics wizard', async () => {
+ await ml.testExecution.logTestStep('loads the data frame analytics page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToDataFrameAnalytics();
- });
- it('loads the source selection modal', async () => {
+ await ml.testExecution.logTestStep('loads the source selection modal');
await ml.dataFrameAnalytics.startAnalyticsCreation();
- });
- it('selects the source data and loads the job wizard page', async () => {
+ await ml.testExecution.logTestStep(
+ 'selects the source data and loads the job wizard page'
+ );
await ml.jobSourceSelection.selectSourceForAnalyticsJob(testData.source);
await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive();
});
- it('selects the job type', async () => {
+ it('navigates through the wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('selects the job type');
await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists();
await ml.dataFrameAnalyticsCreation.selectJobType(testData.jobType);
- });
- it('inputs the dependent variable', async () => {
+ await ml.testExecution.logTestStep('inputs the dependent variable');
await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists();
await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable);
- });
- it('inputs the training percent', async () => {
+ await ml.testExecution.logTestStep('inputs the training percent');
await ml.dataFrameAnalyticsCreation.assertTrainingPercentInputExists();
await ml.dataFrameAnalyticsCreation.setTrainingPercent(testData.trainingPercent);
- });
- it('displays the source data preview', async () => {
+ await ml.testExecution.logTestStep('displays the source data preview');
await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists();
- });
- it('displays the include fields selection', async () => {
+ await ml.testExecution.logTestStep('displays the include fields selection');
await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists();
- });
- it('continues to the additional options step', async () => {
+ await ml.testExecution.logTestStep('continues to the additional options step');
await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep();
- });
- it('accepts the suggested model memory limit', async () => {
+ await ml.testExecution.logTestStep('accepts the suggested model memory limit');
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists();
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated();
- });
- it('continues to the details step', async () => {
+ await ml.testExecution.logTestStep('continues to the details step');
await ml.dataFrameAnalyticsCreation.continueToDetailsStep();
- });
- it('inputs the job id', async () => {
+ await ml.testExecution.logTestStep('inputs the job id');
await ml.dataFrameAnalyticsCreation.assertJobIdInputExists();
await ml.dataFrameAnalyticsCreation.setJobId(testData.jobId);
- });
- it('inputs the job description', async () => {
+ await ml.testExecution.logTestStep('inputs the job description');
await ml.dataFrameAnalyticsCreation.assertJobDescriptionInputExists();
await ml.dataFrameAnalyticsCreation.setJobDescription(testData.jobDescription);
- });
- it('should default the set destination index to job id switch to true', async () => {
+ await ml.testExecution.logTestStep(
+ 'should default the set destination index to job id switch to true'
+ );
await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdSwitchExists();
await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdCheckState(true);
- });
- it('should input the destination index', async () => {
+ await ml.testExecution.logTestStep('should input the destination index');
await ml.dataFrameAnalyticsCreation.setDestIndexSameAsIdCheckState(false);
await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists();
await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex);
- });
- it('sets the create index pattern switch', async () => {
+ await ml.testExecution.logTestStep('sets the create index pattern switch');
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
testData.createIndexPattern
);
- });
- it('continues to the create step', async () => {
+ await ml.testExecution.logTestStep('continues to the create step');
await ml.dataFrameAnalyticsCreation.continueToCreateStep();
});
- it('creates and starts the analytics job', async () => {
+ it('runs the analytics job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('creates and starts the analytics job');
await ml.dataFrameAnalyticsCreation.assertCreateButtonExists();
await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true);
await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId);
- });
- it('finishes analytics processing', async () => {
+ await ml.testExecution.logTestStep('finishes analytics processing');
await ml.dataFrameAnalytics.waitForAnalyticsCompletion(testData.jobId);
- });
- it('displays the analytics table', async () => {
+ await ml.testExecution.logTestStep('displays the analytics table');
await ml.dataFrameAnalyticsCreation.navigateToJobManagementPage();
await ml.dataFrameAnalytics.assertAnalyticsTableExists();
- });
- it('displays the stats bar', async () => {
+ await ml.testExecution.logTestStep('displays the stats bar');
await ml.dataFrameAnalytics.assertAnalyticsStatsBarExists();
- });
- it('displays the created job in the analytics table', async () => {
+ await ml.testExecution.logTestStep('displays the created job in the analytics table');
await ml.dataFrameAnalyticsTable.refreshAnalyticsTable();
await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.jobId);
const rows = await ml.dataFrameAnalyticsTable.parseAnalyticsTable();
@@ -166,9 +154,10 @@ export default function ({ getService }: FtrProviderContext) {
1,
`Filtered analytics table should have 1 row for job id '${testData.jobId}' (got matching items '${filteredRows}')`
);
- });
- it('displays details for the created job in the analytics table', async () => {
+ await ml.testExecution.logTestStep(
+ 'displays details for the created job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, {
id: testData.jobId,
description: testData.jobDescription,
@@ -180,25 +169,28 @@ export default function ({ getService }: FtrProviderContext) {
});
});
- it('should open the edit form for the created job in the analytics table', async () => {
+ it('edits the analytics job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'should open the edit form for the created job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.openEditFlyout(testData.jobId);
- });
- it('should input the description in the edit form', async () => {
+ await ml.testExecution.logTestStep('should input the description in the edit form');
await ml.dataFrameAnalyticsEdit.assertJobDescriptionEditInputExists();
await ml.dataFrameAnalyticsEdit.setJobDescriptionEdit(editedDescription);
- });
- it('should input the model memory limit in the edit form', async () => {
+ await ml.testExecution.logTestStep(
+ 'should input the model memory limit in the edit form'
+ );
await ml.dataFrameAnalyticsEdit.assertJobMmlEditInputExists();
await ml.dataFrameAnalyticsEdit.setJobMmlEdit('21mb');
- });
- it('should submit the edit job form', async () => {
+ await ml.testExecution.logTestStep('should submit the edit job form');
await ml.dataFrameAnalyticsEdit.updateAnalyticsJob();
- });
- it('displays details for the edited job in the analytics table', async () => {
+ await ml.testExecution.logTestStep(
+ 'displays details for the edited job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, {
id: testData.jobId,
description: editedDescription,
@@ -208,14 +200,14 @@ export default function ({ getService }: FtrProviderContext) {
status: testData.expected.row.status,
progress: testData.expected.row.progress,
});
- });
- it('creates the destination index and writes results to it', async () => {
+ await ml.testExecution.logTestStep(
+ 'creates the destination index and writes results to it'
+ );
await ml.api.assertIndicesExist(testData.destinationIndex);
await ml.api.assertIndicesNotEmpty(testData.destinationIndex);
- });
- it('displays the results view for created job', async () => {
+ await ml.testExecution.logTestStep('displays the results view for created job');
await ml.dataFrameAnalyticsTable.openResultsView();
await ml.dataFrameAnalytics.assertClassificationEvaluatePanelElementsExists();
await ml.dataFrameAnalytics.assertClassificationTablePanelExists();
diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts
index e8f0a69b397cd..5494f2f963d37 100644
--- a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts
+++ b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts
@@ -159,57 +159,62 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testResources.deleteIndexPatternByTitle(cloneDestIndex);
});
- it('should open the wizard with a proper header', async () => {
+ it('opens the existing job in the data frame analytics job wizard', async () => {
+ await ml.testExecution.logTestStep('should open the wizard with a proper header');
const headerText = await ml.dataFrameAnalyticsCreation.getHeaderText();
expect(headerText).to.match(/Clone job/);
await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive();
});
- it('should have correct init form values for config step', async () => {
+ it('navigates through the wizard, checks and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep(
+ 'should have correct init form values for config step'
+ );
await ml.dataFrameAnalyticsCreation.assertInitialCloneJobConfigStep(
testData.job as DataFrameAnalyticsConfig
);
- });
- it('should continue to the additional options step', async () => {
+ await ml.testExecution.logTestStep('should continue to the additional options step');
await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep();
- });
- it('should have correct init form values for additional options step', async () => {
+ await ml.testExecution.logTestStep(
+ 'should have correct init form values for additional options step'
+ );
await ml.dataFrameAnalyticsCreation.assertInitialCloneJobAdditionalOptionsStep(
testData.job.analysis as DataFrameAnalyticsConfig['analysis']
);
- });
- it('should continue to the details step', async () => {
+ await ml.testExecution.logTestStep('should continue to the details step');
await ml.dataFrameAnalyticsCreation.continueToDetailsStep();
- });
- it('should have correct init form values for details step', async () => {
+ await ml.testExecution.logTestStep(
+ 'should have correct init form values for details step'
+ );
await ml.dataFrameAnalyticsCreation.assertInitialCloneJobDetailsStep(
testData.job as DataFrameAnalyticsConfig
);
await ml.dataFrameAnalyticsCreation.setJobId(cloneJobId);
await ml.dataFrameAnalyticsCreation.setDestIndex(cloneDestIndex);
- });
- it('should continue to the create step', async () => {
+ await ml.testExecution.logTestStep('should continue to the create step');
await ml.dataFrameAnalyticsCreation.continueToCreateStep();
});
- it('should have enabled Create button on a valid form input', async () => {
+ it('runs the clone analytics job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'should have enabled Create button on a valid form input'
+ );
expect(await ml.dataFrameAnalyticsCreation.isCreateButtonDisabled()).to.be(false);
- });
- it('should create a clone job', async () => {
+ await ml.testExecution.logTestStep('should create a clone job');
await ml.dataFrameAnalyticsCreation.createAnalyticsJob(cloneJobId);
- });
- it('should finish analytics processing', async () => {
+ await ml.testExecution.logTestStep('should finish analytics processing');
await ml.dataFrameAnalytics.waitForAnalyticsCompletion(cloneJobId);
- });
- it('should display the created job in the analytics table', async () => {
+ await ml.testExecution.logTestStep(
+ 'should display the created job in the analytics table'
+ );
await ml.dataFrameAnalyticsCreation.navigateToJobManagementPage();
await ml.dataFrameAnalyticsTable.refreshAnalyticsTable();
await ml.dataFrameAnalyticsTable.filterWithSearchString(cloneJobId);
diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts
index 5b89cec49db3e..e4bc7b940aaea 100644
--- a/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts
+++ b/x-pack/test/functional/apps/ml/data_frame_analytics/outlier_detection_creation.ts
@@ -66,116 +66,102 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex);
});
- it('loads the data frame analytics page', async () => {
+ it('loads the data frame analytics wizard', async () => {
+ await ml.testExecution.logTestStep('loads the data frame analytics page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToDataFrameAnalytics();
- });
- it('loads the source selection modal', async () => {
+ await ml.testExecution.logTestStep('loads the source selection modal');
await ml.dataFrameAnalytics.startAnalyticsCreation();
- });
- it('selects the source data and loads the job wizard page', async () => {
+ await ml.testExecution.logTestStep(
+ 'selects the source data and loads the job wizard page'
+ );
await ml.jobSourceSelection.selectSourceForAnalyticsJob(testData.source);
await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive();
});
- it('selects the job type', async () => {
+ it('navigates through the wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('selects the job type');
await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists();
await ml.dataFrameAnalyticsCreation.selectJobType(testData.jobType);
- });
- it('does not display the dependent variable input', async () => {
+ await ml.testExecution.logTestStep('does not display the dependent variable input');
await ml.dataFrameAnalyticsCreation.assertDependentVariableInputMissing();
- });
- it('does not display the training percent input', async () => {
+ await ml.testExecution.logTestStep('does not display the training percent input');
await ml.dataFrameAnalyticsCreation.assertTrainingPercentInputMissing();
- });
- it('displays the source data preview', async () => {
+ await ml.testExecution.logTestStep('displays the source data preview');
await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists();
- });
- it('enables the source data preview histogram charts', async () => {
+ await ml.testExecution.logTestStep('enables the source data preview histogram charts');
await ml.dataFrameAnalyticsCreation.enableSourceDataPreviewHistogramCharts();
- });
- it('displays the source data preview histogram charts', async () => {
+ await ml.testExecution.logTestStep('displays the source data preview histogram charts');
await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewHistogramCharts(
testData.expected.histogramCharts
);
- });
- it('displays the include fields selection', async () => {
+ await ml.testExecution.logTestStep('displays the include fields selection');
await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists();
- });
- it('continues to the additional options step', async () => {
+ await ml.testExecution.logTestStep('continues to the additional options step');
await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep();
- });
- it('accepts the suggested model memory limit', async () => {
+ await ml.testExecution.logTestStep('accepts the suggested model memory limit');
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists();
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated();
- });
- it('continues to the details step', async () => {
+ await ml.testExecution.logTestStep('continues to the details step');
await ml.dataFrameAnalyticsCreation.continueToDetailsStep();
- });
- it('inputs the job id', async () => {
+ await ml.testExecution.logTestStep('inputs the job id');
await ml.dataFrameAnalyticsCreation.assertJobIdInputExists();
await ml.dataFrameAnalyticsCreation.setJobId(testData.jobId);
- });
- it('inputs the job description', async () => {
+ await ml.testExecution.logTestStep('inputs the job description');
await ml.dataFrameAnalyticsCreation.assertJobDescriptionInputExists();
await ml.dataFrameAnalyticsCreation.setJobDescription(testData.jobDescription);
- });
- it('should default the set destination index to job id switch to true', async () => {
+ await ml.testExecution.logTestStep(
+ 'should default the set destination index to job id switch to true'
+ );
await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdSwitchExists();
await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdCheckState(true);
- });
- it('should input the destination index', async () => {
+ await ml.testExecution.logTestStep('should input the destination index');
await ml.dataFrameAnalyticsCreation.setDestIndexSameAsIdCheckState(false);
await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists();
await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex);
- });
- it('sets the create index pattern switch', async () => {
+ await ml.testExecution.logTestStep('sets the create index pattern switch');
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
testData.createIndexPattern
);
- });
- it('continues to the create step', async () => {
+ await ml.testExecution.logTestStep('continues to the create step');
await ml.dataFrameAnalyticsCreation.continueToCreateStep();
});
- it('creates and starts the analytics job', async () => {
+ it('runs the analytics job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('creates and starts the analytics job');
await ml.dataFrameAnalyticsCreation.assertCreateButtonExists();
await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true);
await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId);
- });
- it('finishes analytics processing', async () => {
+ await ml.testExecution.logTestStep('finishes analytics processing');
await ml.dataFrameAnalytics.waitForAnalyticsCompletion(testData.jobId);
- });
- it('displays the analytics table', async () => {
+ await ml.testExecution.logTestStep('displays the analytics table');
await ml.dataFrameAnalyticsCreation.navigateToJobManagementPage();
await ml.dataFrameAnalytics.assertAnalyticsTableExists();
- });
- it('displays the stats bar', async () => {
+ await ml.testExecution.logTestStep('displays the stats bar');
await ml.dataFrameAnalytics.assertAnalyticsStatsBarExists();
- });
- it('displays the created job in the analytics table', async () => {
+ await ml.testExecution.logTestStep('displays the created job in the analytics table');
await ml.dataFrameAnalyticsTable.refreshAnalyticsTable();
await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.jobId);
const rows = await ml.dataFrameAnalyticsTable.parseAnalyticsTable();
@@ -184,9 +170,10 @@ export default function ({ getService }: FtrProviderContext) {
1,
`Filtered analytics table should have 1 row for job id '${testData.jobId}' (got matching items '${filteredRows}')`
);
- });
- it('displays details for the created job in the analytics table', async () => {
+ await ml.testExecution.logTestStep(
+ 'displays details for the created job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, {
id: testData.jobId,
description: testData.jobDescription,
@@ -198,25 +185,28 @@ export default function ({ getService }: FtrProviderContext) {
});
});
- it('should open the edit form for the created job in the analytics table', async () => {
+ it('edits the analytics job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'should open the edit form for the created job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.openEditFlyout(testData.jobId);
- });
- it('should input the description in the edit form', async () => {
+ await ml.testExecution.logTestStep('should input the description in the edit form');
await ml.dataFrameAnalyticsEdit.assertJobDescriptionEditInputExists();
await ml.dataFrameAnalyticsEdit.setJobDescriptionEdit(editedDescription);
- });
- it('should input the model memory limit in the edit form', async () => {
+ await ml.testExecution.logTestStep(
+ 'should input the model memory limit in the edit form'
+ );
await ml.dataFrameAnalyticsEdit.assertJobMmlEditInputExists();
await ml.dataFrameAnalyticsEdit.setJobMmlEdit('21mb');
- });
- it('should submit the edit job form', async () => {
+ await ml.testExecution.logTestStep('should submit the edit job form');
await ml.dataFrameAnalyticsEdit.updateAnalyticsJob();
- });
- it('displays details for the edited job in the analytics table', async () => {
+ await ml.testExecution.logTestStep(
+ 'displays details for the edited job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, {
id: testData.jobId,
description: editedDescription,
@@ -226,14 +216,14 @@ export default function ({ getService }: FtrProviderContext) {
status: testData.expected.row.status,
progress: testData.expected.row.progress,
});
- });
- it('creates the destination index and writes results to it', async () => {
+ await ml.testExecution.logTestStep(
+ 'creates the destination index and writes results to it'
+ );
await ml.api.assertIndicesExist(testData.destinationIndex);
await ml.api.assertIndicesNotEmpty(testData.destinationIndex);
- });
- it('displays the results view for created job', async () => {
+ await ml.testExecution.logTestStep('displays the results view for created job');
await ml.dataFrameAnalyticsTable.openResultsView();
await ml.dataFrameAnalytics.assertOutlierTablePanelExists();
});
diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts
index a67a348323347..af9c5417e4826 100644
--- a/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts
+++ b/x-pack/test/functional/apps/ml/data_frame_analytics/regression_creation.ts
@@ -56,108 +56,96 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testResources.deleteIndexPatternByTitle(testData.destinationIndex);
});
- it('loads the data frame analytics page', async () => {
+ it('loads the data frame analytics wizard', async () => {
+ await ml.testExecution.logTestStep('loads the data frame analytics page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToDataFrameAnalytics();
- });
- it('loads the source selection modal', async () => {
+ await ml.testExecution.logTestStep('loads the source selection modal');
await ml.dataFrameAnalytics.startAnalyticsCreation();
- });
- it('selects the source data and loads the job wizard page', async () => {
+ await ml.testExecution.logTestStep(
+ 'selects the source data and loads the job wizard page'
+ );
await ml.jobSourceSelection.selectSourceForAnalyticsJob(testData.source);
await ml.dataFrameAnalyticsCreation.assertConfigurationStepActive();
});
- it('selects the job type', async () => {
+ it('navigates through the wizard and sets all needed fields', async () => {
+ await ml.testExecution.logTestStep('selects the job type');
await ml.dataFrameAnalyticsCreation.assertJobTypeSelectExists();
await ml.dataFrameAnalyticsCreation.selectJobType(testData.jobType);
- });
- it('inputs the dependent variable', async () => {
+ await ml.testExecution.logTestStep('inputs the dependent variable');
await ml.dataFrameAnalyticsCreation.assertDependentVariableInputExists();
await ml.dataFrameAnalyticsCreation.selectDependentVariable(testData.dependentVariable);
- });
- it('inputs the training percent', async () => {
+ await ml.testExecution.logTestStep('inputs the training percent');
await ml.dataFrameAnalyticsCreation.assertTrainingPercentInputExists();
await ml.dataFrameAnalyticsCreation.setTrainingPercent(testData.trainingPercent);
- });
- it('displays the source data preview', async () => {
+ await ml.testExecution.logTestStep('displays the source data preview');
await ml.dataFrameAnalyticsCreation.assertSourceDataPreviewExists();
- });
- it('displays the include fields selection', async () => {
+ await ml.testExecution.logTestStep('displays the include fields selection');
await ml.dataFrameAnalyticsCreation.assertIncludeFieldsSelectionExists();
- });
- it('continues to the additional options step', async () => {
+ await ml.testExecution.logTestStep('continues to the additional options step');
await ml.dataFrameAnalyticsCreation.continueToAdditionalOptionsStep();
- });
- it('accepts the suggested model memory limit', async () => {
+ await ml.testExecution.logTestStep('accepts the suggested model memory limit');
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputExists();
await ml.dataFrameAnalyticsCreation.assertModelMemoryInputPopulated();
- });
- it('continues to the details step', async () => {
+ await ml.testExecution.logTestStep('continues to the details step');
await ml.dataFrameAnalyticsCreation.continueToDetailsStep();
- });
- it('inputs the job id', async () => {
+ await ml.testExecution.logTestStep('inputs the job id');
await ml.dataFrameAnalyticsCreation.assertJobIdInputExists();
await ml.dataFrameAnalyticsCreation.setJobId(testData.jobId);
- });
- it('inputs the job description', async () => {
+ await ml.testExecution.logTestStep('inputs the job description');
await ml.dataFrameAnalyticsCreation.assertJobDescriptionInputExists();
await ml.dataFrameAnalyticsCreation.setJobDescription(testData.jobDescription);
- });
- it('should default the set destination index to job id switch to true', async () => {
+ await ml.testExecution.logTestStep(
+ 'should default the set destination index to job id switch to true'
+ );
await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdSwitchExists();
await ml.dataFrameAnalyticsCreation.assertDestIndexSameAsIdCheckState(true);
- });
- it('should input the destination index', async () => {
+ await ml.testExecution.logTestStep('should input the destination index');
await ml.dataFrameAnalyticsCreation.setDestIndexSameAsIdCheckState(false);
await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists();
await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex);
- });
- it('sets the create index pattern switch', async () => {
+ await ml.testExecution.logTestStep('sets the create index pattern switch');
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
testData.createIndexPattern
);
- });
- it('continues to the create step', async () => {
+ await ml.testExecution.logTestStep('continues to the create step');
await ml.dataFrameAnalyticsCreation.continueToCreateStep();
});
- it('creates and starts the analytics job', async () => {
+ it('runs the analytics job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep('creates and starts the analytics job');
await ml.dataFrameAnalyticsCreation.assertCreateButtonExists();
await ml.dataFrameAnalyticsCreation.assertStartJobCheckboxCheckState(true);
await ml.dataFrameAnalyticsCreation.createAnalyticsJob(testData.jobId);
- });
- it('finishes analytics processing', async () => {
+ await ml.testExecution.logTestStep('finishes analytics processing');
await ml.dataFrameAnalytics.waitForAnalyticsCompletion(testData.jobId);
- });
- it('displays the analytics table', async () => {
+ await ml.testExecution.logTestStep('displays the analytics table');
await ml.dataFrameAnalyticsCreation.navigateToJobManagementPage();
await ml.dataFrameAnalytics.assertAnalyticsTableExists();
- });
- it('displays the stats bar', async () => {
+ await ml.testExecution.logTestStep('displays the stats bar');
await ml.dataFrameAnalytics.assertAnalyticsStatsBarExists();
- });
- it('displays the created job in the analytics table', async () => {
+ await ml.testExecution.logTestStep('displays the created job in the analytics table');
await ml.dataFrameAnalyticsTable.refreshAnalyticsTable();
await ml.dataFrameAnalyticsTable.filterWithSearchString(testData.jobId);
const rows = await ml.dataFrameAnalyticsTable.parseAnalyticsTable();
@@ -166,9 +154,10 @@ export default function ({ getService }: FtrProviderContext) {
1,
`Filtered analytics table should have 1 row for job id '${testData.jobId}' (got matching items '${filteredRows}')`
);
- });
- it('displays details for the created job in the analytics table', async () => {
+ await ml.testExecution.logTestStep(
+ 'displays details for the created job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, {
id: testData.jobId,
description: testData.jobDescription,
@@ -180,25 +169,28 @@ export default function ({ getService }: FtrProviderContext) {
});
});
- it('should open the edit form for the created job in the analytics table', async () => {
+ it('edits the analytics job and displays it correctly in the job list', async () => {
+ await ml.testExecution.logTestStep(
+ 'should open the edit form for the created job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.openEditFlyout(testData.jobId);
- });
- it('should input the description in the edit form', async () => {
+ await ml.testExecution.logTestStep('should input the description in the edit form');
await ml.dataFrameAnalyticsEdit.assertJobDescriptionEditInputExists();
await ml.dataFrameAnalyticsEdit.setJobDescriptionEdit(editedDescription);
- });
- it('should input the model memory limit in the edit form', async () => {
+ await ml.testExecution.logTestStep(
+ 'should input the model memory limit in the edit form'
+ );
await ml.dataFrameAnalyticsEdit.assertJobMmlEditInputExists();
await ml.dataFrameAnalyticsEdit.setJobMmlEdit('21mb');
- });
- it('should submit the edit job form', async () => {
+ await ml.testExecution.logTestStep('should submit the edit job form');
await ml.dataFrameAnalyticsEdit.updateAnalyticsJob();
- });
- it('displays details for the edited job in the analytics table', async () => {
+ await ml.testExecution.logTestStep(
+ 'displays details for the edited job in the analytics table'
+ );
await ml.dataFrameAnalyticsTable.assertAnalyticsRowFields(testData.jobId, {
id: testData.jobId,
description: editedDescription,
@@ -208,14 +200,14 @@ export default function ({ getService }: FtrProviderContext) {
status: testData.expected.row.status,
progress: testData.expected.row.progress,
});
- });
- it('creates the destination index and writes results to it', async () => {
+ await ml.testExecution.logTestStep(
+ 'creates the destination index and writes results to it'
+ );
await ml.api.assertIndicesExist(testData.destinationIndex);
await ml.api.assertIndicesNotEmpty(testData.destinationIndex);
- });
- it('displays the results view for created job', async () => {
+ await ml.testExecution.logTestStep('displays the results view for created job');
await ml.dataFrameAnalyticsTable.openResultsView();
await ml.dataFrameAnalytics.assertRegressionEvaluatePanelElementsExists();
await ml.dataFrameAnalytics.assertRegressionTablePanelExists();
diff --git a/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts
index 3c9111c246630..4b3b0c38f2acf 100644
--- a/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts
+++ b/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts
@@ -47,40 +47,34 @@ export default function ({ getService }: FtrProviderContext) {
await ml.api.deleteIndices(testData.indexName);
});
- it('loads the data visualizer selector page', async () => {
+ it('displays and imports a file', async () => {
+ await ml.testExecution.logTestStep('loads the data visualizer selector page');
await ml.navigation.navigateToDataVisualizer();
- });
- it('loads the file upload page', async () => {
+ await ml.testExecution.logTestStep('loads the file upload page');
await ml.dataVisualizer.navigateToFileUpload();
- });
- it('selects a file and loads visualizer results', async () => {
+ await ml.testExecution.logTestStep('selects a file and loads visualizer results');
await ml.dataVisualizerFileBased.selectFile(testData.filePath);
- });
- it('displays the components of the file details page', async () => {
+ await ml.testExecution.logTestStep('displays the components of the file details page');
await ml.dataVisualizerFileBased.assertFileTitle(testData.expected.results.title);
await ml.dataVisualizerFileBased.assertFileContentPanelExists();
await ml.dataVisualizerFileBased.assertSummaryPanelExists();
await ml.dataVisualizerFileBased.assertFileStatsPanelExists();
- });
- it('loads the import settings page', async () => {
+ await ml.testExecution.logTestStep('loads the import settings page');
await ml.dataVisualizerFileBased.navigateToFileImport();
- });
- it('sets the index name', async () => {
+ await ml.testExecution.logTestStep('sets the index name');
await ml.dataVisualizerFileBased.setIndexName(testData.indexName);
- });
- it('sets the create index pattern checkbox', async () => {
+ await ml.testExecution.logTestStep('sets the create index pattern checkbox');
await ml.dataVisualizerFileBased.setCreateIndexPatternCheckboxState(
testData.createIndexPattern
);
- });
- it('imports the file', async () => {
+ await ml.testExecution.logTestStep('imports the file');
await ml.dataVisualizerFileBased.startImportAndWaitForProcessing();
});
});
@@ -88,15 +82,14 @@ export default function ({ getService }: FtrProviderContext) {
for (const testData of testDataListNegative) {
describe(testData.suiteSuffix, function () {
- it('loads the data visualizer selector page', async () => {
+ it('does not import an invalid file', async () => {
+ await ml.testExecution.logTestStep('loads the data visualizer selector page');
await ml.navigation.navigateToDataVisualizer();
- });
- it('loads the file upload page', async () => {
+ await ml.testExecution.logTestStep('loads the file upload page');
await ml.dataVisualizer.navigateToFileUpload();
- });
- it('selects a file and displays an error', async () => {
+ await ml.testExecution.logTestStep('selects a file and displays an error');
await ml.dataVisualizerFileBased.selectFile(testData.filePath, true);
});
});
diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts
index eb76a8b4298af..2dc1d9ec00eca 100644
--- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts
+++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts
@@ -292,67 +292,74 @@ export default function ({ getService }: FtrProviderContext) {
};
function runTests(testData: TestData) {
- it(`${testData.suiteTitle} loads the saved search selection page`, async () => {
+ it(`${testData.suiteTitle} loads the source data in the data visualizer`, async () => {
+ await ml.testExecution.logTestStep(
+ `${testData.suiteTitle} loads the saved search selection page`
+ );
await ml.dataVisualizer.navigateToIndexPatternSelection();
- });
- it(`${testData.suiteTitle} loads the index data visualizer page`, async () => {
+ await ml.testExecution.logTestStep(
+ `${testData.suiteTitle} loads the index data visualizer page`
+ );
await ml.jobSourceSelection.selectSourceForIndexBasedDataVisualizer(
testData.sourceIndexOrSavedSearch
);
});
- it(`${testData.suiteTitle} displays the time range step`, async () => {
+ it(`${testData.suiteTitle} displays index details`, async () => {
+ await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the time range step`);
await ml.dataVisualizerIndexBased.assertTimeRangeSelectorSectionExists();
- });
- it(`${testData.suiteTitle} loads data for full time range`, async () => {
+ await ml.testExecution.logTestStep(`${testData.suiteTitle} loads data for full time range`);
await ml.dataVisualizerIndexBased.clickUseFullDataButton(testData.expected.totalDocCount);
- });
- it(`${testData.suiteTitle} displays the panels of fields`, async () => {
+ await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the panels of fields`);
await ml.dataVisualizerIndexBased.assertFieldsPanelsExist(testData.expected.fieldsPanelCount);
- });
- if (testData.expected.metricCards !== undefined && testData.expected.metricCards.length > 0) {
- it(`${testData.suiteTitle} displays the Metrics panel`, async () => {
+ if (testData.expected.metricCards !== undefined && testData.expected.metricCards.length > 0) {
+ await ml.testExecution.logTestStep(`${testData.suiteTitle} displays the Metrics panel`);
await ml.dataVisualizerIndexBased.assertFieldsPanelForTypesExist([
ML_JOB_FIELD_TYPES.NUMBER,
]); // document_count not exposed as a type in the panel
- });
- it(`${testData.suiteTitle} displays the expected metric field cards`, async () => {
+ await ml.testExecution.logTestStep(
+ `${testData.suiteTitle} displays the expected metric field cards`
+ );
for (const fieldCard of testData.expected.metricCards as FieldVisConfig[]) {
await ml.dataVisualizerIndexBased.assertCardExists(fieldCard.type, fieldCard.fieldName);
}
- });
- it(`${testData.suiteTitle} filters metric fields cards with search`, async () => {
+ await ml.testExecution.logTestStep(
+ `${testData.suiteTitle} filters metric fields cards with search`
+ );
await ml.dataVisualizerIndexBased.filterFieldsPanelWithSearchString(
['number'],
testData.metricFieldsFilter,
testData.expected.metricFieldsFilterCardCount
);
- });
- }
+ }
- if (
- testData.expected.nonMetricCards !== undefined &&
- testData.expected.nonMetricCards.length > 0
- ) {
- it(`${testData.suiteTitle} displays the non-metric Fields panel`, async () => {
+ if (
+ testData.expected.nonMetricCards !== undefined &&
+ testData.expected.nonMetricCards.length > 0
+ ) {
+ await ml.testExecution.logTestStep(
+ `${testData.suiteTitle} displays the non-metric Fields panel`
+ );
await ml.dataVisualizerIndexBased.assertFieldsPanelForTypesExist(
getFieldTypes(testData.expected.nonMetricCards as FieldVisConfig[])
);
- });
- it(`${testData.suiteTitle} displays the expected non-metric field cards`, async () => {
+ await ml.testExecution.logTestStep(
+ `${testData.suiteTitle} displays the expected non-metric field cards`
+ );
for (const fieldCard of testData.expected.nonMetricCards!) {
await ml.dataVisualizerIndexBased.assertCardExists(fieldCard.type, fieldCard.fieldName);
}
- });
- it(`${testData.suiteTitle} sets the non metric field types input`, async () => {
+ await ml.testExecution.logTestStep(
+ `${testData.suiteTitle} sets the non metric field types input`
+ );
const fieldTypes: ML_JOB_FIELD_TYPES[] = getFieldTypes(
testData.expected.nonMetricCards as FieldVisConfig[]
);
@@ -362,16 +369,17 @@ export default function ({ getService }: FtrProviderContext) {
testData.nonMetricFieldsTypeFilter,
testData.expected.nonMetricFieldsTypeFilterCardCount
);
- });
- it(`${testData.suiteTitle} filters non-metric fields cards with search`, async () => {
+ await ml.testExecution.logTestStep(
+ `${testData.suiteTitle} filters non-metric fields cards with search`
+ );
await ml.dataVisualizerIndexBased.filterFieldsPanelWithSearchString(
getFieldTypes(testData.expected.nonMetricCards as FieldVisConfig[]),
testData.nonMetricFieldsFilter,
testData.expected.nonMetricFieldsFilterCardCount
);
- });
- }
+ }
+ });
}
describe('index based', function () {
diff --git a/x-pack/test/functional/apps/ml/pages.ts b/x-pack/test/functional/apps/ml/pages.ts
index 3691e6b1afcdc..5d084d5abe11e 100644
--- a/x-pack/test/functional/apps/ml/pages.ts
+++ b/x-pack/test/functional/apps/ml/pages.ts
@@ -16,53 +16,49 @@ export default function ({ getService }: FtrProviderContext) {
await ml.securityUI.loginAsMlPowerUser();
});
- it('loads the home page', async () => {
+ it('loads the ML pages', async () => {
+ await ml.testExecution.logTestStep('home');
await ml.navigation.navigateToMl();
- });
- it('loads the overview page', async () => {
+ await ml.testExecution.logTestStep('loads the overview page');
await ml.navigation.navigateToOverview();
- });
- it('loads the anomaly detection area', async () => {
+ await ml.testExecution.logTestStep('loads the anomaly detection area');
await ml.navigation.navigateToAnomalyDetection();
- });
- it('loads the job management page', async () => {
+ await ml.testExecution.logTestStep('loads the job management page');
await ml.navigation.navigateToJobManagement();
await ml.jobManagement.assertJobStatsBarExists();
await ml.jobManagement.assertJobTableExists();
await ml.jobManagement.assertCreateNewJobButtonExists();
- });
- it('loads the settings page', async () => {
+ await ml.testExecution.logTestStep('loads the settings page');
await ml.navigation.navigateToSettings();
await ml.settings.assertSettingsManageCalendarsLinkExists();
await ml.settings.assertSettingsCreateCalendarLinkExists();
await ml.settings.assertSettingsManageFilterListsLinkExists();
await ml.settings.assertSettingsCreateFilterListLinkExists();
- });
- it('loads the data frame analytics page', async () => {
+ await ml.testExecution.logTestStep('loads the data frame analytics page');
await ml.navigation.navigateToDataFrameAnalytics();
await ml.dataFrameAnalytics.assertEmptyListMessageExists();
- });
- it('loads the data visualizer page', async () => {
+ await ml.testExecution.logTestStep('loads the data visualizer page');
await ml.navigation.navigateToDataVisualizer();
await ml.dataVisualizer.assertDataVisualizerImportDataCardExists();
await ml.dataVisualizer.assertDataVisualizerIndexDataCardExists();
- });
- it('should load the stack management with the ML menu item being present', async () => {
+ await ml.testExecution.logTestStep(
+ 'should load the stack management with the ML menu item being present'
+ );
await ml.navigation.navigateToStackManagement();
- });
- it('should load the jobs list page in stack management', async () => {
+ await ml.testExecution.logTestStep('should load the jobs list page in stack management');
await ml.navigation.navigateToStackManagementJobsListPage();
- });
- it('should load the analytics jobs list page in stack management', async () => {
+ await ml.testExecution.logTestStep(
+ 'should load the analytics jobs list page in stack management'
+ );
await ml.navigation.navigateToStackManagementJobsListPageAnalyticsTab();
});
});
diff --git a/x-pack/test/functional/apps/transform/cloning.ts b/x-pack/test/functional/apps/transform/cloning.ts
index d94923d9a7cce..d5c972cb8bd1f 100644
--- a/x-pack/test/functional/apps/transform/cloning.ts
+++ b/x-pack/test/functional/apps/transform/cloning.ts
@@ -87,138 +87,130 @@ export default function ({ getService }: FtrProviderContext) {
await transform.testResources.deleteIndexPatternByTitle(testData.destinationIndex);
});
- it('should load the home page', async () => {
+ it('opens the existing transform in the wizard', async () => {
+ await transform.testExecution.logTestStep('should load the home page');
await transform.navigation.navigateTo();
await transform.management.assertTransformListPageExists();
- });
- it('should display the transforms table', async () => {
+ await transform.testExecution.logTestStep('should display the transforms table');
await transform.management.assertTransformsTableExists();
- });
- it('should display the original transform in the transform list', async () => {
+ await transform.testExecution.logTestStep(
+ 'should display the original transform in the transform list'
+ );
await transform.table.refreshTransformList();
await transform.table.filterWithSearchString(transformConfig.id);
const rows = await transform.table.parseTransformTable();
expect(rows.filter((row) => row.id === transformConfig.id)).to.have.length(1);
- });
- it('should show the actions popover', async () => {
+ await transform.testExecution.logTestStep('should show the actions popover');
await transform.table.assertTransformRowActions(false);
- });
- it('should display the define pivot step', async () => {
+ await transform.testExecution.logTestStep('should display the define pivot step');
await transform.table.clickTransformRowAction('Clone');
await transform.wizard.assertDefineStepActive();
});
- it('should load the index preview', async () => {
+ it('navigates through the wizard, checks and sets all needed fields', async () => {
+ await transform.testExecution.logTestStep('should load the index preview');
await transform.wizard.assertIndexPreviewLoaded();
- });
- it('should show the index preview', async () => {
+ await transform.testExecution.logTestStep('should show the index preview');
await transform.wizard.assertIndexPreview(
testData.expected.indexPreview.columns,
testData.expected.indexPreview.rows
);
- });
- it('should display the query input', async () => {
+ await transform.testExecution.logTestStep('should display the query input');
await transform.wizard.assertQueryInputExists();
await transform.wizard.assertQueryValue('');
- });
- it('should show the pre-filled group-by configuration', async () => {
+ await transform.testExecution.logTestStep(
+ 'should show the pre-filled group-by configuration'
+ );
await transform.wizard.assertGroupByEntryExists(
testData.expected.groupBy.index,
testData.expected.groupBy.label
);
- });
- it('should show the pre-filled aggs configuration', async () => {
+ await transform.testExecution.logTestStep(
+ 'should show the pre-filled aggs configuration'
+ );
await transform.wizard.assertAggregationEntryExists(
testData.expected.aggs.index,
testData.expected.aggs.label
);
- });
- it('should show the pivot preview', async () => {
+ await transform.testExecution.logTestStep('should show the pivot preview');
await transform.wizard.assertPivotPreviewChartHistogramButtonMissing();
await transform.wizard.assertPivotPreviewColumnValues(
testData.expected.pivotPreview.column,
testData.expected.pivotPreview.values
);
- });
- it('should load the details step', async () => {
+ await transform.testExecution.logTestStep('should load the details step');
await transform.wizard.advanceToDetailsStep();
- });
- it('should input the transform id', async () => {
+ await transform.testExecution.logTestStep('should input the transform id');
await transform.wizard.assertTransformIdInputExists();
await transform.wizard.assertTransformIdValue('');
await transform.wizard.setTransformId(testData.transformId);
- });
- it('should input the transform description', async () => {
+ await transform.testExecution.logTestStep('should input the transform description');
await transform.wizard.assertTransformDescriptionInputExists();
await transform.wizard.assertTransformDescriptionValue('');
await transform.wizard.setTransformDescription(testData.transformDescription);
- });
- it('should input the destination index', async () => {
+ await transform.testExecution.logTestStep('should input the destination index');
await transform.wizard.assertDestinationIndexInputExists();
await transform.wizard.assertDestinationIndexValue('');
await transform.wizard.setDestinationIndex(testData.destinationIndex);
- });
- it('should display the create index pattern switch', async () => {
+ await transform.testExecution.logTestStep(
+ 'should display the create index pattern switch'
+ );
await transform.wizard.assertCreateIndexPatternSwitchExists();
await transform.wizard.assertCreateIndexPatternSwitchCheckState(true);
- });
- it('should display the continuous mode switch', async () => {
+ await transform.testExecution.logTestStep('should display the continuous mode switch');
await transform.wizard.assertContinuousModeSwitchExists();
await transform.wizard.assertContinuousModeSwitchCheckState(false);
- });
- it('should load the create step', async () => {
+ await transform.testExecution.logTestStep('should load the create step');
await transform.wizard.advanceToCreateStep();
- });
- it('should display the create and start button', async () => {
+ await transform.testExecution.logTestStep('should display the create and start button');
await transform.wizard.assertCreateAndStartButtonExists();
await transform.wizard.assertCreateAndStartButtonEnabled(true);
- });
- it('should display the create button', async () => {
+ await transform.testExecution.logTestStep('should display the create button');
await transform.wizard.assertCreateButtonExists();
await transform.wizard.assertCreateButtonEnabled(true);
- });
- it('should display the copy to clipboard button', async () => {
+ await transform.testExecution.logTestStep('should display the copy to clipboard button');
await transform.wizard.assertCopyToClipboardButtonExists();
await transform.wizard.assertCopyToClipboardButtonEnabled(true);
});
- it('should create the transform', async () => {
+ it('runs the clone transform and displays it correctly in the job list', async () => {
+ await transform.testExecution.logTestStep('should create the transform');
await transform.wizard.createTransform();
- });
- it('should start the transform and finish processing', async () => {
+ await transform.testExecution.logTestStep(
+ 'should start the transform and finish processing'
+ );
await transform.wizard.startTransform();
await transform.wizard.waitForProgressBarComplete();
- });
- it('should return to the management page', async () => {
+ await transform.testExecution.logTestStep('should return to the management page');
await transform.wizard.returnToManagement();
- });
- it('should display the transforms table', async () => {
+ await transform.testExecution.logTestStep('should display the transforms table');
await transform.management.assertTransformsTableExists();
- });
- it('should display the created transform in the transform list', async () => {
+ await transform.testExecution.logTestStep(
+ 'should display the created transform in the transform list'
+ );
await transform.table.refreshTransformList();
await transform.table.filterWithSearchString(testData.transformId);
const rows = await transform.table.parseTransformTable();
diff --git a/x-pack/test/functional/apps/transform/creation_index_pattern.ts b/x-pack/test/functional/apps/transform/creation_index_pattern.ts
index 7c9983101f607..daecc26186ac1 100644
--- a/x-pack/test/functional/apps/transform/creation_index_pattern.ts
+++ b/x-pack/test/functional/apps/transform/creation_index_pattern.ts
@@ -260,63 +260,54 @@ export default function ({ getService }: FtrProviderContext) {
await transform.testResources.deleteIndexPatternByTitle(testData.destinationIndex);
});
- it('loads the home page', async () => {
+ it('loads the wizard for the source data', async () => {
+ await transform.testExecution.logTestStep('loads the home page');
await transform.navigation.navigateTo();
await transform.management.assertTransformListPageExists();
- });
- it('displays the stats bar', async () => {
+ await transform.testExecution.logTestStep('displays the stats bar');
await transform.management.assertTransformStatsBarExists();
- });
- it('loads the source selection modal', async () => {
+ await transform.testExecution.logTestStep('loads the source selection modal');
await transform.management.startTransformCreation();
- });
- it('selects the source data', async () => {
+ await transform.testExecution.logTestStep('selects the source data');
await transform.sourceSelection.selectSource(testData.source);
});
- it('displays the define pivot step', async () => {
+ it('navigates through the wizard and sets all needed fields', async () => {
+ await transform.testExecution.logTestStep('displays the define pivot step');
await transform.wizard.assertDefineStepActive();
- });
- it('loads the index preview', async () => {
+ await transform.testExecution.logTestStep('loads the index preview');
await transform.wizard.assertIndexPreviewLoaded();
- });
- it('shows the index preview', async () => {
+ await transform.testExecution.logTestStep('shows the index preview');
await transform.wizard.assertIndexPreview(
testData.expected.indexPreview.columns,
testData.expected.indexPreview.rows
);
- });
- it('displays an empty pivot preview', async () => {
+ await transform.testExecution.logTestStep('displays an empty pivot preview');
await transform.wizard.assertPivotPreviewEmpty();
- });
- it('displays the query input', async () => {
+ await transform.testExecution.logTestStep('displays the query input');
await transform.wizard.assertQueryInputExists();
await transform.wizard.assertQueryValue('');
- });
- it('displays the advanced query editor switch', async () => {
+ await transform.testExecution.logTestStep('displays the advanced query editor switch');
await transform.wizard.assertAdvancedQueryEditorSwitchExists();
await transform.wizard.assertAdvancedQueryEditorSwitchCheckState(false);
- });
- it('enables the index preview histogram charts', async () => {
+ await transform.testExecution.logTestStep('enables the index preview histogram charts');
await transform.wizard.enableIndexPreviewHistogramCharts();
- });
- it('displays the index preview histogram charts', async () => {
+ await transform.testExecution.logTestStep('displays the index preview histogram charts');
await transform.wizard.assertIndexPreviewHistogramCharts(
testData.expected.histogramCharts
);
- });
- it('adds the group by entries', async () => {
+ await transform.testExecution.logTestStep('adds the group by entries');
for (const [index, entry] of testData.groupByEntries.entries()) {
await transform.wizard.assertGroupByInputExists();
await transform.wizard.assertGroupByInputValue([]);
@@ -327,112 +318,97 @@ export default function ({ getService }: FtrProviderContext) {
entry.intervalLabel
);
}
- });
- it('adds the aggregation entries', async () => {
+ await transform.testExecution.logTestStep('adds the aggregation entries');
await transform.wizard.addAggregationEntries(testData.aggregationEntries);
- });
- it('displays the advanced pivot editor switch', async () => {
+ await transform.testExecution.logTestStep('displays the advanced pivot editor switch');
await transform.wizard.assertAdvancedPivotEditorSwitchExists();
await transform.wizard.assertAdvancedPivotEditorSwitchCheckState(false);
- });
- it('displays the advanced configuration', async () => {
+ await transform.testExecution.logTestStep('displays the advanced configuration');
await transform.wizard.enabledAdvancedPivotEditor();
await transform.wizard.assertAdvancedPivotEditorContent(
testData.expected.pivotAdvancedEditorValueArr
);
- });
- it('loads the pivot preview', async () => {
+ await transform.testExecution.logTestStep('loads the pivot preview');
await transform.wizard.assertPivotPreviewLoaded();
- });
- it('shows the pivot preview', async () => {
+ await transform.testExecution.logTestStep('shows the pivot preview');
await transform.wizard.assertPivotPreviewChartHistogramButtonMissing();
await transform.wizard.assertPivotPreviewColumnValues(
testData.expected.pivotPreview.column,
testData.expected.pivotPreview.values
);
- });
- it('loads the details step', async () => {
+ await transform.testExecution.logTestStep('loads the details step');
await transform.wizard.advanceToDetailsStep();
- });
- it('inputs the transform id', async () => {
+ await transform.testExecution.logTestStep('inputs the transform id');
await transform.wizard.assertTransformIdInputExists();
await transform.wizard.assertTransformIdValue('');
await transform.wizard.setTransformId(testData.transformId);
- });
- it('inputs the transform description', async () => {
+ await transform.testExecution.logTestStep('inputs the transform description');
await transform.wizard.assertTransformDescriptionInputExists();
await transform.wizard.assertTransformDescriptionValue('');
await transform.wizard.setTransformDescription(testData.transformDescription);
- });
- it('inputs the destination index', async () => {
+ await transform.testExecution.logTestStep('inputs the destination index');
await transform.wizard.assertDestinationIndexInputExists();
await transform.wizard.assertDestinationIndexValue('');
await transform.wizard.setDestinationIndex(testData.destinationIndex);
- });
- it('displays the create index pattern switch', async () => {
+ await transform.testExecution.logTestStep('displays the create index pattern switch');
await transform.wizard.assertCreateIndexPatternSwitchExists();
await transform.wizard.assertCreateIndexPatternSwitchCheckState(true);
- });
- it('displays the continuous mode switch', async () => {
+ await transform.testExecution.logTestStep('displays the continuous mode switch');
await transform.wizard.assertContinuousModeSwitchExists();
await transform.wizard.assertContinuousModeSwitchCheckState(false);
- });
- it('loads the create step', async () => {
+ await transform.testExecution.logTestStep('loads the create step');
await transform.wizard.advanceToCreateStep();
- });
- it('displays the create and start button', async () => {
+ await transform.testExecution.logTestStep('displays the create and start button');
await transform.wizard.assertCreateAndStartButtonExists();
await transform.wizard.assertCreateAndStartButtonEnabled(true);
- });
- it('displays the create button', async () => {
+ await transform.testExecution.logTestStep('displays the create button');
await transform.wizard.assertCreateButtonExists();
await transform.wizard.assertCreateButtonEnabled(true);
- });
- it('displays the copy to clipboard button', async () => {
+ await transform.testExecution.logTestStep('displays the copy to clipboard button');
await transform.wizard.assertCopyToClipboardButtonExists();
await transform.wizard.assertCopyToClipboardButtonEnabled(true);
});
- it('creates the transform', async () => {
+ it('runs the transform and displays it correctly in the job list', async () => {
+ await transform.testExecution.logTestStep('creates the transform');
await transform.wizard.createTransform();
- });
- it('starts the transform and finishes processing', async () => {
+ await transform.testExecution.logTestStep('starts the transform and finishes processing');
await transform.wizard.startTransform();
await transform.wizard.waitForProgressBarComplete();
- });
- it('returns to the management page', async () => {
+ await transform.testExecution.logTestStep('returns to the management page');
await transform.wizard.returnToManagement();
- });
- it('displays the transforms table', async () => {
+ await transform.testExecution.logTestStep('displays the transforms table');
await transform.management.assertTransformsTableExists();
- });
- it('displays the created transform in the transform list', async () => {
+ await transform.testExecution.logTestStep(
+ 'displays the created transform in the transform list'
+ );
await transform.table.refreshTransformList();
await transform.table.filterWithSearchString(testData.transformId);
const rows = await transform.table.parseTransformTable();
expect(rows.filter((row) => row.id === testData.transformId)).to.have.length(1);
- });
- it('transform creation displays details for the created transform in the transform list', async () => {
+ await transform.testExecution.logTestStep(
+ 'transform creation displays details for the created transform in the transform list'
+ );
await transform.table.assertTransformRowFields(testData.transformId, {
id: testData.transformId,
description: testData.transformDescription,
diff --git a/x-pack/test/functional/apps/transform/creation_saved_search.ts b/x-pack/test/functional/apps/transform/creation_saved_search.ts
index 54cc5b3f62933..d3cbc1159a9c7 100644
--- a/x-pack/test/functional/apps/transform/creation_saved_search.ts
+++ b/x-pack/test/functional/apps/transform/creation_saved_search.ts
@@ -79,51 +79,44 @@ export default function ({ getService }: FtrProviderContext) {
await transform.testResources.deleteIndexPatternByTitle(testData.destinationIndex);
});
- it('loads the home page', async () => {
+ it('loads the wizard for the source data', async () => {
+ await transform.testExecution.logTestStep('loads the home page');
await transform.navigation.navigateTo();
await transform.management.assertTransformListPageExists();
- });
- it('displays the stats bar', async () => {
+ await transform.testExecution.logTestStep('displays the stats bar');
await transform.management.assertTransformStatsBarExists();
- });
- it('loads the source selection modal', async () => {
+ await transform.testExecution.logTestStep('loads the source selection modal');
await transform.management.startTransformCreation();
- });
- it('selects the source data', async () => {
+ await transform.testExecution.logTestStep('selects the source data');
await transform.sourceSelection.selectSource(testData.source);
});
- it('displays the define pivot step', async () => {
+ it('navigates through the wizard and sets all needed fields', async () => {
+ await transform.testExecution.logTestStep('displays the define pivot step');
await transform.wizard.assertDefineStepActive();
- });
- it('loads the index preview', async () => {
+ await transform.testExecution.logTestStep('loads the index preview');
await transform.wizard.assertIndexPreviewLoaded();
- });
- it('shows the filtered index preview', async () => {
+ await transform.testExecution.logTestStep('shows the filtered index preview');
await transform.wizard.assertIndexPreviewColumnValues(
testData.expected.indexPreview.column,
testData.expected.indexPreview.values
);
- });
- it('displays an empty pivot preview', async () => {
+ await transform.testExecution.logTestStep('displays an empty pivot preview');
await transform.wizard.assertPivotPreviewEmpty();
- });
- it('hides the query input', async () => {
+ await transform.testExecution.logTestStep('hides the query input');
await transform.wizard.assertQueryInputMissing();
- });
- it('hides the advanced query editor switch', async () => {
+ await transform.testExecution.logTestStep('hides the advanced query editor switch');
await transform.wizard.assertAdvancedQueryEditorSwitchMissing();
- });
- it('adds the group by entries', async () => {
+ await transform.testExecution.logTestStep('adds the group by entries');
for (const [index, entry] of testData.groupByEntries.entries()) {
await transform.wizard.assertGroupByInputExists();
await transform.wizard.assertGroupByInputValue([]);
@@ -134,108 +127,94 @@ export default function ({ getService }: FtrProviderContext) {
entry.intervalLabel
);
}
- });
- it('adds the aggregation entries', async () => {
+ await transform.testExecution.logTestStep('adds the aggregation entries');
for (const [index, agg] of testData.aggregationEntries.entries()) {
await transform.wizard.assertAggregationInputExists();
await transform.wizard.assertAggregationInputValue([]);
await transform.wizard.addAggregationEntry(index, agg.identifier, agg.label);
}
- });
- it('displays the advanced pivot editor switch', async () => {
+ await transform.testExecution.logTestStep('displays the advanced pivot editor switch');
await transform.wizard.assertAdvancedPivotEditorSwitchExists();
await transform.wizard.assertAdvancedPivotEditorSwitchCheckState(false);
- });
- it('loads the pivot preview', async () => {
+ await transform.testExecution.logTestStep('loads the pivot preview');
await transform.wizard.assertPivotPreviewLoaded();
- });
- it('shows the pivot preview', async () => {
+ await transform.testExecution.logTestStep('shows the pivot preview');
await transform.wizard.assertPivotPreviewColumnValues(
testData.expected.pivotPreview.column,
testData.expected.pivotPreview.values
);
- });
- it('loads the details step', async () => {
+ await transform.testExecution.logTestStep('loads the details step');
await transform.wizard.advanceToDetailsStep();
- });
- it('inputs the transform id', async () => {
+ await transform.testExecution.logTestStep('inputs the transform id');
await transform.wizard.assertTransformIdInputExists();
await transform.wizard.assertTransformIdValue('');
await transform.wizard.setTransformId(testData.transformId);
- });
- it('inputs the transform description', async () => {
+ await transform.testExecution.logTestStep('inputs the transform description');
await transform.wizard.assertTransformDescriptionInputExists();
await transform.wizard.assertTransformDescriptionValue('');
await transform.wizard.setTransformDescription(testData.transformDescription);
- });
- it('inputs the destination index', async () => {
+ await transform.testExecution.logTestStep('inputs the destination index');
await transform.wizard.assertDestinationIndexInputExists();
await transform.wizard.assertDestinationIndexValue('');
await transform.wizard.setDestinationIndex(testData.destinationIndex);
- });
- it('displays the create index pattern switch', async () => {
+ await transform.testExecution.logTestStep('displays the create index pattern switch');
await transform.wizard.assertCreateIndexPatternSwitchExists();
await transform.wizard.assertCreateIndexPatternSwitchCheckState(true);
- });
- it('displays the continuous mode switch', async () => {
+ await transform.testExecution.logTestStep('displays the continuous mode switch');
await transform.wizard.assertContinuousModeSwitchExists();
await transform.wizard.assertContinuousModeSwitchCheckState(false);
- });
- it('loads the create step', async () => {
+ await transform.testExecution.logTestStep('loads the create step');
await transform.wizard.advanceToCreateStep();
- });
- it('displays the create and start button', async () => {
+ await transform.testExecution.logTestStep('displays the create and start button');
await transform.wizard.assertCreateAndStartButtonExists();
await transform.wizard.assertCreateAndStartButtonEnabled(true);
- });
- it('displays the create button', async () => {
+ await transform.testExecution.logTestStep('displays the create button');
await transform.wizard.assertCreateButtonExists();
await transform.wizard.assertCreateButtonEnabled(true);
- });
- it('displays the copy to clipboard button', async () => {
+ await transform.testExecution.logTestStep('displays the copy to clipboard button');
await transform.wizard.assertCopyToClipboardButtonExists();
await transform.wizard.assertCopyToClipboardButtonEnabled(true);
});
- it('creates the transform', async () => {
+ it('runs the transform and displays it correctly in the job list', async () => {
+ await transform.testExecution.logTestStep('creates the transform');
await transform.wizard.createTransform();
- });
- it('starts the transform and finishes processing', async () => {
+ await transform.testExecution.logTestStep('starts the transform and finishes processing');
await transform.wizard.startTransform();
await transform.wizard.waitForProgressBarComplete();
- });
- it('returns to the management page', async () => {
+ await transform.testExecution.logTestStep('returns to the management page');
await transform.wizard.returnToManagement();
- });
- it('displays the transforms table', async () => {
+ await transform.testExecution.logTestStep('displays the transforms table');
await transform.management.assertTransformsTableExists();
- });
- it('displays the created transform in the transform list', async () => {
+ await transform.testExecution.logTestStep(
+ 'displays the created transform in the transform list'
+ );
await transform.table.refreshTransformList();
await transform.table.filterWithSearchString(testData.transformId);
const rows = await transform.table.parseTransformTable();
expect(rows.filter((row) => row.id === testData.transformId)).to.have.length(1);
- });
- it('transform creation displays details for the created transform in the transform list', async () => {
+ await transform.testExecution.logTestStep(
+ 'transform creation displays details for the created transform in the transform list'
+ );
await transform.table.assertTransformRowFields(testData.transformId, {
id: testData.transformId,
description: testData.transformDescription,
@@ -243,13 +222,15 @@ export default function ({ getService }: FtrProviderContext) {
mode: testData.expected.row.mode,
progress: testData.expected.row.progress,
});
- });
- it('expands the transform management table row and walks through available tabs', async () => {
+ await transform.testExecution.logTestStep(
+ 'expands the transform management table row and walks through available tabs'
+ );
await transform.table.assertTransformExpandedRow();
- });
- it('displays the transform preview in the expanded row', async () => {
+ await transform.testExecution.logTestStep(
+ 'displays the transform preview in the expanded row'
+ );
await transform.table.assertTransformsExpandedRowPreviewColumnValues(
testData.expected.pivotPreview.column,
testData.expected.pivotPreview.values
diff --git a/x-pack/test/functional/apps/transform/editing.ts b/x-pack/test/functional/apps/transform/editing.ts
index 44ecca17328a7..5582d279833e7 100644
--- a/x-pack/test/functional/apps/transform/editing.ts
+++ b/x-pack/test/functional/apps/transform/editing.ts
@@ -61,32 +61,32 @@ export default function ({ getService }: FtrProviderContext) {
};
describe(`${testData.suiteTitle}`, function () {
- it('should load the home page', async () => {
+ it('opens the edit flyout for an existing transform', async () => {
+ await transform.testExecution.logTestStep('should load the home page');
await transform.navigation.navigateTo();
await transform.management.assertTransformListPageExists();
- });
- it('should display the transforms table', async () => {
+ await transform.testExecution.logTestStep('should display the transforms table');
await transform.management.assertTransformsTableExists();
- });
- it('should display the original transform in the transform list', async () => {
+ await transform.testExecution.logTestStep(
+ 'should display the original transform in the transform list'
+ );
await transform.table.refreshTransformList();
await transform.table.filterWithSearchString(transformConfig.id);
const rows = await transform.table.parseTransformTable();
expect(rows.filter((row) => row.id === transformConfig.id)).to.have.length(1);
- });
- it('should show the actions popover', async () => {
+ await transform.testExecution.logTestStep('should show the actions popover');
await transform.table.assertTransformRowActions(false);
- });
- it('should show the edit flyout', async () => {
+ await transform.testExecution.logTestStep('should show the edit flyout');
await transform.table.clickTransformRowAction('Edit');
await transform.editFlyout.assertTransformEditFlyoutExists();
});
- it('should update the transform description', async () => {
+ it('navigates through the edit flyout and sets all needed fields', async () => {
+ await transform.testExecution.logTestStep('should update the transform description');
await transform.editFlyout.assertTransformEditFlyoutInputExists('Description');
await transform.editFlyout.assertTransformEditFlyoutInputValue(
'Description',
@@ -96,18 +96,18 @@ export default function ({ getService }: FtrProviderContext) {
'Description',
testData.transformDescription
);
- });
- it('should update the transform documents per second', async () => {
+ await transform.testExecution.logTestStep(
+ 'should update the transform documents per second'
+ );
await transform.editFlyout.assertTransformEditFlyoutInputExists('DocsPerSecond');
await transform.editFlyout.assertTransformEditFlyoutInputValue('DocsPerSecond', '');
await transform.editFlyout.setTransformEditFlyoutInputValue(
'DocsPerSecond',
testData.transformDocsPerSecond
);
- });
- it('should update the transform frequency', async () => {
+ await transform.testExecution.logTestStep('should update the transform frequency');
await transform.editFlyout.assertTransformEditFlyoutInputExists('Frequency');
await transform.editFlyout.assertTransformEditFlyoutInputValue('Frequency', '');
await transform.editFlyout.setTransformEditFlyoutInputValue(
@@ -116,22 +116,24 @@ export default function ({ getService }: FtrProviderContext) {
);
});
- it('should update the transform', async () => {
+ it('updates the transform and displays it correctly in the job list', async () => {
+ await transform.testExecution.logTestStep('should update the transform');
await transform.editFlyout.updateTransform();
- });
- it('should display the transforms table', async () => {
+ await transform.testExecution.logTestStep('should display the transforms table');
await transform.management.assertTransformsTableExists();
- });
- it('should display the updated transform in the transform list', async () => {
+ await transform.testExecution.logTestStep(
+ 'should display the updated transform in the transform list'
+ );
await transform.table.refreshTransformList();
await transform.table.filterWithSearchString(transformConfig.id);
const rows = await transform.table.parseTransformTable();
expect(rows.filter((row) => row.id === transformConfig.id)).to.have.length(1);
- });
- it('should display the updated transform in the transform list row cells', async () => {
+ await transform.testExecution.logTestStep(
+ 'should display the updated transform in the transform list row cells'
+ );
await transform.table.assertTransformRowFields(transformConfig.id, {
id: transformConfig.id,
description: testData.transformDescription,
@@ -139,9 +141,10 @@ export default function ({ getService }: FtrProviderContext) {
mode: testData.expected.row.mode,
progress: testData.expected.row.progress,
});
- });
- it('should display the messages tab and include an update message', async () => {
+ await transform.testExecution.logTestStep(
+ 'should display the messages tab and include an update message'
+ );
await transform.table.assertTransformExpandedRowMessages(testData.expected.messageText);
});
});
diff --git a/x-pack/test/functional/services/ml/common_api.ts b/x-pack/test/functional/services/ml/common_api.ts
new file mode 100644
index 0000000000000..e68dbf4bdcaf6
--- /dev/null
+++ b/x-pack/test/functional/services/ml/common_api.ts
@@ -0,0 +1,22 @@
+/*
+ * 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 { ProvidedType } from '@kbn/test/types/ftr';
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export const COMMON_REQUEST_HEADERS = {
+ 'kbn-xsrf': 'some-xsrf-token',
+};
+
+export type MlCommonAPI = ProvidedType;
+
+export function MachineLearningCommonAPIProvider({}: FtrProviderContext) {
+ return {
+ async getCommonRequestHeader() {
+ return COMMON_REQUEST_HEADERS;
+ },
+ };
+}
diff --git a/x-pack/test/functional/services/ml/common.ts b/x-pack/test/functional/services/ml/common_ui.ts
similarity index 92%
rename from x-pack/test/functional/services/ml/common.ts
rename to x-pack/test/functional/services/ml/common_ui.ts
index 3333a81f028fa..b66fd7087654d 100644
--- a/x-pack/test/functional/services/ml/common.ts
+++ b/x-pack/test/functional/services/ml/common_ui.ts
@@ -12,13 +12,9 @@ interface SetValueOptions {
typeCharByChar?: boolean;
}
-export const COMMON_REQUEST_HEADERS = {
- 'kbn-xsrf': 'some-xsrf-token',
-};
+export type MlCommonUI = ProvidedType;
-export type MlCommon = ProvidedType;
-
-export function MachineLearningCommonProvider({ getService }: FtrProviderContext) {
+export function MachineLearningCommonUIProvider({ getService }: FtrProviderContext) {
const log = getService('log');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts
index cdd26b60d3be0..ffa1d9fd46c75 100644
--- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts
+++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts
@@ -7,7 +7,7 @@ import expect from '@kbn/expect';
import { DataFrameAnalyticsConfig } from '../../../../plugins/ml/public/application/data_frame_analytics/common';
import { FtrProviderContext } from '../../ftr_provider_context';
-import { MlCommon } from './common';
+import { MlCommonUI } from './common_ui';
import { MlApi } from './api';
import {
ClassificationAnalysis,
@@ -32,7 +32,7 @@ const isClassificationAnalysis = (arg: any): arg is ClassificationAnalysis => {
export function MachineLearningDataFrameAnalyticsCreationProvider(
{ getService }: FtrProviderContext,
- mlCommon: MlCommon,
+ mlCommonUI: MlCommonUI,
mlApi: MlApi
) {
const testSubjects = getService('testSubjects');
@@ -113,16 +113,20 @@ export function MachineLearningDataFrameAnalyticsCreationProvider(
},
async setJobId(jobId: string) {
- await mlCommon.setValueWithChecks('mlAnalyticsCreateJobFlyoutJobIdInput', jobId, {
+ await mlCommonUI.setValueWithChecks('mlAnalyticsCreateJobFlyoutJobIdInput', jobId, {
clearWithKeyboard: true,
});
await this.assertJobIdValue(jobId);
},
async setJobDescription(jobDescription: string) {
- await mlCommon.setValueWithChecks('mlDFAnalyticsJobCreationJobDescription', jobDescription, {
- clearWithKeyboard: true,
- });
+ await mlCommonUI.setValueWithChecks(
+ 'mlDFAnalyticsJobCreationJobDescription',
+ jobDescription,
+ {
+ clearWithKeyboard: true,
+ }
+ );
await this.assertJobDescriptionValue(jobDescription);
},
@@ -218,7 +222,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider(
},
async setDestIndex(destIndex: string) {
- await mlCommon.setValueWithChecks(
+ await mlCommonUI.setValueWithChecks(
'mlAnalyticsCreateJobFlyoutDestinationIndexInput',
destIndex,
{
@@ -405,7 +409,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider(
async setModelMemory(modelMemory: string) {
await retry.tryForTime(15 * 1000, async () => {
- await mlCommon.setValueWithChecks(
+ await mlCommonUI.setValueWithChecks(
'mlAnalyticsCreateJobWizardModelMemoryInput',
modelMemory,
{
diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_edit.ts b/x-pack/test/functional/services/ml/data_frame_analytics_edit.ts
index fd06dd24d6f8b..6e446667875d9 100644
--- a/x-pack/test/functional/services/ml/data_frame_analytics_edit.ts
+++ b/x-pack/test/functional/services/ml/data_frame_analytics_edit.ts
@@ -6,11 +6,11 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
-import { MlCommon } from './common';
+import { MlCommonUI } from './common_ui';
export function MachineLearningDataFrameAnalyticsEditProvider(
{ getService }: FtrProviderContext,
- mlCommon: MlCommon
+ mlCommonUI: MlCommonUI
) {
const testSubjects = getService('testSubjects');
const retry = getService('retry');
@@ -43,14 +43,14 @@ export function MachineLearningDataFrameAnalyticsEditProvider(
);
},
async setJobDescriptionEdit(jobDescription: string) {
- await mlCommon.setValueWithChecks('mlAnalyticsEditFlyoutDescriptionInput', jobDescription, {
+ await mlCommonUI.setValueWithChecks('mlAnalyticsEditFlyoutDescriptionInput', jobDescription, {
clearWithKeyboard: true,
});
await this.assertJobDescriptionEditValue(jobDescription);
},
async setJobMmlEdit(mml: string) {
- await mlCommon.setValueWithChecks('mlAnalyticsEditFlyoutmodelMemoryLimitInput', mml, {
+ await mlCommonUI.setValueWithChecks('mlAnalyticsEditFlyoutmodelMemoryLimitInput', mml, {
clearWithKeyboard: true,
});
await this.assertJobMmlEditValue(mml);
diff --git a/x-pack/test/functional/services/ml/data_visualizer_file_based.ts b/x-pack/test/functional/services/ml/data_visualizer_file_based.ts
index 8c5e40dd5dbdd..14c6f8de7d329 100644
--- a/x-pack/test/functional/services/ml/data_visualizer_file_based.ts
+++ b/x-pack/test/functional/services/ml/data_visualizer_file_based.ts
@@ -7,11 +7,11 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
-import { MlCommon } from './common';
+import { MlCommonUI } from './common_ui';
export function MachineLearningDataVisualizerFileBasedProvider(
{ getService, getPageObjects }: FtrProviderContext,
- mlCommon: MlCommon
+ mlCommonUI: MlCommonUI
) {
const log = getService('log');
const retry = getService('retry');
@@ -75,7 +75,7 @@ export function MachineLearningDataVisualizerFileBasedProvider(
},
async setIndexName(indexName: string) {
- await mlCommon.setValueWithChecks('mlFileDataVisIndexNameInput', indexName, {
+ await mlCommonUI.setValueWithChecks('mlFileDataVisIndexNameInput', indexName, {
clearWithKeyboard: true,
});
await this.assertIndexNameValue(indexName);
diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts
index fd36bb0f47f95..d7ff60440bf31 100644
--- a/x-pack/test/functional/services/ml/index.ts
+++ b/x-pack/test/functional/services/ml/index.ts
@@ -9,7 +9,8 @@ import { FtrProviderContext } from '../../ftr_provider_context';
import { MachineLearningAnomaliesTableProvider } from './anomalies_table';
import { MachineLearningAnomalyExplorerProvider } from './anomaly_explorer';
import { MachineLearningAPIProvider } from './api';
-import { MachineLearningCommonProvider } from './common';
+import { MachineLearningCommonAPIProvider } from './common_api';
+import { MachineLearningCommonUIProvider } from './common_ui';
import { MachineLearningCustomUrlsProvider } from './custom_urls';
import { MachineLearningDataFrameAnalyticsProvider } from './data_frame_analytics';
import { MachineLearningDataFrameAnalyticsCreationProvider } from './data_frame_analytics_creation';
@@ -33,10 +34,12 @@ import { MachineLearningSecurityCommonProvider } from './security_common';
import { MachineLearningSecurityUIProvider } from './security_ui';
import { MachineLearningSettingsProvider } from './settings';
import { MachineLearningSingleMetricViewerProvider } from './single_metric_viewer';
+import { MachineLearningTestExecutionProvider } from './test_execution';
import { MachineLearningTestResourcesProvider } from './test_resources';
export function MachineLearningProvider(context: FtrProviderContext) {
- const common = MachineLearningCommonProvider(context);
+ const commonAPI = MachineLearningCommonAPIProvider(context);
+ const commonUI = MachineLearningCommonUIProvider(context);
const anomaliesTable = MachineLearningAnomaliesTableProvider(context);
const anomalyExplorer = MachineLearningAnomalyExplorerProvider(context);
@@ -45,22 +48,22 @@ export function MachineLearningProvider(context: FtrProviderContext) {
const dataFrameAnalytics = MachineLearningDataFrameAnalyticsProvider(context, api);
const dataFrameAnalyticsCreation = MachineLearningDataFrameAnalyticsCreationProvider(
context,
- common,
+ commonUI,
api
);
- const dataFrameAnalyticsEdit = MachineLearningDataFrameAnalyticsEditProvider(context, common);
+ const dataFrameAnalyticsEdit = MachineLearningDataFrameAnalyticsEditProvider(context, commonUI);
const dataFrameAnalyticsTable = MachineLearningDataFrameAnalyticsTableProvider(context);
const dataVisualizer = MachineLearningDataVisualizerProvider(context);
- const dataVisualizerFileBased = MachineLearningDataVisualizerFileBasedProvider(context, common);
+ const dataVisualizerFileBased = MachineLearningDataVisualizerFileBasedProvider(context, commonUI);
const dataVisualizerIndexBased = MachineLearningDataVisualizerIndexBasedProvider(context);
const jobManagement = MachineLearningJobManagementProvider(context, api);
const jobSelection = MachineLearningJobSelectionProvider(context);
const jobSourceSelection = MachineLearningJobSourceSelectionProvider(context);
const jobTable = MachineLearningJobTableProvider(context);
const jobTypeSelection = MachineLearningJobTypeSelectionProvider(context);
- const jobWizardAdvanced = MachineLearningJobWizardAdvancedProvider(context, common);
+ const jobWizardAdvanced = MachineLearningJobWizardAdvancedProvider(context, commonUI);
const jobWizardCategorization = MachineLearningJobWizardCategorizationProvider(context);
- const jobWizardCommon = MachineLearningJobWizardCommonProvider(context, common, customUrls);
+ const jobWizardCommon = MachineLearningJobWizardCommonProvider(context, commonUI, customUrls);
const jobWizardMultiMetric = MachineLearningJobWizardMultiMetricProvider(context);
const jobWizardPopulation = MachineLearningJobWizardPopulationProvider(context);
const navigation = MachineLearningNavigationProvider(context);
@@ -68,13 +71,15 @@ export function MachineLearningProvider(context: FtrProviderContext) {
const securityUI = MachineLearningSecurityUIProvider(context, securityCommon);
const settings = MachineLearningSettingsProvider(context);
const singleMetricViewer = MachineLearningSingleMetricViewerProvider(context);
+ const testExecution = MachineLearningTestExecutionProvider(context);
const testResources = MachineLearningTestResourcesProvider(context);
return {
anomaliesTable,
anomalyExplorer,
api,
- common,
+ commonAPI,
+ commonUI,
customUrls,
dataFrameAnalytics,
dataFrameAnalyticsCreation,
@@ -98,6 +103,7 @@ export function MachineLearningProvider(context: FtrProviderContext) {
securityUI,
settings,
singleMetricViewer,
+ testExecution,
testResources,
};
}
diff --git a/x-pack/test/functional/services/ml/job_wizard_advanced.ts b/x-pack/test/functional/services/ml/job_wizard_advanced.ts
index e4d2ecf66f646..dbb0c1ceeb14e 100644
--- a/x-pack/test/functional/services/ml/job_wizard_advanced.ts
+++ b/x-pack/test/functional/services/ml/job_wizard_advanced.ts
@@ -6,11 +6,11 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
-import { MlCommon } from './common';
+import { MlCommonUI } from './common_ui';
export function MachineLearningJobWizardAdvancedProvider(
{ getService }: FtrProviderContext,
- mlCommon: MlCommon
+ mlCommonUI: MlCommonUI
) {
const comboBox = getService('comboBox');
const testSubjects = getService('testSubjects');
@@ -54,7 +54,7 @@ export function MachineLearningJobWizardAdvancedProvider(
},
async setQueryDelay(queryDelay: string) {
- await mlCommon.setValueWithChecks('mlJobWizardInputQueryDelay', queryDelay, {
+ await mlCommonUI.setValueWithChecks('mlJobWizardInputQueryDelay', queryDelay, {
clearWithKeyboard: true,
typeCharByChar: true,
});
@@ -74,7 +74,7 @@ export function MachineLearningJobWizardAdvancedProvider(
},
async setFrequency(frequency: string) {
- await mlCommon.setValueWithChecks('mlJobWizardInputFrequency', frequency, {
+ await mlCommonUI.setValueWithChecks('mlJobWizardInputFrequency', frequency, {
clearWithKeyboard: true,
typeCharByChar: true,
});
@@ -94,7 +94,7 @@ export function MachineLearningJobWizardAdvancedProvider(
},
async setScrollSize(scrollSize: string) {
- await mlCommon.setValueWithChecks('mlJobWizardInputScrollSize', scrollSize, {
+ await mlCommonUI.setValueWithChecks('mlJobWizardInputScrollSize', scrollSize, {
clearWithKeyboard: true,
typeCharByChar: true,
});
@@ -303,7 +303,7 @@ export function MachineLearningJobWizardAdvancedProvider(
},
async setDetectorDescription(description: string) {
- await mlCommon.setValueWithChecks('mlAdvancedDetectorDescriptionInput', description, {
+ await mlCommonUI.setValueWithChecks('mlAdvancedDetectorDescriptionInput', description, {
clearWithKeyboard: true,
});
await this.assertDetectorDescriptionValue(description);
diff --git a/x-pack/test/functional/services/ml/job_wizard_common.ts b/x-pack/test/functional/services/ml/job_wizard_common.ts
index 2843c36e08a1d..97253c5f45303 100644
--- a/x-pack/test/functional/services/ml/job_wizard_common.ts
+++ b/x-pack/test/functional/services/ml/job_wizard_common.ts
@@ -6,12 +6,12 @@
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
-import { MlCommon } from './common';
+import { MlCommonUI } from './common_ui';
import { MlCustomUrls } from './custom_urls';
export function MachineLearningJobWizardCommonProvider(
{ getService }: FtrProviderContext,
- mlCommon: MlCommon,
+ mlCommonUI: MlCommonUI,
customUrls: MlCustomUrls
) {
const comboBox = getService('comboBox');
@@ -128,7 +128,7 @@ export function MachineLearningJobWizardCommonProvider(
},
async setBucketSpan(bucketSpan: string) {
- await mlCommon.setValueWithChecks('mlJobWizardInputBucketSpan', bucketSpan, {
+ await mlCommonUI.setValueWithChecks('mlJobWizardInputBucketSpan', bucketSpan, {
clearWithKeyboard: true,
typeCharByChar: true,
});
@@ -148,7 +148,7 @@ export function MachineLearningJobWizardCommonProvider(
},
async setJobId(jobId: string) {
- await mlCommon.setValueWithChecks('mlJobWizardInputJobId', jobId, {
+ await mlCommonUI.setValueWithChecks('mlJobWizardInputJobId', jobId, {
clearWithKeyboard: true,
});
await this.assertJobIdValue(jobId);
@@ -169,7 +169,7 @@ export function MachineLearningJobWizardCommonProvider(
},
async setJobDescription(jobDescription: string) {
- await mlCommon.setValueWithChecks('mlJobWizardInputJobDescription', jobDescription, {
+ await mlCommonUI.setValueWithChecks('mlJobWizardInputJobDescription', jobDescription, {
clearWithKeyboard: true,
});
await this.assertJobDescriptionValue(jobDescription);
@@ -219,11 +219,11 @@ export function MachineLearningJobWizardCommonProvider(
async addCalendar(calendarId: string) {
await this.ensureAdditionalSettingsSectionOpen();
- await comboBox.setCustom('mlJobWizardComboBoxCalendars > comboBoxInput', calendarId);
+ await comboBox.set('mlJobWizardComboBoxCalendars > comboBoxInput', calendarId);
const actualCalendarSelection = await this.getSelectedCalendars();
expect(actualCalendarSelection).to.contain(
calendarId,
- `Expected calendar selection to conatin '${calendarId}' (got '${actualCalendarSelection}')`
+ `Expected calendar selection to contain '${calendarId}' (got '${actualCalendarSelection}')`
);
},
@@ -372,7 +372,7 @@ export function MachineLearningJobWizardCommonProvider(
subj = advancedSectionSelector(subj);
}
await retry.tryForTime(15 * 1000, async () => {
- await mlCommon.setValueWithChecks(subj, modelMemoryLimit, { clearWithKeyboard: true });
+ await mlCommonUI.setValueWithChecks(subj, modelMemoryLimit, { clearWithKeyboard: true });
await this.assertModelMemoryLimitValue(modelMemoryLimit, {
withAdvancedSection: sectionOptions.withAdvancedSection,
});
diff --git a/x-pack/test/functional/services/ml/test_execution.ts b/x-pack/test/functional/services/ml/test_execution.ts
new file mode 100644
index 0000000000000..230c20b4d2d5a
--- /dev/null
+++ b/x-pack/test/functional/services/ml/test_execution.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export function MachineLearningTestExecutionProvider({ getService }: FtrProviderContext) {
+ const log = getService('log');
+
+ return {
+ async logTestStep(stepTitle: string) {
+ await log.debug(`=== TEST STEP === ${stepTitle}`);
+ },
+ };
+}
diff --git a/x-pack/test/functional/services/ml/test_resources.ts b/x-pack/test/functional/services/ml/test_resources.ts
index 675ec890b9edf..949635964c613 100644
--- a/x-pack/test/functional/services/ml/test_resources.ts
+++ b/x-pack/test/functional/services/ml/test_resources.ts
@@ -6,7 +6,7 @@
import { ProvidedType } from '@kbn/test/types/ftr';
import { savedSearches, dashboards } from './test_resources_data';
-import { COMMON_REQUEST_HEADERS } from './common';
+import { COMMON_REQUEST_HEADERS } from './common_api';
import { FtrProviderContext } from '../../ftr_provider_context';
export enum SavedObjectType {
diff --git a/x-pack/test/functional/services/transform/index.ts b/x-pack/test/functional/services/transform/index.ts
index 24091ba773218..83a227ce56604 100644
--- a/x-pack/test/functional/services/transform/index.ts
+++ b/x-pack/test/functional/services/transform/index.ts
@@ -14,6 +14,7 @@ import { TransformSecurityCommonProvider } from './security_common';
import { TransformSecurityUIProvider } from './security_ui';
import { TransformSourceSelectionProvider } from './source_selection';
import { TransformTableProvider } from './transform_table';
+import { TransformTestExecutionProvider } from './test_execution';
import { TransformWizardProvider } from './wizard';
import { MachineLearningTestResourcesProvider } from '../ml/test_resources';
@@ -27,6 +28,7 @@ export function TransformProvider(context: FtrProviderContext) {
const securityUI = TransformSecurityUIProvider(context, securityCommon);
const sourceSelection = TransformSourceSelectionProvider(context);
const table = TransformTableProvider(context);
+ const testExecution = TransformTestExecutionProvider(context);
const testResources = MachineLearningTestResourcesProvider(context);
const wizard = TransformWizardProvider(context);
@@ -39,6 +41,7 @@ export function TransformProvider(context: FtrProviderContext) {
securityUI,
sourceSelection,
table,
+ testExecution,
testResources,
wizard,
};
diff --git a/x-pack/test/functional/services/transform/test_execution.ts b/x-pack/test/functional/services/transform/test_execution.ts
new file mode 100644
index 0000000000000..ee27f9ee5194b
--- /dev/null
+++ b/x-pack/test/functional/services/transform/test_execution.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export function TransformTestExecutionProvider({ getService }: FtrProviderContext) {
+ const log = getService('log');
+
+ return {
+ async logTestStep(stepTitle: string) {
+ await log.debug(`=== TEST STEP === ${stepTitle}`);
+ },
+ };
+}
diff --git a/yarn.lock b/yarn.lock
index 0bc1ef72651e3..d13db64ad8b79 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1120,29 +1120,29 @@
enabled "2.0.x"
kuler "^2.0.0"
-"@elastic/apm-rum-core@^5.5.0":
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.5.0.tgz#e05ffd87b95420c788ed3be7cfbbbce1ff54bcf5"
- integrity sha512-fPx65oZD495WdHQ3YA8TnzqmjqlvSxoXm0tZqXQKzKVv7CMsNkolnEPSAXFl0W5pmAVRvw6T+vMmxcVIGsCD4Q==
+"@elastic/apm-rum-core@^5.6.0":
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.6.0.tgz#d1f643eb00e590d5884598a20bb54efb1490ee13"
+ integrity sha512-hG+lITWBQd0mw00BQ1zYVRKDCh5b9FKFiht9fMXcT0SENOsT5J37RIbQHPdVawluT7/mhDF07t4fR8V0xRB1/g==
dependencies:
error-stack-parser "^1.3.5"
opentracing "^0.14.3"
promise-polyfill "^8.1.3"
-"@elastic/apm-rum-react@^1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-1.2.2.tgz#b92f1491bae62de0b4296264afe73171f17af022"
- integrity sha512-KXM2qxG4p1GeDoud9jpmUA19uuQxW4M+CgtrNIXuNwITMIw46qRLyl5zOIvy9dqHodvLIvZ7RWsFtSZH4kZnAQ==
+"@elastic/apm-rum-react@^1.2.3":
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-1.2.3.tgz#fdf28492daca0ee6aa67c53a457eea1f16739e1e"
+ integrity sha512-oCjF/L46OYDRLHKt60l7aU+DFE484dwb/kKN12VZCOgueDZm4BCJd7yaosBtWDhnw0tl0Iqc0X3r4U7pQ+g9aA==
dependencies:
- "@elastic/apm-rum" "^5.4.0"
+ "@elastic/apm-rum" "^5.5.0"
hoist-non-react-statics "^3.3.0"
-"@elastic/apm-rum@^5.4.0":
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-5.4.0.tgz#2d87d5ca19f7f4a021c03f075d9d767894e88b3c"
- integrity sha512-X4uaJlM28pyDOsD06serggspbTyz7Za0zFr+OWUntI6tQKu++Tn8yGsr6L2WuXhKNGhyJmrAfh13pmy9ZGyFcg==
+"@elastic/apm-rum@^5.5.0":
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-5.5.0.tgz#24a8b4db0fa328c1e54710d18837e1adba7e51e0"
+ integrity sha512-uEOJG7Lm0CLtGfXOLXSsiPLpTPvrNUqlWQEKf/D77lpHRVWxBb56xa4X4CK2on8V1XzHDufcYBPcBcKSGozTLw==
dependencies:
- "@elastic/apm-rum-core" "^5.5.0"
+ "@elastic/apm-rum-core" "^5.6.0"
"@elastic/charts@19.8.1":
version "19.8.1"