mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[Inventory] Adding initial e2e structure (#196560)](https://github.com/elastic/kibana/pull/196560) <!--- Backport version: 8.9.8 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Cauê Marcondes","email":"55978943+cauemarcondes@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-17T13:50:11Z","message":"[Inventory] Adding initial e2e structure (#196560)\n\ncloses https://github.com/elastic/kibana/issues/193992\r\n\r\nHow to open cypress dashboard locally:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --open \r\n```\r\n\r\nHow to run cypress tests:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js\r\n```\r\n\r\nHow to run cypress tests multiple times:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --server\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --runner --times=X\r\n```\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"3bfa7c00181599541c924d36b593205fd5d9fed4","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-infra_services","v8.16.0"],"number":196560,"url":"https://github.com/elastic/kibana/pull/196560","mergeCommit":{"message":"[Inventory] Adding initial e2e structure (#196560)\n\ncloses https://github.com/elastic/kibana/issues/193992\r\n\r\nHow to open cypress dashboard locally:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --open \r\n```\r\n\r\nHow to run cypress tests:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js\r\n```\r\n\r\nHow to run cypress tests multiple times:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --server\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --runner --times=X\r\n```\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"3bfa7c00181599541c924d36b593205fd5d9fed4"}},"sourceBranch":"main","suggestedTargetBranches":["8.16"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/196560","number":196560,"mergeCommit":{"message":"[Inventory] Adding initial e2e structure (#196560)\n\ncloses https://github.com/elastic/kibana/issues/193992\r\n\r\nHow to open cypress dashboard locally:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --open \r\n```\r\n\r\nHow to run cypress tests:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js\r\n```\r\n\r\nHow to run cypress tests multiple times:\r\n```\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --server\r\nnode x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --runner --times=X\r\n```\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"3bfa7c00181599541c924d36b593205fd5d9fed4"}},{"branch":"8.16","label":"v8.16.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
1434d5aa0c
commit
0641343906
43 changed files with 2940 additions and 63470 deletions
|
@ -3,9 +3,10 @@ disabled:
|
|||
- x-pack/plugins/observability_solution/observability_onboarding/e2e/ftr_config_open.ts
|
||||
- x-pack/plugins/observability_solution/observability_onboarding/e2e/ftr_config_runner.ts
|
||||
- x-pack/plugins/observability_solution/observability_onboarding/e2e/ftr_config.ts
|
||||
- x-pack/plugins/observability_solution/apm/ftr_e2e/ftr_config_open.ts
|
||||
- x-pack/plugins/observability_solution/apm/ftr_e2e/ftr_config_run.ts
|
||||
- x-pack/plugins/observability_solution/apm/ftr_e2e/ftr_config.ts
|
||||
- x-pack/plugins/observability_solution/inventory/e2e/ftr_config_run.ts
|
||||
- x-pack/plugins/observability_solution/inventory/e2e/ftr_config.ts
|
||||
- x-pack/plugins/observability_solution/profiling/e2e/ftr_config_open.ts
|
||||
- x-pack/plugins/observability_solution/profiling/e2e/ftr_config_runner.ts
|
||||
- x-pack/plugins/observability_solution/profiling/e2e/ftr_config.ts
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
{
|
||||
"key": "cypress/security_serverless_explore",
|
||||
"name": "[Serverless] Security Solution Explore - Cypress"
|
||||
},
|
||||
},
|
||||
{
|
||||
"key": "cypress/security_solution_rule_management",
|
||||
"name": "Security Solution Rule Management - Cypress"
|
||||
|
@ -87,6 +87,10 @@
|
|||
{
|
||||
"key": "cypress/apm_cypress",
|
||||
"name": "APM - Cypress"
|
||||
},
|
||||
{
|
||||
"key": "cypress/inventory_cypress",
|
||||
"name": "Inventory - Cypress"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -80,3 +80,20 @@ steps:
|
|||
limit: 3
|
||||
- exit_status: '*'
|
||||
limit: 1
|
||||
|
||||
- command: .buildkite/scripts/steps/functional/inventory_cypress.sh
|
||||
label: 'Inventory Cypress Tests'
|
||||
agents:
|
||||
image: family/kibana-ubuntu-2004
|
||||
imageProject: elastic-images-prod
|
||||
provider: gcp
|
||||
machineType: n2-standard-4
|
||||
preemptible: true
|
||||
depends_on: build
|
||||
timeout_in_minutes: 120
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: '-1'
|
||||
limit: 3
|
||||
- exit_status: '*'
|
||||
limit: 1
|
||||
|
|
17
.buildkite/pipelines/pull_request/inventory_cypress.yml
Normal file
17
.buildkite/pipelines/pull_request/inventory_cypress.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
steps:
|
||||
- command: .buildkite/scripts/steps/functional/inventory_cypress.sh
|
||||
label: 'Inventory Cypress Tests'
|
||||
agents:
|
||||
machineType: n2-standard-4
|
||||
preemptible: true
|
||||
depends_on:
|
||||
- build
|
||||
- quick_checks
|
||||
timeout_in_minutes: 120
|
||||
parallelism: 1
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: '-1'
|
||||
limit: 3
|
||||
- exit_status: '*'
|
||||
limit: 1
|
|
@ -77,6 +77,16 @@ const getPipeline = (filename: string, removeSteps = true) => {
|
|||
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/apm_cypress.yml'));
|
||||
}
|
||||
|
||||
if (
|
||||
(await doAnyChangesMatch([
|
||||
/^x-pack\/plugins\/observability_solution\/inventory/,
|
||||
/^packages\/kbn-apm-synthtrace/,
|
||||
])) ||
|
||||
GITHUB_PR_LABELS.includes('ci:all-cypress-suites')
|
||||
) {
|
||||
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/inventory_cypress.yml'));
|
||||
}
|
||||
|
||||
if (
|
||||
(await doAnyChangesMatch([
|
||||
/^x-pack\/plugins\/observability_solution\/observability_onboarding/,
|
||||
|
|
17
.buildkite/scripts/steps/functional/inventory_cypress.sh
Normal file
17
.buildkite/scripts/steps/functional/inventory_cypress.sh
Normal file
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/common/util.sh
|
||||
|
||||
.buildkite/scripts/bootstrap.sh
|
||||
.buildkite/scripts/download_build_artifacts.sh
|
||||
|
||||
export JOB=kibana-inventory-onboarding-cypress
|
||||
|
||||
echo "--- Observability Inventory Cypress Tests"
|
||||
|
||||
cd "$XPACK_DIR"
|
||||
|
||||
node plugins/observability_solution/inventory/scripts/test/e2e.js \
|
||||
--kibana-install-dir "$KIBANA_BUILD_LOCATION" \
|
1937
.github/CODEOWNERS
vendored
Normal file
1937
.github/CODEOWNERS
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1443,6 +1443,7 @@
|
|||
"@kbn/get-repo-files": "link:packages/kbn-get-repo-files",
|
||||
"@kbn/import-locator": "link:packages/kbn-import-locator",
|
||||
"@kbn/import-resolver": "link:packages/kbn-import-resolver",
|
||||
"@kbn/inventory-e2e": "link:x-pack/plugins/observability_solution/inventory/e2e",
|
||||
"@kbn/jest-serializers": "link:packages/kbn-jest-serializers",
|
||||
"@kbn/journeys": "link:packages/kbn-journeys",
|
||||
"@kbn/json-ast": "link:packages/kbn-json-ast",
|
||||
|
|
|
@ -15,7 +15,8 @@ class ContainerEntity extends Serializable<EntityFields> {
|
|||
super({
|
||||
...fields,
|
||||
'entity.type': 'container',
|
||||
'entity.definitionId': 'latest',
|
||||
'entity.definitionId': 'builtin_containers_from_ecs_data',
|
||||
'entity.identityFields': ['container.id'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -23,21 +24,19 @@ class ContainerEntity extends Serializable<EntityFields> {
|
|||
export function containerEntity({
|
||||
agentName,
|
||||
dataStreamType,
|
||||
dataStreamDataset,
|
||||
containerId,
|
||||
entityId,
|
||||
}: {
|
||||
agentName: string[];
|
||||
dataStreamType: EntityDataStreamType[];
|
||||
dataStreamDataset: string;
|
||||
containerId: string;
|
||||
entityId: string;
|
||||
}) {
|
||||
return new ContainerEntity({
|
||||
'source_data_stream.type': dataStreamType,
|
||||
'source_data_stream.dataset': dataStreamDataset,
|
||||
'agent.name': agentName,
|
||||
'container.id': containerId,
|
||||
'entity.displayName': containerId,
|
||||
'entity.id': entityId,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@ class HostEntity extends Serializable<EntityFields> {
|
|||
super({
|
||||
...fields,
|
||||
'entity.type': 'host',
|
||||
'entity.definitionId': 'latest',
|
||||
'entity.definitionId': 'builtin_hosts_from_ecs_data',
|
||||
'entity.identityFields': ['host.name'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -23,21 +24,19 @@ class HostEntity extends Serializable<EntityFields> {
|
|||
export function hostEntity({
|
||||
agentName,
|
||||
dataStreamType,
|
||||
dataStreamDataset,
|
||||
hostName,
|
||||
entityId,
|
||||
}: {
|
||||
agentName: string[];
|
||||
dataStreamType: EntityDataStreamType[];
|
||||
dataStreamDataset: string;
|
||||
hostName: string;
|
||||
entityId: string;
|
||||
}) {
|
||||
return new HostEntity({
|
||||
'source_data_stream.type': dataStreamType,
|
||||
'source_data_stream.dataset': dataStreamDataset,
|
||||
'agent.name': agentName,
|
||||
'host.name': hostName,
|
||||
'entity.displayName': hostName,
|
||||
'entity.id': entityId,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@ class ServiceEntity extends Serializable<EntityFields> {
|
|||
super({
|
||||
...fields,
|
||||
'entity.type': 'service',
|
||||
'entity.definitionId': 'latest',
|
||||
'entity.definitionId': 'builtin_services_from_ecs_data',
|
||||
'entity.identityFields': ['service.name'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +36,7 @@ export function serviceEntity({
|
|||
}) {
|
||||
return new ServiceEntity({
|
||||
'service.name': serviceName,
|
||||
'entity.displayName': serviceName,
|
||||
'service.environment': environment,
|
||||
'source_data_stream.type': dataStreamType,
|
||||
'agent.name': agentName,
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { randomInt } from 'crypto';
|
||||
import { Fields } from '../entity';
|
||||
import { Serializable } from '../serializable';
|
||||
|
||||
|
@ -180,3 +178,12 @@ export const log = {
|
|||
create,
|
||||
createMinimal,
|
||||
};
|
||||
|
||||
function randomInt(min: number, max: number) {
|
||||
if (min > max) {
|
||||
throw new Error('Min value must be less than or equal to max value.');
|
||||
}
|
||||
|
||||
const random = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
return random;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ export { InfraSynthtraceKibanaClient } from './src/lib/infra/infra_synthtrace_ki
|
|||
export { MonitoringSynthtraceEsClient } from './src/lib/monitoring/monitoring_synthtrace_es_client';
|
||||
export { LogsSynthtraceEsClient } from './src/lib/logs/logs_synthtrace_es_client';
|
||||
export { EntitiesSynthtraceEsClient } from './src/lib/entities/entities_synthtrace_es_client';
|
||||
export { EntitiesSynthtraceKibanaClient } from './src/lib/entities/entities_synthtrace_kibana_client';
|
||||
export { SyntheticsSynthtraceEsClient } from './src/lib/synthetics/synthetics_synthtrace_es_client';
|
||||
export {
|
||||
addObserverVersionTransform,
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
import { Logger } from '../lib/utils/create_logger';
|
||||
import { ScenarioReturnType } from '../lib/utils/with_client';
|
||||
import { RunOptions } from './utils/parse_run_cli_flags';
|
||||
import { EntitiesSynthtraceKibanaClient } from '../lib/apm/client/entities_synthtrace_kibana_client';
|
||||
import { EntitiesSynthtraceKibanaClient } from '../lib/entities/entities_synthtrace_kibana_client';
|
||||
|
||||
interface EsClients {
|
||||
apmEsClient: ApmSynthtraceEsClient;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EntitiesSynthtraceKibanaClient } from '../../lib/apm/client/entities_synthtrace_kibana_client';
|
||||
import { EntitiesSynthtraceKibanaClient } from '../../lib/entities/entities_synthtrace_kibana_client';
|
||||
import { Logger } from '../../lib/utils/create_logger';
|
||||
|
||||
export function getEntitiesKibanaClient({ target, logger }: { target: string; logger: Logger }) {
|
||||
|
|
|
@ -17,6 +17,10 @@ import { Logger } from '../utils/create_logger';
|
|||
|
||||
export type EntitiesSynthtraceEsClientOptions = Omit<SynthtraceEsClientOptions, 'pipeline'>;
|
||||
|
||||
interface Pipeline {
|
||||
includeSerialization?: boolean;
|
||||
}
|
||||
|
||||
export class EntitiesSynthtraceEsClient extends SynthtraceEsClient<EntityFields> {
|
||||
constructor(options: { client: Client; logger: Logger } & EntitiesSynthtraceEsClientOptions) {
|
||||
super({
|
||||
|
@ -25,13 +29,20 @@ export class EntitiesSynthtraceEsClient extends SynthtraceEsClient<EntityFields>
|
|||
});
|
||||
this.indices = ['.entities.v1.latest.builtin*'];
|
||||
}
|
||||
|
||||
getDefaultPipeline({ includeSerialization }: Pipeline = { includeSerialization: true }) {
|
||||
return entitiesPipeline({ includeSerialization });
|
||||
}
|
||||
}
|
||||
|
||||
function entitiesPipeline() {
|
||||
function entitiesPipeline({ includeSerialization }: Pipeline = { includeSerialization: true }) {
|
||||
return (base: Readable) => {
|
||||
const serializationTransform = includeSerialization ? [getSerializeTransform()] : [];
|
||||
|
||||
return pipeline(
|
||||
// @ts-expect-error Some weird stuff here with the type definition for pipeline. We have tests!
|
||||
base,
|
||||
getSerializeTransform(),
|
||||
...serializationTransform,
|
||||
lastSeenTimestampTransform(),
|
||||
getRoutingTransform(),
|
||||
getDedotTransform(),
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
*/
|
||||
|
||||
import fetch from 'node-fetch';
|
||||
import { Logger } from '../../utils/create_logger';
|
||||
import { kibanaHeaders } from '../../shared/client_headers';
|
||||
import { getFetchAgent } from '../../../cli/utils/ssl';
|
||||
import { Logger } from '../utils/create_logger';
|
||||
import { kibanaHeaders } from '../shared/client_headers';
|
||||
import { getFetchAgent } from '../../cli/utils/ssl';
|
||||
|
||||
interface EntityDefinitionResponse {
|
||||
definitions: Array<{ type: string; state: { installed: boolean; running: boolean } }>;
|
|
@ -23,6 +23,10 @@ export const LogsCustom = 'logs@custom';
|
|||
|
||||
export type LogsSynthtraceEsClientOptions = Omit<SynthtraceEsClientOptions, 'pipeline'>;
|
||||
|
||||
interface Pipeline {
|
||||
includeSerialization?: boolean;
|
||||
}
|
||||
|
||||
export class LogsSynthtraceEsClient extends SynthtraceEsClient<LogDocument> {
|
||||
constructor(options: { client: Client; logger: Logger } & LogsSynthtraceEsClientOptions) {
|
||||
super({
|
||||
|
@ -105,13 +109,22 @@ export class LogsSynthtraceEsClient extends SynthtraceEsClient<LogDocument> {
|
|||
this.logger.error(`Custom pipeline creation failed: ${LogsCustom} - ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultPipeline({ includeSerialization }: Pipeline = { includeSerialization: true }) {
|
||||
return logsPipeline({ includeSerialization });
|
||||
}
|
||||
}
|
||||
|
||||
function logsPipeline() {
|
||||
function logsPipeline({ includeSerialization }: Pipeline = { includeSerialization: true }) {
|
||||
return (base: Readable) => {
|
||||
const serializationTransform = includeSerialization
|
||||
? [getSerializeTransform<LogDocument>()]
|
||||
: [];
|
||||
|
||||
return pipeline(
|
||||
// @ts-expect-error Some weird stuff here with the type definition for pipeline. We have tests!
|
||||
base,
|
||||
getSerializeTransform<LogDocument>(),
|
||||
...serializationTransform,
|
||||
getRoutingTransform('logs'),
|
||||
(err: unknown) => {
|
||||
if (err) {
|
||||
|
|
|
@ -1062,6 +1062,8 @@
|
|||
"@kbn/interactive-setup-test-endpoints-plugin/*": ["test/interactive_setup_api_integration/plugins/test_endpoints/*"],
|
||||
"@kbn/interpreter": ["packages/kbn-interpreter"],
|
||||
"@kbn/interpreter/*": ["packages/kbn-interpreter/*"],
|
||||
"@kbn/inventory-e2e": ["x-pack/plugins/observability_solution/inventory/e2e"],
|
||||
"@kbn/inventory-e2e/*": ["x-pack/plugins/observability_solution/inventory/e2e/*"],
|
||||
"@kbn/inventory-plugin": ["x-pack/plugins/observability_solution/inventory"],
|
||||
"@kbn/inventory-plugin/*": ["x-pack/plugins/observability_solution/inventory/*"],
|
||||
"@kbn/investigate-app-plugin": ["x-pack/plugins/observability_solution/investigate_app"],
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,21 @@
|
|||
# Inventory
|
||||
|
||||
Home of the Inventory plugin, which renders the... _inventory_.
|
||||
|
||||
# Running e2e (Cypress) tests
|
||||
|
||||
How to open cypress dashboard locally:
|
||||
```
|
||||
node x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --open
|
||||
```
|
||||
|
||||
How to run cypress tests:
|
||||
```
|
||||
node x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js
|
||||
```
|
||||
|
||||
How to run cypress tests multiple times:
|
||||
```
|
||||
node x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --server
|
||||
node x-pack/plugins/observability_solution/inventory/scripts/test/e2e.js --runner --times=X
|
||||
```
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
TBD
|
|
@ -0,0 +1,2 @@
|
|||
package_paths:
|
||||
- /packages/package-storage
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { defineCypressConfig } from '@kbn/cypress-config';
|
||||
import { setupNodeEvents } from './setup_cypress_node_events';
|
||||
|
||||
export default defineCypressConfig({
|
||||
projectId: 'omwh6f',
|
||||
fileServerFolder: './cypress',
|
||||
fixturesFolder: './cypress/fixtures',
|
||||
screenshotsFolder: './cypress/screenshots',
|
||||
videosFolder: './cypress/videos',
|
||||
requestTimeout: 10000,
|
||||
responseTimeout: 40000,
|
||||
defaultCommandTimeout: 30000,
|
||||
execTimeout: 120000,
|
||||
pageLoadTimeout: 120000,
|
||||
viewportHeight: 1800,
|
||||
viewportWidth: 1440,
|
||||
video: true,
|
||||
screenshotOnRunFailure: true,
|
||||
retries: {
|
||||
runMode: 1,
|
||||
},
|
||||
e2e: {
|
||||
setupNodeEvents,
|
||||
baseUrl: 'http://localhost:5601',
|
||||
supportFile: './cypress/support/e2e.ts',
|
||||
specPattern: './cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
|
||||
},
|
||||
});
|
2
x-pack/plugins/observability_solution/inventory/e2e/cypress/.gitignore
vendored
Normal file
2
x-pack/plugins/observability_solution/inventory/e2e/cypress/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/videos/*
|
||||
/screenshots/*
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { apm, entities, log, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { generateLongIdWithSeed } from '@kbn/apm-synthtrace-client/src/lib/utils/generate_id';
|
||||
|
||||
const SYNTH_NODE_TRACES_LOGS_ENTITY_ID = generateLongIdWithSeed('service');
|
||||
const HOST_SERVER_1_LOGS_ENTITY_ID = generateLongIdWithSeed('host');
|
||||
const CONTAINER_ID_METRICS_ENTITY_ID = generateLongIdWithSeed('container');
|
||||
|
||||
const SYNTH_NODE_TRACE_LOGS = 'synth-node-trace-logs';
|
||||
const HOST_NAME = 'server1';
|
||||
const CONTAINER_ID = 'foo';
|
||||
|
||||
const ENVIRONMENT = 'test';
|
||||
|
||||
export function generateEntities({ from, to }: { from: number; to: number }) {
|
||||
const serviceSynthNodeTracesLogs = entities.serviceEntity({
|
||||
serviceName: SYNTH_NODE_TRACE_LOGS,
|
||||
agentName: ['nodejs'],
|
||||
dataStreamType: ['traces', 'logs'],
|
||||
environment: ENVIRONMENT,
|
||||
entityId: SYNTH_NODE_TRACES_LOGS_ENTITY_ID,
|
||||
});
|
||||
|
||||
const hostServer1Logs = entities.hostEntity({
|
||||
hostName: HOST_NAME,
|
||||
agentName: ['nodejs'],
|
||||
dataStreamType: ['logs'],
|
||||
entityId: HOST_SERVER_1_LOGS_ENTITY_ID,
|
||||
});
|
||||
|
||||
const containerMetrics = entities.containerEntity({
|
||||
containerId: CONTAINER_ID,
|
||||
agentName: ['filebeat'],
|
||||
dataStreamType: ['metrics'],
|
||||
entityId: CONTAINER_ID_METRICS_ENTITY_ID,
|
||||
});
|
||||
|
||||
const range = timerange(from, to);
|
||||
|
||||
return range
|
||||
.interval('1m')
|
||||
.rate(1)
|
||||
.generator((timestamp) => {
|
||||
return [
|
||||
serviceSynthNodeTracesLogs.timestamp(timestamp),
|
||||
hostServer1Logs.timestamp(timestamp),
|
||||
containerMetrics.timestamp(timestamp),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
export function generateTraces({ from, to }: { from: number; to: number }) {
|
||||
const synthNodeTraceLogs = apm
|
||||
.service({
|
||||
name: SYNTH_NODE_TRACE_LOGS,
|
||||
environment: ENVIRONMENT,
|
||||
agentName: 'nodejs',
|
||||
})
|
||||
.instance('instance_1');
|
||||
|
||||
const range = timerange(from, to);
|
||||
return range
|
||||
.interval('1m')
|
||||
.rate(1)
|
||||
.generator((timestamp) => {
|
||||
return [
|
||||
synthNodeTraceLogs
|
||||
.transaction({ transactionName: 't1' })
|
||||
.timestamp(timestamp)
|
||||
.duration(1000)
|
||||
.success(),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
const MESSAGE_LOG_LEVELS = [
|
||||
{ message: 'A simple log', level: 'info' },
|
||||
{ message: 'Yet another debug log', level: 'debug' },
|
||||
{ message: 'Error with certificate: "ca_trusted_fingerprint"', level: 'error' },
|
||||
];
|
||||
export function generateLogs({ from, to }: { from: number; to: number }) {
|
||||
const range = timerange(from, to);
|
||||
return range
|
||||
.interval('1m')
|
||||
.rate(1)
|
||||
.generator((timestamp) => {
|
||||
return Array(3)
|
||||
.fill(0)
|
||||
.map(() => {
|
||||
const index = Math.floor(Math.random() * 3);
|
||||
const logMessage = MESSAGE_LOG_LEVELS[index];
|
||||
|
||||
return log
|
||||
.create({ isLogsDb: false })
|
||||
.service(SYNTH_NODE_TRACE_LOGS)
|
||||
.message(logMessage.message)
|
||||
.logLevel(logMessage.level)
|
||||
.setGeoLocation([1])
|
||||
.setHostIp('223.72.43.22')
|
||||
.defaults({
|
||||
'agent.name': 'nodejs',
|
||||
})
|
||||
.timestamp(timestamp);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { apmSynthtrace, entitiesSynthtrace, logsSynthtrace } from '../../synthtrace';
|
||||
import { generateEntities, generateLogs, generateTraces } from './generate_data';
|
||||
|
||||
const start = '2024-10-16T00:00:00.000Z';
|
||||
const end = '2024-10-16T00:15:00.000Z';
|
||||
|
||||
describe('Home page', () => {
|
||||
beforeEach(() => {
|
||||
cy.loginAsSuperUser();
|
||||
});
|
||||
|
||||
describe('When EEM is disabled', () => {
|
||||
it('Shows no data screen', () => {
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.contains('See everything you have in one place');
|
||||
cy.getByTestSubj('inventoryInventoryPageTemplateFilledButton').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When EEM is enabled', () => {
|
||||
describe('When there is no entities', () => {
|
||||
it('Shows inventory page with empty message', () => {
|
||||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.contains('Inventory');
|
||||
cy.contains('Tell us what you think!');
|
||||
cy.contains('Trying for the first time?');
|
||||
cy.contains('No entities available');
|
||||
cy.getByTestSubj('addDataButton').should('exist');
|
||||
cy.getByTestSubj('associateServiceLogsButton').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When there is entities and signal data', () => {
|
||||
before(() => {
|
||||
entitiesSynthtrace.index(
|
||||
generateEntities({ from: new Date(start).getTime(), to: new Date(end).getTime() })
|
||||
);
|
||||
apmSynthtrace.index(
|
||||
generateTraces({ from: new Date(start).getTime(), to: new Date(end).getTime() })
|
||||
);
|
||||
logsSynthtrace.index(
|
||||
generateLogs({ from: new Date(start).getTime(), to: new Date(end).getTime() })
|
||||
);
|
||||
});
|
||||
after(() => {
|
||||
entitiesSynthtrace.clean();
|
||||
apmSynthtrace.clean();
|
||||
logsSynthtrace.clean();
|
||||
});
|
||||
|
||||
it('Shows inventory page with entities', () => {
|
||||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.contains('server1');
|
||||
cy.contains('Host');
|
||||
cy.contains('synth-node-trace-logs');
|
||||
cy.contains('Service');
|
||||
cy.contains('foo');
|
||||
cy.contains('Container');
|
||||
});
|
||||
|
||||
it('Navigates to apm when clicking on a service type entity', () => {
|
||||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.contains('synth-node-trace-logs').click();
|
||||
cy.url().should('include', '/app/apm/services/synth-node-trace-logs/overview');
|
||||
});
|
||||
|
||||
it('Navigates to hosts when clicking on a host type entity', () => {
|
||||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.contains('server1').click();
|
||||
cy.url().should('include', '/app/metrics/detail/host/server1');
|
||||
});
|
||||
|
||||
it('Navigates to infra when clicking on a container type entity', () => {
|
||||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.contains('foo').click();
|
||||
cy.url().should('include', '/app/metrics/detail/container/foo');
|
||||
});
|
||||
|
||||
it('Filters entities by service type', () => {
|
||||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.intercept('GET', '/internal/inventory/entities*').as('getEntitites');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.getByTestSubj('entityTypesFilterComboBox')
|
||||
.click()
|
||||
.getByTestSubj('entityTypesFilterserviceOption')
|
||||
.click();
|
||||
cy.wait('@getEntitites');
|
||||
cy.get('server1').should('not.exist');
|
||||
cy.contains('synth-node-trace-logs');
|
||||
cy.get('foo').should('not.exist');
|
||||
});
|
||||
|
||||
it('Filters entities by host type', () => {
|
||||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.intercept('GET', '/internal/inventory/entities*').as('getEntitites');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.getByTestSubj('entityTypesFilterComboBox')
|
||||
.click()
|
||||
.getByTestSubj('entityTypesFilterhostOption')
|
||||
.click();
|
||||
cy.wait('@getEntitites');
|
||||
cy.contains('server1');
|
||||
cy.get('synth-node-trace-logs').should('not.exist');
|
||||
cy.get('foo').should('not.exist');
|
||||
});
|
||||
|
||||
it('Filters entities by container type', () => {
|
||||
cy.intercept('GET', '/internal/entities/managed/enablement', {
|
||||
fixture: 'eem_enabled.json',
|
||||
}).as('getEEMStatus');
|
||||
cy.intercept('GET', '/internal/inventory/entities*').as('getEntitites');
|
||||
cy.visitKibana('/app/inventory');
|
||||
cy.wait('@getEEMStatus');
|
||||
cy.getByTestSubj('entityTypesFilterComboBox')
|
||||
.click()
|
||||
.getByTestSubj('entityTypesFiltercontainerOption')
|
||||
.click();
|
||||
cy.wait('@getEntitites');
|
||||
cy.get('server1').should('not.exist');
|
||||
cy.get('synth-node-trace-logs').should('not.exist');
|
||||
cy.contains('foo');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"enabled": true
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import '@frsource/cypress-plugin-visual-regression-diff';
|
||||
import 'cypress-axe';
|
||||
import 'cypress-real-events/support';
|
||||
|
||||
Cypress.Commands.add('getByTestSubj', (selector: string) => {
|
||||
return cy.get(`[data-test-subj="${selector}"]`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('visitKibana', (url: string) => {
|
||||
cy.visit(url);
|
||||
cy.getByTestSubj('kbnLoadingMessage').should('exist');
|
||||
cy.getByTestSubj('kbnLoadingMessage').should('not.exist', {
|
||||
timeout: 50000,
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('loginAsSuperUser', () => {
|
||||
return cy.loginAs({ username: 'elastic', password: 'changeme' });
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
'loginAs',
|
||||
({ username, password }: { username: string; password: string }) => {
|
||||
const kibanaUrl = Cypress.env('KIBANA_URL');
|
||||
cy.log(`Logging in as ${username} on ${kibanaUrl}`);
|
||||
cy.visit('/');
|
||||
cy.request({
|
||||
log: true,
|
||||
method: 'POST',
|
||||
url: `${kibanaUrl}/internal/security/login`,
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: 'basic',
|
||||
currentURL: `${kibanaUrl}/login`,
|
||||
params: { username, password },
|
||||
},
|
||||
headers: {
|
||||
'kbn-xsrf': 'e2e_test',
|
||||
},
|
||||
});
|
||||
cy.visit('/');
|
||||
}
|
||||
);
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
Cypress.on('uncaught:exception', (err, runnable) => {
|
||||
return false;
|
||||
});
|
||||
|
||||
import './commands';
|
18
x-pack/plugins/observability_solution/inventory/e2e/cypress/support/types.d.ts
vendored
Normal file
18
x-pack/plugins/observability_solution/inventory/e2e/cypress/support/types.d.ts
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
loginAsSuperUser(): Cypress.Chainable<Cypress.Response<any>>;
|
||||
loginAs(params: {
|
||||
username: string;
|
||||
password: string;
|
||||
}): Cypress.Chainable<Cypress.Response<any>>;
|
||||
getByTestSubj(selector: string): Chainable<JQuery<Element>>;
|
||||
visitKibana(url: string): void;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
EntitiesSynthtraceKibanaClient,
|
||||
createLogger,
|
||||
LogLevel,
|
||||
ApmSynthtraceKibanaClient,
|
||||
} from '@kbn/apm-synthtrace';
|
||||
import cypress from 'cypress';
|
||||
import path from 'path';
|
||||
import Url from 'url';
|
||||
import { FtrProviderContext } from './ftr_provider_context';
|
||||
|
||||
export async function cypressTestRunner({ getService }: FtrProviderContext) {
|
||||
const config = getService('config');
|
||||
|
||||
const username = config.get('servers.elasticsearch.username');
|
||||
const password = config.get('servers.elasticsearch.password');
|
||||
|
||||
const kibanaUrl = Url.format({
|
||||
protocol: config.get('servers.kibana.protocol'),
|
||||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
auth: `${username}:${password}`,
|
||||
});
|
||||
|
||||
const esNode = Url.format({
|
||||
protocol: config.get('servers.elasticsearch.protocol'),
|
||||
port: config.get('servers.elasticsearch.port'),
|
||||
hostname: config.get('servers.elasticsearch.hostname'),
|
||||
auth: `${username}:${password}`,
|
||||
});
|
||||
|
||||
const esRequestTimeout = config.get('timeouts.esRequestTimeout');
|
||||
const apmKibanaClient = new ApmSynthtraceKibanaClient({
|
||||
logger: createLogger(LogLevel.info),
|
||||
target: kibanaUrl,
|
||||
});
|
||||
|
||||
const packageVersion = await apmKibanaClient.fetchLatestApmPackageVersion();
|
||||
|
||||
const entitiesKibanaClient = new EntitiesSynthtraceKibanaClient({
|
||||
logger: createLogger(LogLevel.info),
|
||||
target: kibanaUrl,
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
apmKibanaClient.installApmPackage(packageVersion),
|
||||
entitiesKibanaClient.installEntityIndexPatterns(),
|
||||
]);
|
||||
|
||||
const kibanaUrlWithoutAuth = Url.format({
|
||||
protocol: config.get('servers.kibana.protocol'),
|
||||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
});
|
||||
|
||||
const cypressProjectPath = path.join(__dirname);
|
||||
const { open, ...cypressCliArgs } = getCypressCliArgs();
|
||||
const cypressExecution = open ? cypress.open : cypress.run;
|
||||
const res = await cypressExecution({
|
||||
...cypressCliArgs,
|
||||
project: cypressProjectPath,
|
||||
browser: 'electron',
|
||||
config: {
|
||||
e2e: {
|
||||
baseUrl: kibanaUrlWithoutAuth,
|
||||
},
|
||||
},
|
||||
env: {
|
||||
KIBANA_URL: kibanaUrlWithoutAuth,
|
||||
APM_PACKAGE_VERSION: packageVersion,
|
||||
ES_NODE: esNode,
|
||||
ES_REQUEST_TIMEOUT: esRequestTimeout,
|
||||
TEST_CLOUD: process.env.TEST_CLOUD,
|
||||
},
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function getCypressCliArgs(): Record<string, unknown> {
|
||||
if (!process.env.CYPRESS_CLI_ARGS) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { $0, _, ...cypressCliArgs } = JSON.parse(process.env.CYPRESS_CLI_ARGS) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
|
||||
const spec =
|
||||
typeof cypressCliArgs.spec === 'string' && !cypressCliArgs.spec.includes('**')
|
||||
? `**/${cypressCliArgs.spec}*`
|
||||
: cypressCliArgs.spec;
|
||||
|
||||
return { ...cypressCliArgs, spec };
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
import { CA_CERT_PATH } from '@kbn/dev-utils';
|
||||
import { commonFunctionalServices } from '@kbn/ftr-common-functional-services';
|
||||
import { commonFunctionalUIServices } from '@kbn/ftr-common-functional-ui-services';
|
||||
import { cypressTestRunner } from './cypress_test_runner';
|
||||
import { FtrProviderContext } from './ftr_provider_context';
|
||||
|
||||
async function ftrConfig({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const kibanaCommonTestsConfig = await readConfigFile(
|
||||
require.resolve('@kbn/test-suites-src/common/config')
|
||||
);
|
||||
const xpackFunctionalTestsConfig = await readConfigFile(
|
||||
require.resolve('@kbn/test-suites-xpack/functional/config.base')
|
||||
);
|
||||
|
||||
return {
|
||||
...kibanaCommonTestsConfig.getAll(),
|
||||
|
||||
services: {
|
||||
...commonFunctionalServices,
|
||||
...commonFunctionalUIServices,
|
||||
},
|
||||
|
||||
esTestCluster: {
|
||||
...xpackFunctionalTestsConfig.get('esTestCluster'),
|
||||
serverArgs: [
|
||||
...xpackFunctionalTestsConfig.get('esTestCluster.serverArgs'),
|
||||
// define custom es server here
|
||||
],
|
||||
},
|
||||
|
||||
kbnTestServer: {
|
||||
...xpackFunctionalTestsConfig.get('kbnTestServer'),
|
||||
serverArgs: [
|
||||
...xpackFunctionalTestsConfig.get('kbnTestServer.serverArgs'),
|
||||
'--home.disableWelcomeScreen=true',
|
||||
'--csp.strict=false',
|
||||
'--csp.warnLegacyBrowsers=false',
|
||||
// define custom kibana server args here
|
||||
`--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`,
|
||||
],
|
||||
},
|
||||
testRunner: async (ftrProviderContext: FtrProviderContext) => {
|
||||
const result = await cypressTestRunner(ftrProviderContext);
|
||||
|
||||
// set exit code explicitly if at least one Cypress test fails
|
||||
if (
|
||||
result &&
|
||||
((result as CypressCommandLine.CypressFailedRunResult)?.status === 'failed' ||
|
||||
(result as CypressCommandLine.CypressRunResult)?.totalFailed)
|
||||
) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ftrConfig;
|
10
x-pack/plugins/observability_solution/inventory/e2e/ftr_provider_context.d.ts
vendored
Normal file
10
x-pack/plugins/observability_solution/inventory/e2e/ftr_provider_context.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { GenericFtrProviderContext } from '@kbn/test';
|
||||
|
||||
export type FtrProviderContext = GenericFtrProviderContext<{}, {}>;
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type": "test-helper",
|
||||
"id": "@kbn/inventory-e2e",
|
||||
"owner": "@elastic/obs-ux-infra_services-team",
|
||||
"devOnly": true
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
ApmSynthtraceEsClient,
|
||||
EntitiesSynthtraceEsClient,
|
||||
LogLevel,
|
||||
LogsSynthtraceEsClient,
|
||||
createLogger,
|
||||
} from '@kbn/apm-synthtrace';
|
||||
import { createEsClientForTesting } from '@kbn/test';
|
||||
// eslint-disable-next-line @kbn/imports/no_unresolvable_imports
|
||||
import { initPlugin } from '@frsource/cypress-plugin-visual-regression-diff/plugins';
|
||||
import del from 'del';
|
||||
import { some } from 'lodash';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
export function setupNodeEvents(on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) {
|
||||
const logger = createLogger(LogLevel.info);
|
||||
|
||||
const client = createEsClientForTesting({
|
||||
esUrl: config.env.ES_NODE,
|
||||
requestTimeout: config.env.ES_REQUEST_TIMEOUT,
|
||||
isCloud: !!config.env.TEST_CLOUD,
|
||||
});
|
||||
|
||||
const entitiesSynthtraceEsClient = new EntitiesSynthtraceEsClient({
|
||||
client,
|
||||
logger,
|
||||
refreshAfterIndex: true,
|
||||
});
|
||||
|
||||
const apmSynthtraceEsClient = new ApmSynthtraceEsClient({
|
||||
client,
|
||||
logger,
|
||||
refreshAfterIndex: true,
|
||||
version: config.env.APM_PACKAGE_VERSION,
|
||||
});
|
||||
|
||||
const logsSynthtraceEsClient = new LogsSynthtraceEsClient({
|
||||
client,
|
||||
logger,
|
||||
refreshAfterIndex: true,
|
||||
});
|
||||
|
||||
entitiesSynthtraceEsClient.pipeline(
|
||||
entitiesSynthtraceEsClient.getDefaultPipeline({ includeSerialization: false })
|
||||
);
|
||||
|
||||
apmSynthtraceEsClient.pipeline(
|
||||
apmSynthtraceEsClient.getDefaultPipeline({ includeSerialization: false })
|
||||
);
|
||||
|
||||
logsSynthtraceEsClient.pipeline(
|
||||
logsSynthtraceEsClient.getDefaultPipeline({ includeSerialization: false })
|
||||
);
|
||||
|
||||
initPlugin(on, config);
|
||||
|
||||
on('task', {
|
||||
// send logs to node process
|
||||
log(message) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(message);
|
||||
return null;
|
||||
},
|
||||
|
||||
async 'entitiesSynthtrace:index'(events: Array<Record<string, any>>) {
|
||||
await entitiesSynthtraceEsClient.index(Readable.from(events));
|
||||
return null;
|
||||
},
|
||||
|
||||
async 'entitiesSynthtrace:clean'() {
|
||||
await entitiesSynthtraceEsClient.clean();
|
||||
return null;
|
||||
},
|
||||
|
||||
async 'apmSynthtrace:index'(events: Array<Record<string, any>>) {
|
||||
await apmSynthtraceEsClient.index(Readable.from(events));
|
||||
return null;
|
||||
},
|
||||
async 'apmSynthtrace:clean'() {
|
||||
await apmSynthtraceEsClient.clean();
|
||||
return null;
|
||||
},
|
||||
async 'logsSynthtrace:index'(events: Array<Record<string, any>>) {
|
||||
await logsSynthtraceEsClient.index(Readable.from(events));
|
||||
return null;
|
||||
},
|
||||
async 'logsSynthtrace:clean'() {
|
||||
await logsSynthtraceEsClient.clean();
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
on('after:spec', (spec, results) => {
|
||||
// Delete videos that have no failures or retries
|
||||
if (results && results.video) {
|
||||
const failures = some(results.tests, (test) => {
|
||||
return some(test.attempts, { state: 'failed' });
|
||||
});
|
||||
if (!failures) {
|
||||
del(results.video);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
on('before:browser:launch', (browser, launchOptions) => {
|
||||
if (browser.name === 'electron' && browser.isHeadless) {
|
||||
launchOptions.preferences.width = 1440;
|
||||
launchOptions.preferences.height = 1600;
|
||||
}
|
||||
return launchOptions;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type {
|
||||
Serializable,
|
||||
SynthtraceGenerator,
|
||||
EntityFields,
|
||||
ApmFields,
|
||||
} from '@kbn/apm-synthtrace-client';
|
||||
|
||||
export const entitiesSynthtrace = {
|
||||
index: (events: SynthtraceGenerator<EntityFields> | Array<Serializable<EntityFields>>) =>
|
||||
cy.task(
|
||||
'entitiesSynthtrace:index',
|
||||
Array.from(events).flatMap((event) => event.serialize())
|
||||
),
|
||||
clean: () => cy.task('entitiesSynthtrace:clean'),
|
||||
};
|
||||
|
||||
export const apmSynthtrace = {
|
||||
index: (events: SynthtraceGenerator<ApmFields> | Array<Serializable<ApmFields>>) =>
|
||||
cy.task(
|
||||
'apmSynthtrace:index',
|
||||
Array.from(events).flatMap((event) => event.serialize())
|
||||
),
|
||||
clean: () => cy.task('apmSynthtrace:clean'),
|
||||
};
|
||||
|
||||
export const logsSynthtrace = {
|
||||
index: (events: SynthtraceGenerator<ApmFields> | Array<Serializable<ApmFields>>) =>
|
||||
cy.task(
|
||||
'logsSynthtrace:index',
|
||||
Array.from(events).flatMap((event) => event.serialize())
|
||||
),
|
||||
clean: () => cy.task('logsSynthtrace:clean'),
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"include": ["**/*"],
|
||||
"exclude": ["tmp", "target/**/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": ["cypress", "node", "cypress-real-events"],
|
||||
"isolatedModules": false
|
||||
},
|
||||
"kbn_references": [
|
||||
"@kbn/test",
|
||||
"@kbn/apm-synthtrace",
|
||||
"@kbn/apm-synthtrace-client",
|
||||
"@kbn/dev-utils",
|
||||
"@kbn/cypress-config",
|
||||
"@kbn/ftr-common-functional-services",
|
||||
"@kbn/ftr-common-functional-ui-services"
|
||||
]
|
||||
}
|
|
@ -21,6 +21,7 @@ interface Props {
|
|||
const toComboBoxOption = (entityType: EntityType): EuiComboBoxOptionOption<EntityType> => ({
|
||||
key: entityType,
|
||||
label: getEntityTypeLabel(entityType),
|
||||
'data-test-subj': `entityTypesFilter${entityType}Option`,
|
||||
});
|
||||
|
||||
export function EntityTypesControls({ onChange }: Props) {
|
||||
|
@ -44,6 +45,7 @@ export function EntityTypesControls({ onChange }: Props) {
|
|||
|
||||
return (
|
||||
<EuiComboBox<EntityType>
|
||||
data-test-subj="entityTypesFilterComboBox"
|
||||
isLoading={loading}
|
||||
css={css`
|
||||
max-width: 325px;
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
const { times } = require('lodash');
|
||||
const path = require('path');
|
||||
const yargs = require('yargs');
|
||||
const childProcess = require('child_process');
|
||||
const { REPO_ROOT } = require('@kbn/repo-info');
|
||||
|
||||
const { argv } = yargs(process.argv.slice(2))
|
||||
.parserConfiguration({ 'unknown-options-as-args': true })
|
||||
.option('kibana-install-dir', {
|
||||
default: '',
|
||||
type: 'string',
|
||||
description: 'Path to the Kibana install directory',
|
||||
})
|
||||
.option('server', {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
description: 'Start Elasticsearch and Kibana',
|
||||
})
|
||||
.option('runner', {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Run all tests (an instance of Elasticsearch and kibana are needs to be available)',
|
||||
})
|
||||
.option('times', {
|
||||
type: 'number',
|
||||
description: 'Repeat the test n number of times',
|
||||
})
|
||||
.option('bail', {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
description: 'stop tests after the first failure',
|
||||
})
|
||||
.help();
|
||||
|
||||
const e2eDir = path.join(__dirname, '../../e2e');
|
||||
|
||||
let ftrScript = 'functional_tests.js';
|
||||
if (argv.server) {
|
||||
ftrScript = 'functional_tests_server.js';
|
||||
} else if (argv.runner) {
|
||||
ftrScript = 'functional_test_runner.js';
|
||||
}
|
||||
|
||||
const cypressCliArgs = yargs(argv._).parserConfiguration({
|
||||
'boolean-negation': false,
|
||||
}).argv;
|
||||
|
||||
if (cypressCliArgs.grep) {
|
||||
throw new Error('--grep is not supported. Please use --spec instead');
|
||||
}
|
||||
|
||||
const spawnArgs = [
|
||||
`${REPO_ROOT}/scripts/${ftrScript}`,
|
||||
`--config=./ftr_config.ts`,
|
||||
`--kibana-install-dir=${argv.kibanaInstallDir}`,
|
||||
...(argv.bail ? [`--bail`] : []),
|
||||
];
|
||||
|
||||
function runTests() {
|
||||
console.log(`Running e2e tests: "node ${spawnArgs.join(' ')}"`);
|
||||
|
||||
return childProcess.spawnSync('node', spawnArgs, {
|
||||
cwd: e2eDir,
|
||||
env: {
|
||||
...process.env,
|
||||
CYPRESS_CLI_ARGS: JSON.stringify(cypressCliArgs),
|
||||
NODE_OPTIONS: '--openssl-legacy-provider',
|
||||
},
|
||||
encoding: 'utf8',
|
||||
stdio: 'inherit',
|
||||
});
|
||||
}
|
||||
|
||||
const runCounter = { succeeded: 0, failed: 0, remaining: argv.times };
|
||||
let exitStatus = 0;
|
||||
times(argv.times ?? 1, () => {
|
||||
const child = runTests();
|
||||
if (child.status === 0) {
|
||||
runCounter.succeeded++;
|
||||
} else {
|
||||
exitStatus = child.status;
|
||||
runCounter.failed++;
|
||||
}
|
||||
|
||||
runCounter.remaining--;
|
||||
|
||||
if (argv.times > 1) {
|
||||
console.log(runCounter);
|
||||
}
|
||||
});
|
||||
|
||||
process.exitCode = exitStatus;
|
||||
console.log(`Quitting with exit code ${exitStatus}`);
|
|
@ -5419,6 +5419,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/inventory-e2e@link:x-pack/plugins/observability_solution/inventory/e2e":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/inventory-plugin@link:x-pack/plugins/observability_solution/inventory":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue