mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[User Experience] Synthetics e2e tests. (#132896)
Co-authored-by: shahzad31 <shahzad.muhammad@elastic.co>
This commit is contained in:
parent
ec4d9abbc1
commit
eab8ece3fb
29 changed files with 17732 additions and 152 deletions
|
@ -39,7 +39,8 @@ disabled:
|
|||
|
||||
# Elastic Synthetics configs
|
||||
- x-pack/plugins/synthetics/e2e/config.ts
|
||||
- x-pack/plugins/synthetics/e2e/playwright_run.ts
|
||||
- x-pack/plugins/synthetics/e2e/synthetics_run.ts
|
||||
- x-pack/plugins/ux/e2e/synthetics_run.ts
|
||||
|
||||
# Configs that exist but weren't running in CI when this file was introduced
|
||||
- test/visual_regression/config.ts
|
||||
|
|
11
.buildkite/pipelines/pull_request/ux_plugin_e2e.yml
Normal file
11
.buildkite/pipelines/pull_request/ux_plugin_e2e.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
steps:
|
||||
- command: .buildkite/scripts/steps/functional/ux_synthetics_e2e.sh
|
||||
label: 'UX Plugin @elastic/synthetics Tests'
|
||||
agents:
|
||||
queue: ci-group-6
|
||||
depends_on: build
|
||||
timeout_in_minutes: 120
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: '*'
|
||||
limit: 1
|
|
@ -108,6 +108,10 @@ const uploadPipeline = (pipelineContent) => {
|
|||
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/synthetics_plugin.yml'));
|
||||
}
|
||||
|
||||
if (await doAnyChangesMatch([/^x-pack\/plugins\/ux/])) {
|
||||
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/ux_plugin_e2e.yml'));
|
||||
}
|
||||
|
||||
if (process.env.GITHUB_PR_LABELS.includes('ci:deploy-cloud')) {
|
||||
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/deploy_cloud.yml'));
|
||||
}
|
||||
|
|
17
.buildkite/scripts/steps/functional/ux_synthetics_e2e.sh
Executable file
17
.buildkite/scripts/steps/functional/ux_synthetics_e2e.sh
Executable 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-ux-plugin-synthetics
|
||||
|
||||
echo "--- User Experience @elastic/synthetics Tests"
|
||||
|
||||
cd "$XPACK_DIR"
|
||||
|
||||
checks-reporter-with-killswitch "User Experience plugin @elastic/synthetics Tests" \
|
||||
node plugins/ux/scripts/e2e.js --kibana-install-dir "$KIBANA_BUILD_LOCATION" ${GREP:+--grep \"${GREP}\"}
|
|
@ -77,6 +77,7 @@ it('produces the right watch and ignore list', () => {
|
|||
<absolute path>/x-pack/plugins/security_solution/scripts,
|
||||
<absolute path>/x-pack/plugins/security_solution/server/lib/detection_engine/scripts,
|
||||
<absolute path>/x-pack/plugins/synthetics/e2e,
|
||||
<absolute path>/x-pack/plugins/ux/e2e,
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -67,6 +67,7 @@ export function getServerWatchPaths({ pluginPaths, pluginScanDirs }: Options) {
|
|||
fromRoot('x-pack/plugins/security_solution/scripts'),
|
||||
fromRoot('x-pack/plugins/security_solution/server/lib/detection_engine/scripts'),
|
||||
fromRoot('x-pack/plugins/synthetics/e2e'),
|
||||
fromRoot('x-pack/plugins/ux/e2e'),
|
||||
];
|
||||
|
||||
return {
|
||||
|
|
|
@ -70,6 +70,11 @@ export const PROJECTS = [
|
|||
disableTypeCheck: true,
|
||||
}),
|
||||
|
||||
createProject('x-pack/plugins/ux/e2e/tsconfig.json', {
|
||||
name: 'ux/synthetics-e2e-tests',
|
||||
disableTypeCheck: true,
|
||||
}),
|
||||
|
||||
// Glob patterns to be all search at once
|
||||
...findProjects([
|
||||
'src/plugins/*/tsconfig.json',
|
||||
|
|
12
test/scripts/jenkins_ux_synthetics.sh
Executable file
12
test/scripts/jenkins_ux_synthetics.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source test/scripts/jenkins_test_setup_xpack.sh
|
||||
|
||||
echo " -> Running User Experience plugin @elastic/synthetics tests"
|
||||
cd "$XPACK_DIR"
|
||||
|
||||
checks-reporter-with-killswitch "User Experience plugin @elastic/synthetics Tests" \
|
||||
node plugins/ux/scripts/e2e.js
|
||||
|
||||
echo ""
|
||||
echo ""
|
|
@ -165,6 +165,15 @@ def functionalXpack(Map params = [:]) {
|
|||
}
|
||||
}
|
||||
|
||||
whenChanged([
|
||||
'x-pack/plugins/ux/',
|
||||
]) {
|
||||
if (githubPr.isPr()) {
|
||||
task(kibanaPipeline.functionalTestProcess('xpack-uxPluginSynthetics', './test/scripts/jenkins_ux_synthetics.sh'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
whenChanged([
|
||||
'x-pack/plugins/fleet/',
|
||||
]) {
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
import yargs from 'yargs';
|
||||
import { playwrightRunTests } from './playwright_start';
|
||||
|
||||
const { argv } = yargs(process.argv.slice(2))
|
||||
.option('headless', {
|
||||
|
@ -14,6 +13,11 @@ const { argv } = yargs(process.argv.slice(2))
|
|||
type: 'boolean',
|
||||
description: 'Start in headless mode',
|
||||
})
|
||||
.option('pauseOnError', {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
description: 'Pause on error',
|
||||
})
|
||||
.option('grep', {
|
||||
default: undefined,
|
||||
type: 'string',
|
||||
|
@ -21,15 +25,4 @@ const { argv } = yargs(process.argv.slice(2))
|
|||
})
|
||||
.help();
|
||||
|
||||
const { headless, grep } = argv;
|
||||
|
||||
async function runE2ETests({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const kibanaConfig = await readConfigFile(require.resolve('./config.ts'));
|
||||
return {
|
||||
...kibanaConfig.getAll(),
|
||||
testRunner: playwrightRunTests({ headless, match: grep }),
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default runE2ETests;
|
||||
export { argv };
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import Url from 'url';
|
||||
import { run as playwrightRun } from '@elastic/synthetics';
|
||||
import { createApmUsers } from '@kbn/apm-plugin/scripts/create_apm_users/create_apm_users';
|
||||
import { esArchiverLoad, esArchiverUnload } from './tasks/es_archiver';
|
||||
|
||||
import './journeys';
|
||||
|
||||
export function playwrightRunTests({ headless, match }: { headless: boolean; match?: string }) {
|
||||
return async ({ getService }: any) => {
|
||||
const results = await playwrightStart(getService, headless, match);
|
||||
|
||||
Object.entries(results).forEach(([_journey, result]) => {
|
||||
if (result.status !== 'succeeded') {
|
||||
throw new Error('Tests failed');
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async function playwrightStart(getService: any, headless = true, match?: string) {
|
||||
console.log('Loading esArchiver...');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
esArchiverLoad('full_heartbeat');
|
||||
esArchiverLoad('browser');
|
||||
|
||||
const config = getService('config');
|
||||
|
||||
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');
|
||||
|
||||
const kibanaUrl = Url.format({
|
||||
protocol: config.get('servers.kibana.protocol'),
|
||||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
});
|
||||
|
||||
await createApmUsers({
|
||||
elasticsearch: { username: 'elastic', password: 'changeme' },
|
||||
kibana: { hostname: kibanaUrl },
|
||||
});
|
||||
|
||||
const res = await playwrightRun({
|
||||
params: { kibanaUrl, getService },
|
||||
playwrightOptions: { headless, chromiumSandbox: false, timeout: 60 * 1000 },
|
||||
match: match === 'undefined' ? '' : match,
|
||||
});
|
||||
|
||||
console.log('Removing esArchiver...');
|
||||
esArchiverUnload('full_heartbeat');
|
||||
esArchiverUnload('browser');
|
||||
|
||||
return res;
|
||||
}
|
41
x-pack/plugins/synthetics/e2e/synthetics_run.ts
Normal file
41
x-pack/plugins/synthetics/e2e/synthetics_run.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 path from 'path';
|
||||
import { SyntheticsRunner } from './synthetics_start';
|
||||
|
||||
import { argv } from './parse_args_params';
|
||||
|
||||
const { headless, grep, pauseOnError } = argv;
|
||||
|
||||
async function runE2ETests({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const kibanaConfig = await readConfigFile(require.resolve('./config.ts'));
|
||||
return {
|
||||
...kibanaConfig.getAll(),
|
||||
testRunner: async ({ getService }: any) => {
|
||||
const syntheticsRunner = new SyntheticsRunner(getService, {
|
||||
headless,
|
||||
match: grep,
|
||||
pauseOnError,
|
||||
});
|
||||
|
||||
await syntheticsRunner.setup();
|
||||
const fixturesDir = path.join(__dirname, '../e2e/fixtures/es_archiver/');
|
||||
|
||||
await syntheticsRunner.loadTestData(fixturesDir, ['full_heartbeat', 'browser']);
|
||||
|
||||
await syntheticsRunner.loadTestFiles(async () => {
|
||||
require('./journeys');
|
||||
});
|
||||
|
||||
await syntheticsRunner.run();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default runE2ETests;
|
106
x-pack/plugins/synthetics/e2e/synthetics_start.ts
Normal file
106
x-pack/plugins/synthetics/e2e/synthetics_start.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
||||
import Url from 'url';
|
||||
import { run as syntheticsRun } from '@elastic/synthetics';
|
||||
import { PromiseType } from 'utility-types';
|
||||
import { createApmUsers } from '@kbn/apm-plugin/scripts/create_apm_users/create_apm_users';
|
||||
|
||||
import { esArchiverUnload } from './tasks/es_archiver';
|
||||
|
||||
export interface ArgParams {
|
||||
headless: boolean;
|
||||
match?: string;
|
||||
pauseOnError: boolean;
|
||||
}
|
||||
|
||||
export class SyntheticsRunner {
|
||||
public getService: any;
|
||||
public kibanaUrl: string;
|
||||
|
||||
public testFilesLoaded: boolean = false;
|
||||
|
||||
public params: ArgParams;
|
||||
|
||||
constructor(getService: any, params: ArgParams) {
|
||||
this.getService = getService;
|
||||
this.kibanaUrl = this.getKibanaUrl();
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
async setup() {
|
||||
await this.createTestUsers();
|
||||
}
|
||||
|
||||
async createTestUsers() {
|
||||
await createApmUsers({
|
||||
elasticsearch: { username: 'elastic', password: 'changeme' },
|
||||
kibana: { hostname: this.kibanaUrl },
|
||||
});
|
||||
}
|
||||
|
||||
async loadTestFiles(callback: () => Promise<void>) {
|
||||
console.log('Loading test files');
|
||||
await callback();
|
||||
this.testFilesLoaded = true;
|
||||
console.log('Successfully loaded test files');
|
||||
}
|
||||
|
||||
async loadTestData(e2eDir: string, dataArchives: string[]) {
|
||||
console.log('Loading esArchiver...');
|
||||
|
||||
const esArchiver = this.getService('esArchiver');
|
||||
|
||||
const promises = dataArchives.map((archive) => esArchiver.loadIfNeeded(e2eDir + archive));
|
||||
|
||||
await Promise.all([
|
||||
...promises,
|
||||
esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'),
|
||||
]);
|
||||
}
|
||||
|
||||
getKibanaUrl() {
|
||||
const config = this.getService('config');
|
||||
|
||||
return Url.format({
|
||||
protocol: config.get('servers.kibana.protocol'),
|
||||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
});
|
||||
}
|
||||
|
||||
async run() {
|
||||
if (!this.testFilesLoaded) {
|
||||
throw new Error('Test files not loaded');
|
||||
}
|
||||
const { headless, match, pauseOnError } = this.params;
|
||||
const results = await syntheticsRun({
|
||||
params: { kibanaUrl: this.kibanaUrl, getService: this.getService },
|
||||
playwrightOptions: { headless, chromiumSandbox: false, timeout: 60 * 1000 },
|
||||
match: match === 'undefined' ? '' : match,
|
||||
pauseOnError,
|
||||
});
|
||||
|
||||
await this.assertResults(results);
|
||||
}
|
||||
|
||||
assertResults(results: PromiseType<ReturnType<typeof syntheticsRun>>) {
|
||||
Object.entries(results).forEach(([_journey, result]) => {
|
||||
if (result.status !== 'succeeded') {
|
||||
throw new Error('Tests failed');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cleanUp() {
|
||||
console.log('Removing esArchiver...');
|
||||
esArchiverUnload('full_heartbeat');
|
||||
esArchiverUnload('browser');
|
||||
}
|
||||
}
|
89
x-pack/plugins/synthetics/scripts/base_e2e.js
Normal file
89
x-pack/plugins/synthetics/scripts/base_e2e.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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 yargs = require('yargs');
|
||||
const childProcess = require('child_process');
|
||||
|
||||
const { argv } = yargs(process.argv.slice(2))
|
||||
.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('open', {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
description: 'Opens the Synthetics Test Runner',
|
||||
})
|
||||
.option('kibana-install-dir', {
|
||||
default: '',
|
||||
type: 'string',
|
||||
description: 'Path to the Kibana install directory',
|
||||
})
|
||||
.option('headless', {
|
||||
default: true,
|
||||
type: 'boolean',
|
||||
description: 'Start in headless mode',
|
||||
})
|
||||
.option('grep', {
|
||||
default: undefined,
|
||||
type: 'string',
|
||||
description: 'run only journeys with a name or tags that matches the glob',
|
||||
})
|
||||
.help();
|
||||
|
||||
const { server, runner, open, kibanaInstallDir, headless, grep } = argv;
|
||||
|
||||
let ftrScript = 'functional_tests';
|
||||
if (server) {
|
||||
ftrScript = 'functional_tests_server';
|
||||
} else if (runner || open) {
|
||||
ftrScript = 'functional_test_runner';
|
||||
}
|
||||
|
||||
const config = './synthetics_run.ts';
|
||||
|
||||
function executeSyntheticsRunner(dirPath) {
|
||||
console.log(`Running ${ftrScript} in ${dirPath}`);
|
||||
if (server) {
|
||||
childProcess.execSync(
|
||||
`node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}'`,
|
||||
{
|
||||
cwd: dirPath,
|
||||
stdio: 'inherit',
|
||||
}
|
||||
);
|
||||
} else if (runner) {
|
||||
childProcess.execSync(
|
||||
`node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}' --headless ${headless} --grep '${grep}'`,
|
||||
{
|
||||
cwd: dirPath,
|
||||
stdio: 'inherit',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
childProcess.execSync(
|
||||
`node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}' --grep '${grep}'`,
|
||||
{
|
||||
cwd: dirPath,
|
||||
stdio: 'inherit',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
executeSyntheticsRunner,
|
||||
};
|
|
@ -7,82 +7,9 @@
|
|||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const { executeSyntheticsRunner } = require('./base_e2e');
|
||||
const path = require('path');
|
||||
const yargs = require('yargs');
|
||||
const childProcess = require('child_process');
|
||||
|
||||
const { argv } = yargs(process.argv.slice(2))
|
||||
.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('open', {
|
||||
default: false,
|
||||
type: 'boolean',
|
||||
description: 'Opens the Playwright Test Runner',
|
||||
})
|
||||
.option('kibana-install-dir', {
|
||||
default: '',
|
||||
type: 'string',
|
||||
description: 'Path to the Kibana install directory',
|
||||
})
|
||||
.option('headless', {
|
||||
default: true,
|
||||
type: 'boolean',
|
||||
description: 'Start in headless mode',
|
||||
})
|
||||
.option('grep', {
|
||||
default: undefined,
|
||||
type: 'string',
|
||||
description: 'run only journeys with a name or tags that matches the glob',
|
||||
})
|
||||
.help();
|
||||
|
||||
const { server, runner, open, kibanaInstallDir, headless, grep } = argv;
|
||||
|
||||
const e2eDir = path.join(__dirname, '../e2e');
|
||||
|
||||
let ftrScript = 'functional_tests';
|
||||
if (server) {
|
||||
ftrScript = 'functional_tests_server';
|
||||
} else if (runner || open) {
|
||||
ftrScript = 'functional_test_runner';
|
||||
}
|
||||
|
||||
const config = './playwright_run.ts';
|
||||
|
||||
function executeRunner() {
|
||||
if (server) {
|
||||
childProcess.execSync(
|
||||
`node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}'`,
|
||||
{
|
||||
cwd: e2eDir,
|
||||
stdio: 'inherit',
|
||||
}
|
||||
);
|
||||
} else if (runner) {
|
||||
childProcess.execSync(
|
||||
`node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}' --headless ${headless} --grep '${grep}'`,
|
||||
{
|
||||
cwd: e2eDir,
|
||||
stdio: 'inherit',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
childProcess.execSync(
|
||||
`node ../../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}' --grep '${grep}'`,
|
||||
{
|
||||
cwd: e2eDir,
|
||||
stdio: 'inherit',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
executeRunner();
|
||||
executeSyntheticsRunner(e2eDir);
|
||||
|
|
BIN
x-pack/plugins/ux/e2e/fixtures/rum_8.0.0/data.json.gz
Normal file
BIN
x-pack/plugins/ux/e2e/fixtures/rum_8.0.0/data.json.gz
Normal file
Binary file not shown.
7865
x-pack/plugins/ux/e2e/fixtures/rum_8.0.0/mappings.json
Normal file
7865
x-pack/plugins/ux/e2e/fixtures/rum_8.0.0/mappings.json
Normal file
File diff suppressed because it is too large
Load diff
BIN
x-pack/plugins/ux/e2e/fixtures/rum_test_data/data.json.gz
Normal file
BIN
x-pack/plugins/ux/e2e/fixtures/rum_test_data/data.json.gz
Normal file
Binary file not shown.
9145
x-pack/plugins/ux/e2e/fixtures/rum_test_data/mappings.json
Normal file
9145
x-pack/plugins/ux/e2e/fixtures/rum_test_data/mappings.json
Normal file
File diff suppressed because it is too large
Load diff
8
x-pack/plugins/ux/e2e/journeys/index.ts
Normal file
8
x-pack/plugins/ux/e2e/journeys/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './url_ux_query.journey';
|
49
x-pack/plugins/ux/e2e/journeys/url_ux_query.journey.ts
Normal file
49
x-pack/plugins/ux/e2e/journeys/url_ux_query.journey.ts
Normal file
|
@ -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 { journey, step, expect, before } from '@elastic/synthetics';
|
||||
import { UXDashboardDatePicker } from '../page_objects/date_picker';
|
||||
import { byTestId, loginToKibana, waitForLoadingToFinish } from './utils';
|
||||
|
||||
journey('UX URL Query', async ({ page, params }) => {
|
||||
before(async () => {
|
||||
await waitForLoadingToFinish({ page });
|
||||
});
|
||||
|
||||
const queryParams = {
|
||||
percentile: '50',
|
||||
rangeFrom: '2020-05-18T11:51:00.000Z',
|
||||
rangeTo: '2021-10-30T06:37:15.536Z',
|
||||
};
|
||||
const queryString = new URLSearchParams(queryParams).toString();
|
||||
|
||||
const baseUrl = `${params.kibanaUrl}/app/ux`;
|
||||
|
||||
step('Go to UX Dashboard', async () => {
|
||||
await page.goto(`${baseUrl}?${queryString}`, {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
await loginToKibana({
|
||||
page,
|
||||
user: { username: 'viewer_user', password: 'changeme' },
|
||||
});
|
||||
});
|
||||
|
||||
step('Set date range', async () => {
|
||||
const datePickerPage = new UXDashboardDatePicker(page);
|
||||
await datePickerPage.setDefaultE2eRange();
|
||||
});
|
||||
|
||||
step('Confirm query params', async () => {
|
||||
const value = await page.$eval(
|
||||
byTestId('uxPercentileSelect'),
|
||||
(sel: HTMLInputElement) => sel.value
|
||||
);
|
||||
|
||||
expect(value).toBe(queryParams.percentile);
|
||||
});
|
||||
});
|
77
x-pack/plugins/ux/e2e/journeys/utils.ts
Normal file
77
x-pack/plugins/ux/e2e/journeys/utils.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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 { expect, Page } from '@elastic/synthetics';
|
||||
|
||||
export async function waitForLoadingToFinish({ page }: { page: Page }) {
|
||||
while (true) {
|
||||
if ((await page.$(byTestId('kbnLoadingMessage'))) === null) break;
|
||||
await page.waitForTimeout(5 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
export async function loginToKibana({
|
||||
page,
|
||||
user,
|
||||
}: {
|
||||
page: Page;
|
||||
user?: { username: string; password: string };
|
||||
}) {
|
||||
await page.fill(
|
||||
'[data-test-subj=loginUsername]',
|
||||
user?.username ?? 'elastic',
|
||||
{
|
||||
timeout: 60 * 1000,
|
||||
}
|
||||
);
|
||||
|
||||
await page.fill(
|
||||
'[data-test-subj=loginPassword]',
|
||||
user?.password ?? 'changeme'
|
||||
);
|
||||
|
||||
await page.click('[data-test-subj=loginSubmit]');
|
||||
|
||||
await waitForLoadingToFinish({ page });
|
||||
}
|
||||
|
||||
export const byTestId = (testId: string) => {
|
||||
return `[data-test-subj=${testId}]`;
|
||||
};
|
||||
|
||||
export const assertText = async ({
|
||||
page,
|
||||
text,
|
||||
}: {
|
||||
page: Page;
|
||||
text: string;
|
||||
}) => {
|
||||
await page.waitForSelector(`text=${text}`);
|
||||
expect(await page.$(`text=${text}`)).toBeTruthy();
|
||||
};
|
||||
|
||||
export const assertNotText = async ({
|
||||
page,
|
||||
text,
|
||||
}: {
|
||||
page: Page;
|
||||
text: string;
|
||||
}) => {
|
||||
expect(await page.$(`text=${text}`)).toBeFalsy();
|
||||
};
|
||||
|
||||
export const getQuerystring = (params: object) => {
|
||||
return Object.entries(params)
|
||||
.map(
|
||||
([key, value]) =>
|
||||
encodeURIComponent(key) + '=' + encodeURIComponent(value)
|
||||
)
|
||||
.join('&');
|
||||
};
|
||||
|
||||
export const delay = (ms: number) =>
|
||||
new Promise((resolve) => setTimeout(resolve, ms));
|
23
x-pack/plugins/ux/e2e/page_objects/dashboard.ts
Normal file
23
x-pack/plugins/ux/e2e/page_objects/dashboard.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { Page } from '@elastic/synthetics';
|
||||
import { Locator, byTestId } from './utils';
|
||||
|
||||
export class UXDashboardFilters {
|
||||
readonly page: Page;
|
||||
readonly percentileSelect: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.percentileSelect = page.locator(byTestId('uxPercentileSelect'));
|
||||
}
|
||||
|
||||
getPercentileOption(percentile: '50' | '55' | '90' | '95' | '99') {
|
||||
return this.page.locator(byTestId(`p${percentile}Percentile`));
|
||||
}
|
||||
}
|
68
x-pack/plugins/ux/e2e/page_objects/date_picker.ts
Normal file
68
x-pack/plugins/ux/e2e/page_objects/date_picker.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 moment from 'moment';
|
||||
import { Page } from '@elastic/synthetics';
|
||||
import { Locator, byTestId } from './utils';
|
||||
|
||||
const DEFAULT_ABS_START_UTC_DATE = '2022-05-22T19:00:00.000Z';
|
||||
const DEFAULT_ABS_END_UTC_DATE = '2022-05-22T20:00:00.000Z';
|
||||
const MOMENT_DATE_INPUT_FORMAT = 'MMM DD, YYYY @ HH:mm:ss:SSS';
|
||||
|
||||
export class UXDashboardDatePicker {
|
||||
readonly page: Page;
|
||||
readonly dateStarButton: Locator;
|
||||
readonly dateEndButton: Locator;
|
||||
readonly datePopupAbsoluteTab: Locator;
|
||||
readonly dateAbsoluteInput: Locator;
|
||||
readonly dateApplyButton: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.dateStarButton = page.locator('.euiDatePopoverButton--start');
|
||||
this.dateEndButton = page.locator('.euiDatePopoverButton--end');
|
||||
this.datePopupAbsoluteTab = page.locator('text=Absolute');
|
||||
this.dateAbsoluteInput = page.locator(
|
||||
byTestId('superDatePickerAbsoluteDateInput')
|
||||
);
|
||||
this.dateApplyButton = page.locator(
|
||||
byTestId('superDatePickerApplyTimeButton')
|
||||
);
|
||||
}
|
||||
|
||||
async setAbsoluteStartDate(dateStr: string) {
|
||||
await this.dateStarButton.first().click({ timeout: 3 * 60 * 1000 });
|
||||
await this.datePopupAbsoluteTab.first().click();
|
||||
await this.dateAbsoluteInput.first().click({ clickCount: 3 }); // clear input
|
||||
await this.dateAbsoluteInput.first().type(dateStr);
|
||||
}
|
||||
|
||||
async setAbsoluteEndDate(dateStr: string) {
|
||||
await this.dateEndButton.first().click();
|
||||
await this.datePopupAbsoluteTab.first().click();
|
||||
await this.dateAbsoluteInput.first().click({ clickCount: 3 }); // clear input
|
||||
await this.dateAbsoluteInput.first().type(dateStr);
|
||||
}
|
||||
|
||||
async applyDate() {
|
||||
await this.dateApplyButton.click();
|
||||
}
|
||||
|
||||
async setDefaultE2eRange() {
|
||||
const startDateStr = moment(DEFAULT_ABS_START_UTC_DATE).format(
|
||||
MOMENT_DATE_INPUT_FORMAT
|
||||
);
|
||||
await this.setAbsoluteStartDate(startDateStr);
|
||||
|
||||
const endDateStr = moment(DEFAULT_ABS_END_UTC_DATE).format(
|
||||
MOMENT_DATE_INPUT_FORMAT
|
||||
);
|
||||
await this.setAbsoluteEndDate(endDateStr);
|
||||
|
||||
await this.applyDate();
|
||||
}
|
||||
}
|
42
x-pack/plugins/ux/e2e/page_objects/login.tsx
Normal file
42
x-pack/plugins/ux/e2e/page_objects/login.tsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 { Page } from '@elastic/synthetics';
|
||||
|
||||
export function loginPageProvider({
|
||||
page,
|
||||
isRemote = false,
|
||||
username = 'elastic',
|
||||
password = 'changeme',
|
||||
}: {
|
||||
page: Page;
|
||||
isRemote?: boolean;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}) {
|
||||
return {
|
||||
async waitForLoadingToFinish() {
|
||||
while (true) {
|
||||
if ((await page.$('[data-test-subj=kbnLoadingMessage]')) === null)
|
||||
break;
|
||||
await page.waitForTimeout(5 * 1000);
|
||||
}
|
||||
},
|
||||
async loginToKibana(usernameT?: string, passwordT?: string) {
|
||||
if (isRemote) {
|
||||
await page.click('text="Log in with Elasticsearch"');
|
||||
}
|
||||
await page.fill('[data-test-subj=loginUsername]', usernameT ?? username, {
|
||||
timeout: 60 * 1000,
|
||||
});
|
||||
await page.fill('[data-test-subj=loginPassword]', passwordT ?? password);
|
||||
|
||||
await page.click('[data-test-subj=loginSubmit]');
|
||||
|
||||
await this.waitForLoadingToFinish();
|
||||
},
|
||||
};
|
||||
}
|
63
x-pack/plugins/ux/e2e/page_objects/utils.tsx
Normal file
63
x-pack/plugins/ux/e2e/page_objects/utils.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 { expect, Page } from '@elastic/synthetics';
|
||||
|
||||
export type Locator = ReturnType<Page['locator']>;
|
||||
|
||||
export function byTestId(testId: string) {
|
||||
return `[data-test-subj=${testId}]`;
|
||||
}
|
||||
|
||||
export function utilsPageProvider({ page }: { page: Page }) {
|
||||
return {
|
||||
byTestId(testId: string) {
|
||||
return byTestId(testId);
|
||||
},
|
||||
|
||||
async waitForLoadingToFinish() {
|
||||
while (true) {
|
||||
if ((await page.$(this.byTestId('kbnLoadingMessage'))) === null) break;
|
||||
await page.waitForTimeout(5 * 1000);
|
||||
}
|
||||
},
|
||||
|
||||
async dismissSyntheticsCallout() {
|
||||
await page.click('[data-test-subj=uptimeDismissSyntheticsCallout]', {
|
||||
timeout: 60 * 1000,
|
||||
});
|
||||
},
|
||||
|
||||
async assertText({ text }: { text: string }) {
|
||||
await page.waitForSelector(`text=${text}`);
|
||||
expect(await page.$(`text=${text}`)).toBeTruthy();
|
||||
},
|
||||
|
||||
async fillByTestSubj(dataTestSubj: string, value: string) {
|
||||
await page.fill(`[data-test-subj=${dataTestSubj}]`, value);
|
||||
},
|
||||
|
||||
async selectByTestSubj(dataTestSubj: string, value: string) {
|
||||
await page.selectOption(`[data-test-subj=${dataTestSubj}]`, value);
|
||||
},
|
||||
|
||||
async checkByTestSubj(dataTestSubj: string, value: string) {
|
||||
await page.check(`[data-test-subj=${dataTestSubj}]`);
|
||||
},
|
||||
|
||||
async clickByTestSubj(dataTestSubj: string) {
|
||||
await page.click(`[data-test-subj=${dataTestSubj}]`);
|
||||
},
|
||||
|
||||
async findByTestSubj(dataTestSubj: string) {
|
||||
return await page.waitForSelector(`[data-test-subj=${dataTestSubj}]`);
|
||||
},
|
||||
|
||||
async findByText(text: string) {
|
||||
return await page.waitForSelector(`text=${text}`);
|
||||
},
|
||||
};
|
||||
}
|
45
x-pack/plugins/ux/e2e/synthetics_run.ts
Normal file
45
x-pack/plugins/ux/e2e/synthetics_run.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 { argv } from '@kbn/synthetics-plugin/e2e/parse_args_params';
|
||||
import { SyntheticsRunner } from '@kbn/synthetics-plugin/e2e/synthetics_start';
|
||||
import path from 'path';
|
||||
|
||||
const { headless, grep, pauseOnError } = argv;
|
||||
|
||||
async function runE2ETests({ readConfigFile }: FtrConfigProviderContext) {
|
||||
const kibanaConfig = await readConfigFile(
|
||||
require.resolve('@kbn/synthetics-plugin/e2e/config')
|
||||
);
|
||||
|
||||
return {
|
||||
...kibanaConfig.getAll(),
|
||||
testRunner: async ({ getService }: any) => {
|
||||
const syntheticsRunner = new SyntheticsRunner(getService, {
|
||||
headless,
|
||||
match: grep,
|
||||
pauseOnError,
|
||||
});
|
||||
|
||||
await syntheticsRunner.setup();
|
||||
|
||||
const fixturesDir = path.join(__dirname, '../e2e/fixtures/');
|
||||
|
||||
await syntheticsRunner.loadTestData(fixturesDir, [
|
||||
'rum_8.0.0',
|
||||
'rum_test_data',
|
||||
]);
|
||||
await syntheticsRunner.loadTestFiles(async () => {
|
||||
require('./journeys');
|
||||
});
|
||||
await syntheticsRunner.run();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default runE2ETests;
|
20
x-pack/plugins/ux/e2e/tsconfig.json
Normal file
20
x-pack/plugins/ux/e2e/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"exclude": ["tmp", "target/**/*"],
|
||||
"include": ["./**/*"],
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [ "node"],
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../../apm/tsconfig.json",
|
||||
},
|
||||
{
|
||||
"path": "../../synthetics/e2e/tsconfig.json",
|
||||
},
|
||||
{
|
||||
"path": "../tsconfig.json",
|
||||
}
|
||||
]
|
||||
}
|
20
x-pack/plugins/ux/scripts/e2e.js
Normal file
20
x-pack/plugins/ux/scripts/e2e.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable @kbn/imports/uniform_imports */
|
||||
|
||||
const {
|
||||
executeSyntheticsRunner,
|
||||
} = require('../../synthetics/scripts/base_e2e');
|
||||
const path = require('path');
|
||||
|
||||
const e2eDir = path.join(__dirname, '../e2e');
|
||||
|
||||
console.log(e2eDir);
|
||||
|
||||
executeSyntheticsRunner(e2eDir);
|
Loading…
Add table
Add a link
Reference in a new issue