mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[ON-WEEK][POC] Playwright (#190803)
## UPDATE It has been removed the execution of the playwright tests on buildkite, the execution will be re-enabled as soon as we are ready and as described below in the PR, there are still steps pending to be done. ## Motivation **Cypress is not performing well lately.** * We have been facing significant performance issues with Cypress. For instance, it takes a long time to open the visual interface and start executing tests. **Teams are finding it increasingly challenging to write new tests and debug existing ones.** * The time and effort required to create new tests or troubleshoot existing ones have become burdensome. **Concern about the impact this could have on our testing practices.** * Lose motivation to write tests or, worse, skip writing crucial tests. ## Why Playwright? * Compared to Cypress, Playwright seems to be known for its faster execution times and lower resource consumption. What could have a positive impact by having faster feedback during development and execution of new tests as well as more efficient use of CI resources. * Provides powerful debugging tools which can make easier to write, debug and execute tests. * Seems to provide the same capabilities we currently use in our Cypress tests. * Given Playwright's active development and backing by Microsoft, it is likely to continue evolving rapidly, making it a safe long-term choice. Considering all the above, Playwright seems to be a strong candidate to replace Cypress and address all the issues we are facing lately regarding UI test automation. ## Objective of this POC To write in Playwright a couple of tests we currently have on Cypress to check the performance of the tool as well as the development experience. The tests selected have been: - [enable_risk_score_redirect.cy.ts](https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/dashboards/enable_risk_score_redirect.cy.ts) - Owned by Entity Analytics team and selected by its simplicity since it does not need any special setup to be executed and is short. - [manual_rule_run.cy.ts](https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_gaps/manual_rule_run.cy.ts) - Owned by Detection Engine team and selected because is short and adds a bit more of complexity due to it needs of clean-up and setting up initial data through the API. ## How to execute the tests ### Visual mode - Navigate to: `x-pack/test/security_solution_playwright` - Execute: `yarn open:ess` for ESS environment or `yarn open:serverless` for serverless environment. ### Headless mode - Navigate to: `x-pack/test/security_solution_playwright` - Execute: `yarn run:ess` for ESS environment or `yarn run:serverless` for serverless environment. ### From VScode - Install `Playwright Test for VScode` extension by Microsoft - Navigate to: `x-pack/test/security_solution_playwright` - Execute: `yarn open:ess` for ESS environment or `yarn open:serverless` for serverless environment. - Open your IDE - Click on the `Testing` icon - On the `Test Explorer` click on the three dots to select the profile you are going to execute `ess` or `serverless` - Click on the test you want to execute or navigate to the spec file of the test and execute it from the same spec. ## My experience - Tests are way easier to implement than with Cypress. - Playwright does not rely on chainable commands. Chainable commands on Cypress can lead to confusing code. - Without chainable commands, the flow of the tests is more explicit and easier to understand. - You can notice that the tool has been designed with Typescript in mind. - Is super easy to implement the Page Object Model pattern (POM). - With POM the test code is clean and focused on "what" rather than "how". - Love the fact that you can execute the tests from the same IDE without having to switch windows during test development. - The visual mode execution gives you lots of information out of the box. ## The scope of this PR - Sets the initial infrastructure to write and execute tests with Playwright. - Has examples and set a basis about how to write tests using the POM. - Allows the execution of the tests in ESS and serverless (just stateless environment). - Integrates the execution of the tests with buildkite. ## Pending to be done/investigate - Proper readme - How to split tests and PO between the different teams - Good reports on CI - Upload screenshots on CI - Flaky test suite runner - Complete the labeling - Execution of the tests on MKI environments ## FAQ **Can I start adding tests to playwright?** Currently, you can explore and experiment with Playwright, but there is still work pending to be done to make the tool officially usable. **Why security engineering productivity is the owner of the playwright folder?** This is something temporary to make sure that good practices are followed. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: dkirchan <diamantis.kirchantzoglou@elastic.co> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> Co-authored-by: Jon <jon@budzenski.me>
This commit is contained in:
parent
7ad92ba86f
commit
4041d274b3
47 changed files with 9280 additions and 14 deletions
|
@ -12,6 +12,9 @@ disabled:
|
|||
- x-pack/test_serverless/functional/test_suites/security/cypress/security_config.ts
|
||||
- x-pack/test/security_solution_cypress/serverless_config.ts
|
||||
|
||||
# Playwright
|
||||
- x-pack/test/security_solution_playwright/serverless_config.ts
|
||||
|
||||
# Serverless base config files
|
||||
- x-pack/test_serverless/api_integration/config.base.ts
|
||||
- x-pack/test_serverless/functional/config.base.ts
|
||||
|
|
|
@ -24,6 +24,9 @@ disabled:
|
|||
- x-pack/test/threat_intelligence_cypress/cli_config_parallel.ts
|
||||
- x-pack/test/threat_intelligence_cypress/config.ts
|
||||
|
||||
# Playwright
|
||||
- x-pack/test/security_solution_playwright/playwright.config.ts
|
||||
|
||||
defaultQueue: 'n2-4-spot'
|
||||
enabled:
|
||||
- x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/actions/trial_license_complete_tier/configs/ess.config.ts
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
steps:
|
||||
- command: .buildkite/scripts/steps/functional/security_serverless_playwright.sh
|
||||
label: 'Serverless Playwright - Security Solution Tests'
|
||||
agents:
|
||||
machineType: n2-standard-4
|
||||
preemptible: true
|
||||
depends_on:
|
||||
- build
|
||||
- quick_checks
|
||||
timeout_in_minutes: 60
|
||||
parallelism: 1
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: '-1'
|
||||
limit: 1
|
||||
|
||||
- command: .buildkite/scripts/steps/functional/security_solution_playwright.sh
|
||||
label: 'Playwright - Security Solution Tests'
|
||||
agents:
|
||||
machineType: n2-standard-4
|
||||
preemptible: true
|
||||
depends_on:
|
||||
- build
|
||||
- quick_checks
|
||||
timeout_in_minutes: 60
|
||||
parallelism: 1
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: '-1'
|
||||
limit: 1
|
14
.buildkite/scripts/steps/functional/security_serverless_playwright.sh
Executable file
14
.buildkite/scripts/steps/functional/security_serverless_playwright.sh
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
|
||||
export JOB=kibana-security-solution-playwright
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
echo "--- SERVERLESS - Security Solution Playwright Tests"
|
||||
|
||||
cd x-pack/test/security_solution_playwright
|
||||
|
||||
yarn run:serverless; exit_code=$?; exit $exit_code
|
14
.buildkite/scripts/steps/functional/security_solution_playwright.sh
Executable file
14
.buildkite/scripts/steps/functional/security_solution_playwright.sh
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source .buildkite/scripts/steps/functional/common.sh
|
||||
|
||||
export JOB=kibana-security-solution-playwright
|
||||
export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION}
|
||||
|
||||
echo "--- ESS - Security Solution Playwright Tests"
|
||||
|
||||
cd x-pack/test/security_solution_playwright
|
||||
|
||||
yarn run:ess; exit_code=$?; exit $exit_code
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -1481,6 +1481,7 @@ x-pack/test/security_solution_api_integration/test_suites/sources @elastic/secur
|
|||
/x-pack/test/security_solution_cypress/cypress/* @elastic/security-engineering-productivity
|
||||
/x-pack/test/security_solution_cypress/cypress/tasks/login.ts @elastic/security-engineering-productivity
|
||||
/x-pack/test/security_solution_cypress/es_archives @elastic/security-engineering-productivity
|
||||
/x-pack/test/security_solution_playwright @elastic/security-engineering-productivity
|
||||
/x-pack/plugins/security_solution/scripts/run_cypress @MadameSheema @patrykkopycinski @maximpn @banderror
|
||||
|
||||
## Security Solution sub teams - Threat Hunting Investigations
|
||||
|
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -150,3 +150,11 @@ oas_docs/output/kibana.serverless.tmp*.yaml
|
|||
oas_docs/output/kibana.tmp*.yaml
|
||||
oas_docs/output/kibana.new.yaml
|
||||
oas_docs/output/kibana.serverless.new.yaml
|
||||
|
||||
# Security Solution Playwright
|
||||
x-pack/test/security_solution_playwright/test-results/
|
||||
x-pack/test/security_solution_playwright/playwright-report/
|
||||
x-pack/test/security_solution_playwright/blob-report/
|
||||
x-pack/test/security_solution_playwright/playwright/.cache/
|
||||
x-pack/test/security_solution_playwright/.auth/
|
||||
x-pack/test/security_solution_playwright/.env
|
|
@ -152,6 +152,10 @@ export async function installYarnDeps(log, opts = undefined) {
|
|||
});
|
||||
|
||||
log.success('yarn deps installed');
|
||||
|
||||
await run('yarn', ['playwright', 'install']);
|
||||
|
||||
log.success('Playwright browsers installed');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1072,6 +1072,7 @@
|
|||
"deepmerge": "^4.2.2",
|
||||
"del": "^6.1.0",
|
||||
"diff": "^5.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"elastic-apm-node": "^4.7.3",
|
||||
"email-addresses": "^5.0.0",
|
||||
"eventsource-parser": "^1.1.1",
|
||||
|
@ -1462,6 +1463,7 @@
|
|||
"@mapbox/vector-tile": "1.3.1",
|
||||
"@octokit/rest": "^17.11.2",
|
||||
"@parcel/watcher": "^2.1.0",
|
||||
"@playwright/test": "=1.46.0",
|
||||
"@redocly/cli": "^1.21.0",
|
||||
"@statoscope/webpack-plugin": "^5.28.2",
|
||||
"@storybook/addon-a11y": "^6.5.16",
|
||||
|
@ -1761,7 +1763,8 @@
|
|||
"pirates": "^4.0.1",
|
||||
"piscina": "^3.2.0",
|
||||
"pixelmatch": "^5.3.0",
|
||||
"playwright": "=1.38.0",
|
||||
"playwright": "=1.46.0",
|
||||
"playwright-chromium": "=1.46.0",
|
||||
"pngjs": "^3.4.0",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-loader": "^4.2.0",
|
||||
|
|
|
@ -485,6 +485,15 @@
|
|||
"matchBaseBranches": ["main"],
|
||||
"labels": ["release_note:skip", "team:obs-entities"],
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"groupName": "Security Engineering Productivity",
|
||||
"matchDepNames": ["dotenv", "playwright-chromium", "@playwright/test"],
|
||||
"reviewers": ["team:security-engineering-productivity"],
|
||||
"matchBaseBranches": ["main"],
|
||||
"labels": ["Team: Sec Eng Productivity", "release_note:skip", "backport:all-open"],
|
||||
"minimumReleaseAge": "7 days",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"customManagers": [
|
||||
|
|
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* 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 { run } from '@kbn/dev-cli-runner';
|
||||
import yargs from 'yargs';
|
||||
import _ from 'lodash';
|
||||
import globby from 'globby';
|
||||
import pMap from 'p-map';
|
||||
import { withProcRunner } from '@kbn/dev-proc-runner';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
import { EsVersion, FunctionalTestRunner, runElasticsearch, runKibanaServer } from '@kbn/test';
|
||||
|
||||
import {
|
||||
Lifecycle,
|
||||
ProviderCollection,
|
||||
readProviderSpec,
|
||||
} from '@kbn/test/src/functional_test_runner/lib';
|
||||
import pRetry from 'p-retry';
|
||||
import execa from 'execa';
|
||||
import { prefixedOutputLogger } from '../endpoint/common/utils';
|
||||
import { createToolingLogger } from '../../common/endpoint/data_loaders/utils';
|
||||
import { parseTestFileConfig, retrieveIntegrations } from '../run_cypress/utils';
|
||||
import { getFTRConfig } from '../run_cypress/get_ftr_config';
|
||||
import type { StartedFleetServer } from '../endpoint/common/fleet_server/fleet_server_services';
|
||||
import { startFleetServer } from '../endpoint/common/fleet_server/fleet_server_services';
|
||||
import { createKbnClient } from '../endpoint/common/stack_services';
|
||||
|
||||
export const cli = () => {
|
||||
run(
|
||||
async ({ log: _cliLogger }) => {
|
||||
const { argv } = yargs(process.argv.slice(2))
|
||||
.coerce('configFile', (arg) => (_.isArray(arg) ? _.last(arg) : arg))
|
||||
.coerce('spec', (arg) => (_.isArray(arg) ? _.last(arg) : arg))
|
||||
.coerce('env', (arg: string) =>
|
||||
arg.split(',').reduce((acc, curr) => {
|
||||
const [key, value] = curr.split('=');
|
||||
if (key === 'burn') {
|
||||
acc[key] = parseInt(value, 10);
|
||||
} else {
|
||||
acc[key] = value;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, string | number>)
|
||||
)
|
||||
.boolean('inspect');
|
||||
|
||||
_cliLogger.info(`
|
||||
----------------------------------------------
|
||||
Script arguments:
|
||||
----------------------------------------------
|
||||
${JSON.stringify(argv, null, 2)}
|
||||
----------------------------------------------
|
||||
`);
|
||||
|
||||
const isOpen = argv._.includes('open');
|
||||
const playwrightConfigFilePath = require.resolve(`../../${argv.configFile}`) as string;
|
||||
const playwrightConfigFile = await import(playwrightConfigFilePath);
|
||||
|
||||
const log = prefixedOutputLogger('playwright', createToolingLogger());
|
||||
|
||||
log.info(`
|
||||
----------------------------------------------
|
||||
Playwright config for file: ${playwrightConfigFilePath}:
|
||||
----------------------------------------------
|
||||
${JSON.stringify(playwrightConfigFile, null, 2)}
|
||||
----------------------------------------------
|
||||
|
||||
|
||||
`);
|
||||
|
||||
const specConfig = playwrightConfigFile.testMatch;
|
||||
const specArg = argv.spec;
|
||||
const specPattern = specArg ?? specConfig;
|
||||
const files = retrieveIntegrations(globby.sync(specPattern));
|
||||
const esPorts: number[] = [9200, 9220];
|
||||
const kibanaPorts: number[] = [5601, 5620];
|
||||
const fleetServerPorts: number[] = [8220];
|
||||
|
||||
const getEsPort = <T>(): T | number => {
|
||||
if (isOpen) {
|
||||
return 9220;
|
||||
}
|
||||
|
||||
const esPort = parseInt(`92${Math.floor(Math.random() * 89) + 10}`, 10);
|
||||
if (esPorts.includes(esPort)) {
|
||||
return getEsPort();
|
||||
}
|
||||
esPorts.push(esPort);
|
||||
return esPort;
|
||||
};
|
||||
|
||||
const getKibanaPort = <T>(): T | number => {
|
||||
if (isOpen) {
|
||||
return 5620;
|
||||
}
|
||||
|
||||
const kibanaPort = parseInt(`56${Math.floor(Math.random() * 89) + 10}`, 10);
|
||||
if (kibanaPorts.includes(kibanaPort)) {
|
||||
return getKibanaPort();
|
||||
}
|
||||
kibanaPorts.push(kibanaPort);
|
||||
return kibanaPort;
|
||||
};
|
||||
|
||||
const getFleetServerPort = <T>(): T | number => {
|
||||
if (isOpen) {
|
||||
return 8220;
|
||||
}
|
||||
|
||||
const fleetServerPort = parseInt(`82${Math.floor(Math.random() * 89) + 10}`, 10);
|
||||
if (fleetServerPorts.includes(fleetServerPort)) {
|
||||
return getFleetServerPort();
|
||||
}
|
||||
fleetServerPorts.push(fleetServerPort);
|
||||
return fleetServerPort;
|
||||
};
|
||||
|
||||
const cleanupServerPorts = ({
|
||||
esPort,
|
||||
kibanaPort,
|
||||
fleetServerPort,
|
||||
}: {
|
||||
esPort: number;
|
||||
kibanaPort: number;
|
||||
fleetServerPort: number;
|
||||
}) => {
|
||||
_.pull(esPorts, esPort);
|
||||
_.pull(kibanaPorts, kibanaPort);
|
||||
_.pull(fleetServerPorts, fleetServerPort);
|
||||
};
|
||||
|
||||
const failedSpecFilePaths: string[] = [];
|
||||
|
||||
const runSpecs = async (filePaths: string[]) =>
|
||||
pMap(
|
||||
filePaths,
|
||||
async (filePath) => {
|
||||
let result: Error | undefined;
|
||||
await withProcRunner(log, async (procs) => {
|
||||
const abortCtrl = new AbortController();
|
||||
|
||||
const onEarlyExit = (msg: string) => {
|
||||
log.error(msg);
|
||||
abortCtrl.abort();
|
||||
};
|
||||
|
||||
const esPort: number = getEsPort();
|
||||
const kibanaPort: number = getKibanaPort();
|
||||
const fleetServerPort: number = getFleetServerPort();
|
||||
|
||||
const specFileFTRConfig = parseTestFileConfig(filePath);
|
||||
const ftrConfigFilePath = path.resolve(
|
||||
_.isArray(argv.ftrConfigFile) ? _.last(argv.ftrConfigFile) : argv.ftrConfigFile
|
||||
);
|
||||
|
||||
const config = await getFTRConfig({
|
||||
log,
|
||||
esPort,
|
||||
kibanaPort,
|
||||
fleetServerPort,
|
||||
ftrConfigFilePath,
|
||||
specFilePath: filePath,
|
||||
specFileFTRConfig,
|
||||
isOpen,
|
||||
});
|
||||
|
||||
const createUrlFromFtrConfig = (
|
||||
type: 'elasticsearch' | 'kibana' | 'fleetserver',
|
||||
withAuth: boolean = false
|
||||
): string => {
|
||||
const getKeyPath = (keyPath: string = ''): string => {
|
||||
return `servers.${type}${keyPath ? `.${keyPath}` : ''}`;
|
||||
};
|
||||
|
||||
if (!config.get(getKeyPath())) {
|
||||
throw new Error(`Unable to create URL for ${type}. Not found in FTR config at `);
|
||||
}
|
||||
|
||||
const url = new URL('http://localhost');
|
||||
|
||||
url.port = config.get(getKeyPath('port'));
|
||||
url.protocol = config.get(getKeyPath('protocol'));
|
||||
url.hostname = config.get(getKeyPath('hostname'));
|
||||
|
||||
if (withAuth) {
|
||||
url.username = config.get(getKeyPath('username'));
|
||||
url.password = config.get(getKeyPath('password'));
|
||||
}
|
||||
|
||||
return url.toString().replace(/\/$/, '');
|
||||
};
|
||||
|
||||
const baseUrl = createUrlFromFtrConfig('kibana');
|
||||
const lifecycle = new Lifecycle(log);
|
||||
|
||||
const providers = new ProviderCollection(log, [
|
||||
...readProviderSpec('Service', {
|
||||
lifecycle: () => lifecycle,
|
||||
log: () => log,
|
||||
config: () => config,
|
||||
}),
|
||||
...readProviderSpec('Service', config.get('services')),
|
||||
]);
|
||||
|
||||
const options = {
|
||||
installDir: process.env.KIBANA_INSTALL_DIR,
|
||||
ci: process.env.CI,
|
||||
};
|
||||
let fleetServer: StartedFleetServer | undefined;
|
||||
let shutdownEs;
|
||||
|
||||
try {
|
||||
shutdownEs = await pRetry(
|
||||
async () =>
|
||||
runElasticsearch({
|
||||
config,
|
||||
log,
|
||||
name: `ftr-${esPort}`,
|
||||
esFrom: config.get('esTestCluster')?.from || 'snapshot',
|
||||
onEarlyExit,
|
||||
}),
|
||||
{ retries: 2, forever: false }
|
||||
);
|
||||
await runKibanaServer({
|
||||
procs,
|
||||
config,
|
||||
installDir: options?.installDir,
|
||||
extraKbnOpts:
|
||||
options?.installDir || options?.ci || !isOpen
|
||||
? []
|
||||
: ['--dev', '--no-dev-credentials'],
|
||||
onEarlyExit,
|
||||
inspect: argv.inspect,
|
||||
});
|
||||
|
||||
if (playwrightConfigFile.env?.WITH_FLEET_SERVER) {
|
||||
log.info(`Setting up fleet-server for this Cypress config`);
|
||||
|
||||
const kbnClient = createKbnClient({
|
||||
url: baseUrl,
|
||||
username: config.get('servers.kibana.username'),
|
||||
password: config.get('servers.kibana.password'),
|
||||
log,
|
||||
});
|
||||
|
||||
fleetServer = await pRetry(
|
||||
async () =>
|
||||
startFleetServer({
|
||||
kbnClient,
|
||||
logger: log,
|
||||
port:
|
||||
fleetServerPort ?? config.has('servers.fleetserver.port')
|
||||
? (config.get('servers.fleetserver.port') as number)
|
||||
: undefined,
|
||||
// `force` is needed to ensure that any currently running fleet server (perhaps left
|
||||
// over from an interrupted run) is killed and a new one restarted
|
||||
force: true,
|
||||
}),
|
||||
{ retries: 2, forever: false }
|
||||
);
|
||||
}
|
||||
|
||||
await providers.loadAll();
|
||||
const functionalTestRunner = new FunctionalTestRunner(
|
||||
log,
|
||||
config,
|
||||
EsVersion.getDefault()
|
||||
);
|
||||
const ftrEnv = await pRetry(() => functionalTestRunner.run(abortCtrl.signal), {
|
||||
retries: 1,
|
||||
});
|
||||
|
||||
// Normalized the set of available env vars in Playwright
|
||||
const playwrightCustomEnv = {
|
||||
...ftrEnv,
|
||||
// NOTE:
|
||||
// ELASTICSEARCH_URL needs to be created here with auth because SIEM Playwright setup depends on it. At some
|
||||
// points we should probably try to refactor that code to use `ELASTICSEARCH_URL_WITH_AUTH` instead
|
||||
ELASTICSEARCH_URL:
|
||||
ftrEnv.ELASTICSEARCH_URL ?? createUrlFromFtrConfig('elasticsearch', true),
|
||||
ELASTICSEARCH_URL_WITH_AUTH: createUrlFromFtrConfig('elasticsearch', true),
|
||||
ELASTICSEARCH_USERNAME:
|
||||
ftrEnv.ELASTICSEARCH_USERNAME ?? config.get('servers.elasticsearch.username'),
|
||||
ELASTICSEARCH_PASSWORD:
|
||||
ftrEnv.ELASTICSEARCH_PASSWORD ?? config.get('servers.elasticsearch.password'),
|
||||
FLEET_SERVER_URL: createUrlFromFtrConfig('fleetserver'),
|
||||
KIBANA_URL: baseUrl,
|
||||
KIBANA_URL_WITH_AUTH: createUrlFromFtrConfig('kibana', true),
|
||||
KIBANA_USERNAME: config.get('servers.kibana.username'),
|
||||
KIBANA_PASSWORD: config.get('servers.kibana.password'),
|
||||
IS_SERVERLESS: config.get('serverless'),
|
||||
};
|
||||
|
||||
const envFilePath = path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'..',
|
||||
'..',
|
||||
'test',
|
||||
'security_solution_playwright',
|
||||
'.env'
|
||||
);
|
||||
|
||||
const envContent = Object.entries(playwrightCustomEnv)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join('\n');
|
||||
|
||||
fs.writeFileSync(envFilePath, envContent);
|
||||
|
||||
log.info(`
|
||||
----------------------------------------------
|
||||
Playwright run ENV for file: ${filePath}
|
||||
----------------------------------------------
|
||||
`);
|
||||
|
||||
const project = playwrightCustomEnv.IS_SERVERLESS ? 'serverless' : 'ess';
|
||||
|
||||
if (isOpen) {
|
||||
await execa.command(
|
||||
`../../../node_modules/.bin/playwright test --config ${playwrightConfigFilePath} --ui --project ${project}`,
|
||||
{
|
||||
env: {
|
||||
...playwrightCustomEnv,
|
||||
},
|
||||
stdout: process.stdout,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await execa.command(
|
||||
`../../../node_modules/.bin/playwright test --config ${playwrightConfigFilePath} --project ${project} --grep @${project}`,
|
||||
{
|
||||
env: {
|
||||
...playwrightCustomEnv,
|
||||
FILE_PATH: filePath,
|
||||
},
|
||||
stdout: process.stdout,
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
result = error;
|
||||
failedSpecFilePaths.push(filePath);
|
||||
}
|
||||
|
||||
if (fleetServer) {
|
||||
await fleetServer.stop();
|
||||
}
|
||||
|
||||
await procs.stop('kibana');
|
||||
await shutdownEs?.();
|
||||
cleanupServerPorts({ esPort, kibanaPort, fleetServerPort });
|
||||
return result;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
{
|
||||
concurrency: 1,
|
||||
}
|
||||
);
|
||||
|
||||
await runSpecs(files);
|
||||
},
|
||||
{
|
||||
flags: {
|
||||
allowUnexpected: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require('../../../../../src/setup_node_env');
|
||||
require('./playwright').cli();
|
|
@ -19,7 +19,6 @@ import {
|
|||
FtrConfigProviderContext,
|
||||
} from '@kbn/test';
|
||||
import path from 'path';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import { STATEFUL_ROLES_ROOT_PATH } from '@kbn/es';
|
||||
import { DeploymentAgnosticCommonServices, services } from '../services';
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import { FtrConfigProviderContext, findTestPluginPaths } from '@kbn/test';
|
||||
import { resolve } from 'path';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT as KIBANA_ROOT } from '@kbn/repo-info';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import { FtrConfigProviderContext, findTestPluginPaths } from '@kbn/test';
|
||||
import { resolve } from 'path';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT as KIBANA_ROOT } from '@kbn/repo-info';
|
||||
|
||||
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT as KIBANA_ROOT } from '@kbn/repo-info';
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import { withProcRunner } from '@kbn/dev-proc-runner';
|
||||
import { resolve } from 'path';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import Fs from 'fs';
|
||||
import { createFlagError } from '@kbn/dev-cli-errors';
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT as KIBANA_ROOT } from '@kbn/repo-info';
|
||||
import { FtrConfigProviderContext, findTestPluginPaths } from '@kbn/test';
|
||||
import { services } from './services';
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import { FtrConfigProviderContext, getKibanaCliLoggers } from '@kbn/test';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import { createFlagError } from '@kbn/dev-cli-errors';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
|
|
|
@ -55,6 +55,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
// packages listed in fleet_packages.json
|
||||
// See: https://elastic.slack.com/archives/CNMNXV4RG/p1683033379063079
|
||||
`--xpack.fleet.developer.bundledPackageLocation=./inexistentDir`,
|
||||
'--csp.strict=false',
|
||||
'--csp.warnLegacyBrowsers=false',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -11,8 +11,8 @@ import { AllConnectorsResponse } from '@kbn/actions-plugin/common/routes/connect
|
|||
import { DETECTION_ENGINE_RULES_BULK_ACTION } from '@kbn/security-solution-plugin/common/constants';
|
||||
import { ELASTICSEARCH_PASSWORD, ELASTICSEARCH_USERNAME } from '../../env_var_names_constants';
|
||||
import { deleteAllDocuments } from './elasticsearch';
|
||||
import { DEFAULT_ALERTS_INDEX_PATTERN } from './alerts';
|
||||
import { getSpaceUrl } from '../space';
|
||||
import { DEFAULT_ALERTS_INDEX_PATTERN } from './alerts';
|
||||
|
||||
export const API_AUTH = Object.freeze({
|
||||
user: Cypress.env(ELASTICSEARCH_USERNAME),
|
||||
|
|
|
@ -35,6 +35,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
{ product_line: 'cloud', product_tier: 'complete' },
|
||||
])}`,
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify(['manualRuleRunEnabled'])}`,
|
||||
'--csp.strict=false',
|
||||
'--csp.warnLegacyBrowsers=false',
|
||||
],
|
||||
},
|
||||
testRunner: SecuritySolutionConfigurableCypressTestRunner,
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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 axios from 'axios';
|
||||
import fs from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { HostOptions, SamlSessionManager } from '@kbn/test';
|
||||
import { resolve } from 'path';
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
|
||||
const getYamlData = (filePath: string): any => {
|
||||
const fileContents = fs.readFileSync(filePath, 'utf8');
|
||||
return yaml.safeLoad(fileContents);
|
||||
};
|
||||
|
||||
const getRoleConfiguration = (role: string, filePath: string): any => {
|
||||
const data = getYamlData(filePath);
|
||||
if (data[role]) {
|
||||
return data[role];
|
||||
} else {
|
||||
throw new Error(`Role '${role}' not found in the YAML file.`);
|
||||
}
|
||||
};
|
||||
|
||||
const rolesPath =
|
||||
'../../../packages/kbn-es/src/serverless_resources/project_roles/security/roles.yml';
|
||||
|
||||
export const getApiKeyForUser = async () => {
|
||||
const log = new ToolingLog({ level: 'verbose', writeTo: process.stdout });
|
||||
|
||||
const kbnHost = process.env.KIBANA_URL || process.env.BASE_URL;
|
||||
const kbnUrl = new URL(kbnHost!);
|
||||
|
||||
const hostOptions: HostOptions = {
|
||||
protocol: kbnUrl.protocol as 'http' | 'https',
|
||||
hostname: kbnUrl.hostname,
|
||||
port: parseInt(kbnUrl.port, 10),
|
||||
username: process.env.ELASTICSEARCH_USERNAME ?? '',
|
||||
password: process.env.ELASTICSEARCH_PASSWORD ?? '',
|
||||
};
|
||||
|
||||
const rolesFilename = process.env.PROXY_ORG ? `${process.env.PROXY_ORG}.json` : undefined;
|
||||
const cloudUsersFilePath = resolve(REPO_ROOT, '.ftr', rolesFilename ?? 'role_users.json');
|
||||
|
||||
const samlSessionManager = new SamlSessionManager({
|
||||
hostOptions,
|
||||
log,
|
||||
isCloud: process.env.CLOUD_SERVERLESS === 'true',
|
||||
cloudUsersFilePath,
|
||||
});
|
||||
|
||||
const adminCookieHeader = await samlSessionManager.getApiCredentialsForRole('admin');
|
||||
|
||||
let roleDescriptor = {};
|
||||
|
||||
const roleConfig = getRoleConfiguration('admin', rolesPath);
|
||||
|
||||
roleDescriptor = { ['system_indices_superuser']: roleConfig };
|
||||
|
||||
const response = await axios.post(
|
||||
`${process.env.KIBANA_URL}/internal/security/api_key`,
|
||||
{
|
||||
name: 'myTestApiKey',
|
||||
metadata: {},
|
||||
role_descriptors: roleDescriptor,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'kbn-xsrf': 'cypress-creds',
|
||||
'x-elastic-internal-origin': 'security-solution',
|
||||
...adminCookieHeader,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const apiKey = response.data.encoded;
|
||||
|
||||
return apiKey;
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { APIRequestContext } from '@playwright/test';
|
||||
import { getCommonHeaders } from './headers';
|
||||
|
||||
export const deleteAllSecurityDocuments = async (request: APIRequestContext) => {
|
||||
const securityIndexes = `.lists-*,.items-*,.alerts-security.alerts-*`;
|
||||
const headers = await getCommonHeaders();
|
||||
|
||||
await request.post(`${process.env.ELASTICSEARCH_URL}/${securityIndexes}/_refresh`, {
|
||||
headers,
|
||||
});
|
||||
|
||||
await request.post(
|
||||
`${process.env.ELASTICSEARCH_URL}/${securityIndexes}/_delete_by_query?conflicts=proceed&scroll_size=10000&refresh`,
|
||||
{
|
||||
headers,
|
||||
data: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants';
|
||||
import { getApiKeyForUser } from './api_key';
|
||||
|
||||
export const getCommonHeaders = async (additionalHeaders: Record<string, string> = {}) => {
|
||||
let auth = '';
|
||||
|
||||
if (process.env.IS_SERVERLESS === 'true') {
|
||||
const apiKey = await getApiKeyForUser();
|
||||
auth = `ApiKey ${apiKey}`;
|
||||
} else {
|
||||
const username = process.env.ELASTICSEARCH_USERNAME || '';
|
||||
const password = process.env.ELASTICSEARCH_PASSWORD || '';
|
||||
const encodedCredentials = Buffer.from(`${username}:${password}`).toString('base64');
|
||||
|
||||
auth = `Basic ${encodedCredentials}`;
|
||||
}
|
||||
return {
|
||||
'kbn-xsrf': 'cypress-creds',
|
||||
'x-elastic-internal-origin': 'security-solution',
|
||||
Authorization: auth,
|
||||
...additionalHeaders,
|
||||
};
|
||||
};
|
||||
|
||||
export const getCommonHeadersWithApiVersion = async () => {
|
||||
const header = await getCommonHeaders({
|
||||
'elastic-api-version': INITIAL_REST_VERSION,
|
||||
});
|
||||
return header;
|
||||
};
|
50
x-pack/test/security_solution_playwright/api_utils/rules.ts
Normal file
50
x-pack/test/security_solution_playwright/api_utils/rules.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 {
|
||||
DETECTION_ENGINE_RULES_BULK_ACTION,
|
||||
DETECTION_ENGINE_RULES_URL,
|
||||
} from '@kbn/security-solution-plugin/common/constants';
|
||||
import { APIRequestContext } from '@playwright/test';
|
||||
import { getRuleForAlertTesting } from '../../common/utils/security_solution';
|
||||
import { getCommonHeadersWithApiVersion } from './headers';
|
||||
|
||||
const indexes = [
|
||||
'apm-*-transaction*',
|
||||
'auditbeat-*',
|
||||
'endgame-*',
|
||||
'filebeat-*',
|
||||
'logs-*',
|
||||
'packetbeat-*',
|
||||
'traces-apm*',
|
||||
'winlogbeat-*',
|
||||
'-*elastic-cloud-logs-*',
|
||||
];
|
||||
|
||||
export const createRule = async (request: APIRequestContext) => {
|
||||
const data = getRuleForAlertTesting(indexes);
|
||||
const headers = await getCommonHeadersWithApiVersion();
|
||||
const response = await request.post(DETECTION_ENGINE_RULES_URL, {
|
||||
data,
|
||||
headers,
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const deleteAllRules = async (request: APIRequestContext) => {
|
||||
const headers = await getCommonHeadersWithApiVersion();
|
||||
const response = await request.post(DETECTION_ENGINE_RULES_BULK_ACTION, {
|
||||
data: {
|
||||
query: '',
|
||||
action: 'delete',
|
||||
},
|
||||
headers,
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
};
|
|
@ -0,0 +1,123 @@
|
|||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "_aZE5nwBOpWiDweSth_E",
|
||||
"index": "auditbeat-2022",
|
||||
"source": {
|
||||
"@timestamp" : "2022-03-04T19:41:34.045Z",
|
||||
"host" : {
|
||||
"hostname" : "test.local",
|
||||
"architecture" : "x86_64",
|
||||
"os" : {
|
||||
"platform" : "darwin",
|
||||
"version" : "10.16",
|
||||
"family" : "darwin",
|
||||
"name" : "Mac OS X",
|
||||
"kernel" : "21.3.0",
|
||||
"build" : "21D62",
|
||||
"type" : "macos"
|
||||
},
|
||||
"id" : "44426D67-79AB-547C-7777-440AB8F5DDD2",
|
||||
"ip" : [
|
||||
"fe80::bade:48ff:fe00:1122",
|
||||
"fe81::4ab:9565:1199:be3",
|
||||
"192.168.5.175",
|
||||
"fe80::40d7:d0ff:fe66:f55",
|
||||
"fe81::40d8:d0ff:fe66:f55",
|
||||
"fe82::c2c:6bdf:3307:dce0",
|
||||
"fe83::5069:fcd5:e31c:7059",
|
||||
"fe80::ce81:b2c:bd2c:69e",
|
||||
"fe80::febc:bbc1:c517:827b",
|
||||
"fe80::6d09:bee6:55a5:539d",
|
||||
"fe80::c920:752e:1e0e:edc9",
|
||||
"fe80::a4a:ca38:761f:83e2"
|
||||
],
|
||||
"mac" : [
|
||||
"ad:df:48:00:11:22",
|
||||
"a6:86:e7:ae:5a:b6",
|
||||
"a9:83:e7:ae:5a:b6",
|
||||
"43:d8:d0:66:0f:55",
|
||||
"42:d8:d0:66:0f:57",
|
||||
"82:70:c7:c2:3c:01",
|
||||
"82:70:c6:c2:4c:00",
|
||||
"82:76:a6:c2:3c:05",
|
||||
"82:70:c6:b2:3c:04",
|
||||
"82:71:a6:c2:3c:01"
|
||||
],
|
||||
"name" : "siem-kibana"
|
||||
},
|
||||
"agent" : {
|
||||
"type" : "winlogbeat",
|
||||
"version" : "8.1.0",
|
||||
"ephemeral_id" : "f6df090f-656a-4a79-a6a1-0c8671c9752d",
|
||||
"id" : "0ebd469b-c164-4734-00e6-96d018098dc7",
|
||||
"name" : "test.local"
|
||||
},
|
||||
"event" : {
|
||||
"module" : "sysmon",
|
||||
"dataset" : "process",
|
||||
"kind" : "event",
|
||||
"category" : [
|
||||
"process"
|
||||
],
|
||||
"type" : [
|
||||
"start"
|
||||
],
|
||||
"action" : "process_started"
|
||||
},
|
||||
"destination": {
|
||||
"port": 80
|
||||
},
|
||||
"process" : {
|
||||
"start" : "2022-03-04T19:41:34.902Z",
|
||||
"pid" : 30884,
|
||||
"working_directory" : "/Users/test/security_solution",
|
||||
"hash" : {
|
||||
"sha1" : "ae2d46c38fa207efbea5fcecd6294eebbf5af00f"
|
||||
},
|
||||
"parent" : {
|
||||
"pid" : 777
|
||||
},
|
||||
"executable" : "/bin/zsh",
|
||||
"name" : "zsh",
|
||||
"args" : [
|
||||
"-zsh",
|
||||
"unique"
|
||||
],
|
||||
"entity_id" : "q6pltOhTWlQx3BCE",
|
||||
"entry_leader": {
|
||||
"entity_id": "q6pltOhTWlQx3BCE",
|
||||
"name": "fake entry",
|
||||
"pid": 2342342
|
||||
}
|
||||
},
|
||||
"message" : "Process zsh (PID: 27884) by user test STARTED",
|
||||
"user" : {
|
||||
"id" : "505",
|
||||
"group" : {
|
||||
"name" : "staff",
|
||||
"id" : "20"
|
||||
},
|
||||
"effective" : {
|
||||
"id" : "505",
|
||||
"group" : {
|
||||
"id" : "20"
|
||||
}
|
||||
},
|
||||
"saved" : {
|
||||
"id" : "505",
|
||||
"group" : {
|
||||
"id" : "20"
|
||||
}
|
||||
},
|
||||
"name" : "test"
|
||||
},
|
||||
"service" : {
|
||||
"type" : "system"
|
||||
},
|
||||
"ecs" : {
|
||||
"version" : "8.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 Fs from 'fs';
|
||||
import * as Url from 'url';
|
||||
import { EsArchiver } from '@kbn/es-archiver';
|
||||
import { createEsClientForTesting, KbnClient, systemIndicesSuperuser } from '@kbn/test';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { CA_CERT_PATH } from '@kbn/dev-utils';
|
||||
|
||||
export const createEsArchiver = async () => {
|
||||
const log = new ToolingLog({ level: 'verbose', writeTo: process.stdout });
|
||||
|
||||
const isServerless = process.env.IS_SERVERLESS === 'true';
|
||||
const isCloudServerless = process.env.CLOUD_SERVERLESS === 'true';
|
||||
|
||||
const serverlessCloudUser = {
|
||||
username: process.env.ELASTICSEARCH_USERNAME ?? '',
|
||||
password: process.env.ELASTICSEARCH_PASSWORD ?? '',
|
||||
};
|
||||
|
||||
if (isServerless && (!serverlessCloudUser.username || !serverlessCloudUser.password)) {
|
||||
throw new Error(
|
||||
'ELASTICSEARCH_USERNAME and ELASTICSEARCH_PASSWORD must be defined for serverless configuration'
|
||||
);
|
||||
}
|
||||
|
||||
let authOverride;
|
||||
if (isServerless) {
|
||||
authOverride = isCloudServerless ? serverlessCloudUser : systemIndicesSuperuser;
|
||||
}
|
||||
|
||||
const esUrl = process.env.ELASTICSEARCH_URL;
|
||||
if (!esUrl) {
|
||||
throw new Error('ELASTICSEARCH_URL environment variable is not set');
|
||||
}
|
||||
|
||||
const client = createEsClientForTesting({
|
||||
esUrl: Url.format(new URL(esUrl)),
|
||||
authOverride,
|
||||
});
|
||||
|
||||
const kibanaUrl = process.env.KIBANA_URL || process.env.BASE_URL;
|
||||
|
||||
const kbnClient = new KbnClient({
|
||||
log,
|
||||
url: kibanaUrl as string,
|
||||
...(esUrl.includes('https') ? { certificateAuthorities: [Fs.readFileSync(CA_CERT_PATH)] } : {}),
|
||||
});
|
||||
|
||||
return new EsArchiver({
|
||||
log,
|
||||
client,
|
||||
kbnClient,
|
||||
baseDir: './es_archives',
|
||||
});
|
||||
};
|
41
x-pack/test/security_solution_playwright/fixtures/saml.ts
Normal file
41
x-pack/test/security_solution_playwright/fixtures/saml.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 { test as base } from '@playwright/test';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { HostOptions, SamlSessionManager } from '@kbn/test';
|
||||
import { resolve } from 'path';
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
|
||||
export const test = base.extend({
|
||||
samlSessionManager: async ({}, use) => {
|
||||
const log = new ToolingLog({ level: 'verbose', writeTo: process.stdout });
|
||||
|
||||
const kbnHost = process.env.KIBANA_URL || process.env.BASE_URL;
|
||||
const kbnUrl = new URL(kbnHost!);
|
||||
|
||||
const hostOptions: HostOptions = {
|
||||
protocol: kbnUrl.protocol as 'http' | 'https',
|
||||
hostname: kbnUrl.hostname,
|
||||
port: parseInt(kbnUrl.port, 10),
|
||||
username: process.env.ELASTICSEARCH_USERNAME ?? '',
|
||||
password: process.env.ELASTICSEARCH_PASSWORD ?? '',
|
||||
};
|
||||
|
||||
const rolesFilename = process.env.PROXY_ORG ? `${process.env.PROXY_ORG}.json` : undefined;
|
||||
const cloudUsersFilePath = resolve(REPO_ROOT, '.ftr', rolesFilename ?? 'role_users.json');
|
||||
|
||||
const samlSessionManager = new SamlSessionManager({
|
||||
hostOptions,
|
||||
log,
|
||||
isCloud: process.env.CLOUD_SERVERLESS === 'true',
|
||||
cloudUsersFilePath,
|
||||
});
|
||||
|
||||
await use(samlSessionManager);
|
||||
},
|
||||
});
|
13
x-pack/test/security_solution_playwright/index.d.ts
vendored
Normal file
13
x-pack/test/security_solution_playwright/index.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 module '@kbn/repo-info' {
|
||||
export const REPO_ROOT: string;
|
||||
}
|
||||
declare global {
|
||||
const ReadableStream: any;
|
||||
}
|
15
x-pack/test/security_solution_playwright/package.json
Normal file
15
x-pack/test/security_solution_playwright/package.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"author": "Elastic",
|
||||
"name": "security_solution_playwright",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"license": "Elastic License 2.0",
|
||||
"scripts": {
|
||||
"playwright:open": "node ../../plugins/security_solution/scripts/run_playwright/start_playwright open --config-file ../../test/security_solution_playwright/playwright.config.ts",
|
||||
"playwright:run": "node ../../plugins/security_solution/scripts/run_playwright/start_playwright run --config-file ../../test/security_solution_playwright/playwright.config.ts",
|
||||
"open:ess": "npm run playwright:open -- --ftr-config-file ../security_solution_cypress/cli_config",
|
||||
"run:ess": "npm run playwright:run -- --ftr-config-file ../security_solution_cypress/cli_config",
|
||||
"open:serverless": "npm run playwright:open -- --ftr-config-file ../security_solution_cypress/serverless_config",
|
||||
"run:serverless": "npm run playwright:run -- --ftr-config-file ../security_solution_cypress/serverless_config"
|
||||
}
|
||||
}
|
|
@ -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 { Locator, Page } from '@playwright/test';
|
||||
|
||||
const PAGE_TITLE = '[data-test-subj="entityAnalyticsManagementPageTitle"]';
|
||||
|
||||
export class EntityAnalyticsManagementPage {
|
||||
page: Page;
|
||||
entityAnalyticsManagementPageTitle!: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
this.entityAnalyticsManagementPageTitle = this.page.locator(PAGE_TITLE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { Locator, Page, expect } from '@playwright/test';
|
||||
import { EntityAnalyticsManagementPage } from './entity_analytics_management_po';
|
||||
import { PageFactory } from './page_factory';
|
||||
|
||||
const PAGE_URL = '/app/security/entity_analytics';
|
||||
const ENABLE_HOST_RISK_SCORE_BUTTON = '[data-test-subj="enable_host_risk_score"]';
|
||||
const ENABLE_USER_RISK_SCORE_BUTTON = '[data-test-subj="enable_user_risk_score"]';
|
||||
|
||||
export class EntityAnalyticsPage {
|
||||
page: Page;
|
||||
enableHostRiskScoreBtn!: Locator;
|
||||
enableUserRiskScoreBtn!: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
this.enableHostRiskScoreBtn = this.page.locator(ENABLE_HOST_RISK_SCORE_BUTTON);
|
||||
this.enableUserRiskScoreBtn = this.page.locator(ENABLE_USER_RISK_SCORE_BUTTON);
|
||||
}
|
||||
|
||||
async navigates() {
|
||||
await this.page.goto(PAGE_URL);
|
||||
}
|
||||
|
||||
async enableHostRisk(): Promise<EntityAnalyticsManagementPage> {
|
||||
await this.enableHostRiskScoreBtn.click();
|
||||
return await PageFactory.createEntityAnalyticsManagementPage(this.page);
|
||||
}
|
||||
|
||||
async enableUserRisk(): Promise<EntityAnalyticsManagementPage> {
|
||||
await this.enableUserRiskScoreBtn.click();
|
||||
return await PageFactory.createEntityAnalyticsManagementPage(this.page);
|
||||
}
|
||||
|
||||
async waitForEnableHostRiskScoreToBePresent() {
|
||||
await expect(this.enableHostRiskScoreBtn).toBeVisible();
|
||||
}
|
||||
|
||||
async waitForEnableUserRiskScoreToBePresent() {
|
||||
await expect(this.enableUserRiskScoreBtn).toBeVisible();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 '@playwright/test';
|
||||
import { EntityAnalyticsPage } from './entity_analytics_po';
|
||||
import { EntityAnalyticsManagementPage } from './entity_analytics_management_po';
|
||||
import { RuleDetailsPage } from './rule_details_page_po';
|
||||
import { RuleManagementPage } from './rule_management_po';
|
||||
|
||||
export class PageFactory {
|
||||
static async createEntityAnalyticsPage(page: Page): Promise<EntityAnalyticsPage> {
|
||||
const entityAnalyticsPage = new EntityAnalyticsPage(page);
|
||||
await entityAnalyticsPage.initialize();
|
||||
return entityAnalyticsPage;
|
||||
}
|
||||
|
||||
static async createEntityAnalyticsManagementPage(
|
||||
page: Page
|
||||
): Promise<EntityAnalyticsManagementPage> {
|
||||
const entityAnalyticsManagementPage = new EntityAnalyticsManagementPage(page);
|
||||
await entityAnalyticsManagementPage.initialize();
|
||||
return entityAnalyticsManagementPage;
|
||||
}
|
||||
|
||||
static async createRuleDetailsPage(page: Page): Promise<RuleDetailsPage> {
|
||||
const ruleDetailsPage = new RuleDetailsPage(page);
|
||||
await ruleDetailsPage.initialize();
|
||||
return ruleDetailsPage;
|
||||
}
|
||||
|
||||
static async createRuleManagementPage(page: Page): Promise<RuleManagementPage> {
|
||||
const ruleManagementPage = new RuleManagementPage(page);
|
||||
await ruleManagementPage.initialize();
|
||||
return ruleManagementPage;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 { Locator, Page } from '@playwright/test';
|
||||
|
||||
const PAGE_URL = '/app/security/rules/id/';
|
||||
|
||||
const POPOVER_ACTIONS_TRIGGER_BUTTON = '[data-test-subj="rules-details-popover-button-icon"]';
|
||||
const RULE_DETAILS_MANUAL_RULE_RUN_BTN = '[data-test-subj="rules-details-manual-rule-run"]';
|
||||
const MODAL_CONFIRMATION_BTN = '[data-test-subj="confirmModalConfirmButton"]';
|
||||
const TOASTER = '[data-test-subj="euiToastHeader"]';
|
||||
|
||||
export class RuleDetailsPage {
|
||||
page: Page;
|
||||
popoverActionsTriggerButton!: Locator;
|
||||
ruleDetailsManualRuleRunButton!: Locator;
|
||||
modalConfirmationBtn!: Locator;
|
||||
toaster!: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
this.popoverActionsTriggerButton = this.page.locator(POPOVER_ACTIONS_TRIGGER_BUTTON);
|
||||
this.ruleDetailsManualRuleRunButton = this.page.locator(RULE_DETAILS_MANUAL_RULE_RUN_BTN);
|
||||
this.modalConfirmationBtn = this.page.locator(MODAL_CONFIRMATION_BTN);
|
||||
this.toaster = this.page.locator(TOASTER);
|
||||
}
|
||||
|
||||
async navigateTo(ruleId: string) {
|
||||
await this.page.goto(`${PAGE_URL}${ruleId}`);
|
||||
}
|
||||
|
||||
async manualRuleRun() {
|
||||
await this.popoverActionsTriggerButton.click();
|
||||
await this.ruleDetailsManualRuleRunButton.click();
|
||||
await this.modalConfirmationBtn.click();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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, Locator, Page } from '@playwright/test';
|
||||
|
||||
const PAGE_URL = '/app/security/rules/management';
|
||||
|
||||
const AUTO_REFRESH_POPOVER_TRIGGER_BUTTON = '[data-test-subj="autoRefreshButton"]';
|
||||
const REFRESH_SETTINGS_SWITCH = '[data-test-subj="refreshSettingsSwitch"]';
|
||||
const COLLAPSED_ACTION_BTN = '[data-test-subj="euiCollapsedItemActionsButton"]';
|
||||
const MANUAL_RULE_RUN_ACTION_BTN = '[data-test-subj="manualRuleRunAction"]';
|
||||
const MODAL_CONFIRMATION_BTN = '[data-test-subj="confirmModalConfirmButton"]';
|
||||
const TOASTER = '[data-test-subj="euiToastHeader"]';
|
||||
|
||||
export class RuleManagementPage {
|
||||
page: Page;
|
||||
autoRefreshPopoverTriggerButton!: Locator;
|
||||
refreshSettingsSwitch!: Locator;
|
||||
collapsedActionBtn!: Locator;
|
||||
manualRuleRunActionBtn!: Locator;
|
||||
modalConfirmationBtn!: Locator;
|
||||
toaster!: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
this.autoRefreshPopoverTriggerButton = this.page.locator(AUTO_REFRESH_POPOVER_TRIGGER_BUTTON);
|
||||
this.refreshSettingsSwitch = this.page.locator(REFRESH_SETTINGS_SWITCH);
|
||||
this.collapsedActionBtn = this.page.locator(COLLAPSED_ACTION_BTN);
|
||||
this.manualRuleRunActionBtn = this.page.locator(MANUAL_RULE_RUN_ACTION_BTN);
|
||||
this.modalConfirmationBtn = this.page.locator(MODAL_CONFIRMATION_BTN);
|
||||
this.toaster = this.page.locator(TOASTER);
|
||||
}
|
||||
|
||||
async navigate() {
|
||||
await this.page.goto(PAGE_URL);
|
||||
}
|
||||
|
||||
async disableAutoRefresh() {
|
||||
await this.autoRefreshPopoverTriggerButton.click();
|
||||
await this.refreshSettingsSwitch.click();
|
||||
}
|
||||
|
||||
async manuallyRunFirstRule() {
|
||||
await this.collapsedActionBtn.first().click();
|
||||
await expect(this.manualRuleRunActionBtn).toBeVisible();
|
||||
await this.manualRuleRunActionBtn.click();
|
||||
await this.modalConfirmationBtn.click();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 { defineConfig, devices } from '@playwright/test';
|
||||
import path from 'path';
|
||||
import { ReadableStream as PolyfillReadableStream } from 'web-streams-polyfill';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
(globalThis as any).ReadableStream = PolyfillReadableStream;
|
||||
|
||||
dotenv.config({ path: path.resolve(__dirname, './.env') });
|
||||
|
||||
export default defineConfig({
|
||||
timeout: 60000,
|
||||
expect: { timeout: 60000 },
|
||||
testDir: './tests/',
|
||||
testMatch: process.env.FILE_PATH || '**/*.spec.ts',
|
||||
fullyParallel: false,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: 0,
|
||||
workers: 1,
|
||||
reporter: [['list', { printSteps: true }]],
|
||||
use: {
|
||||
trace: 'on-first-retry',
|
||||
ignoreHTTPSErrors: true,
|
||||
baseURL: process.env.KIBANA_URL,
|
||||
bypassCSP: true,
|
||||
actionTimeout: 60000,
|
||||
navigationTimeout: 60000,
|
||||
screenshot: 'only-on-failure',
|
||||
launchOptions: {
|
||||
args: ['--disable-web-security'],
|
||||
},
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'login_ess',
|
||||
testMatch: '**/setup/login_ess.ts',
|
||||
},
|
||||
{
|
||||
name: 'login_serverless',
|
||||
testMatch: '**/setup/login_serverless.ts',
|
||||
},
|
||||
{
|
||||
name: 'ess',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
dependencies: ['login_ess'],
|
||||
},
|
||||
{
|
||||
name: 'serverless',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
dependencies: ['login_serverless'],
|
||||
},
|
||||
],
|
||||
});
|
|
@ -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 { expect, test } from '@playwright/test';
|
||||
import { PageFactory } from '../page_objects/page_factory';
|
||||
import { EntityAnalyticsPage } from '../page_objects/entity_analytics_po';
|
||||
import { EntityAnalyticsManagementPage } from '../page_objects/entity_analytics_management_po';
|
||||
import { createEsArchiver } from '../fixtures/es_archiver';
|
||||
|
||||
let entityAnalyticsPage: EntityAnalyticsPage;
|
||||
let entityAnalyticsManagementPage: EntityAnalyticsManagementPage;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
const esArchiver = await createEsArchiver();
|
||||
await esArchiver.loadIfNeeded('auditbeat_single');
|
||||
});
|
||||
|
||||
test.describe('Enable risk scores from dashboard', { tag: ['@serverless', '@ess'] }, () => {
|
||||
test.use({ storageState: '.auth/user.json' });
|
||||
test.beforeEach(async ({ page }) => {
|
||||
entityAnalyticsPage = await PageFactory.createEntityAnalyticsPage(page);
|
||||
await entityAnalyticsPage.navigates();
|
||||
});
|
||||
test('host risk enable button should redirect to entity management page', async () => {
|
||||
await entityAnalyticsPage.waitForEnableHostRiskScoreToBePresent();
|
||||
entityAnalyticsManagementPage = await entityAnalyticsPage.enableHostRisk();
|
||||
|
||||
await expect(entityAnalyticsManagementPage.entityAnalyticsManagementPageTitle).toHaveText(
|
||||
'Entity Risk Score'
|
||||
);
|
||||
});
|
||||
|
||||
test('user risk enable button should redirect to entity management page', async () => {
|
||||
await entityAnalyticsPage.waitForEnableUserRiskScoreToBePresent();
|
||||
entityAnalyticsManagementPage = await entityAnalyticsPage.enableUserRisk();
|
||||
|
||||
await expect(entityAnalyticsManagementPage.entityAnalyticsManagementPageTitle).toHaveText(
|
||||
'Entity Risk Score'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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, test } from '@playwright/test';
|
||||
import { PageFactory } from '../page_objects/page_factory';
|
||||
import { RuleDetailsPage } from '../page_objects/rule_details_page_po';
|
||||
import { createRule, deleteAllRules } from '../api_utils/rules';
|
||||
import { deleteAllSecurityDocuments } from '../api_utils/documents';
|
||||
import { RuleManagementPage } from '../page_objects/rule_management_po';
|
||||
import { createEsArchiver } from '../fixtures/es_archiver';
|
||||
|
||||
let ruleDetailsPage: RuleDetailsPage;
|
||||
let ruleManagementPage: RuleManagementPage;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
const esArchiver = await createEsArchiver();
|
||||
await esArchiver.loadIfNeeded('auditbeat_single');
|
||||
});
|
||||
|
||||
test.describe('Manual rule run', { tag: ['@ess', '@serverless'] }, () => {
|
||||
test.use({ storageState: '.auth/user.json' });
|
||||
|
||||
test.beforeEach(async ({ request }) => {
|
||||
await deleteAllRules(request);
|
||||
await deleteAllSecurityDocuments(request);
|
||||
});
|
||||
|
||||
test('schedule from rule details page', async ({ request, page }) => {
|
||||
const { id: ruleId } = await createRule(request);
|
||||
|
||||
ruleDetailsPage = await PageFactory.createRuleDetailsPage(page);
|
||||
|
||||
await ruleDetailsPage.navigateTo(ruleId);
|
||||
await ruleDetailsPage.manualRuleRun();
|
||||
|
||||
await expect(ruleDetailsPage.toaster).toHaveText(
|
||||
'Successfully scheduled manual run for 1 rule'
|
||||
);
|
||||
});
|
||||
|
||||
test('schedule from rules management table', async ({ request, page }) => {
|
||||
await createRule(request);
|
||||
|
||||
ruleManagementPage = await PageFactory.createRuleManagementPage(page);
|
||||
|
||||
await ruleManagementPage.navigate();
|
||||
await ruleManagementPage.disableAutoRefresh();
|
||||
await ruleManagementPage.manuallyRunFirstRule();
|
||||
|
||||
await expect(ruleManagementPage.toaster).toHaveText(
|
||||
'Successfully scheduled manual run for 1 rule'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 { test } from '@playwright/test';
|
||||
import { getCommonHeaders } from '../../api_utils/headers';
|
||||
|
||||
export const authFile = '.auth/user.json';
|
||||
|
||||
test('login', { tag: '@ess' }, async ({ request }) => {
|
||||
const headers = await getCommonHeaders();
|
||||
|
||||
await request.post(`${process.env.KIBANA_URL}/internal/security/login`, {
|
||||
headers,
|
||||
data: {
|
||||
providerType: 'basic',
|
||||
providerName: 'basic',
|
||||
currentURL: '/',
|
||||
params: {
|
||||
username: process.env.ELASTICSEARCH_USERNAME,
|
||||
password: process.env.ELASTICSEARCH_PASSWORD,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await request.storageState({ path: authFile });
|
||||
});
|
|
@ -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 fs from 'fs';
|
||||
import path from 'path';
|
||||
import { test } from '../../fixtures/saml';
|
||||
|
||||
export const authFile = '.auth/user.json';
|
||||
|
||||
test('serverless', { tag: '@serverless' }, async ({ samlSessionManager }) => {
|
||||
const cookie = await samlSessionManager.getInteractiveUserSessionCookieWithRoleScope(
|
||||
'platform_engineer'
|
||||
);
|
||||
const parsedUrl = new URL(process.env.KIBANA_URL!);
|
||||
const domain = parsedUrl.hostname;
|
||||
const maxAge = 100 * 365 * 24 * 60 * 60;
|
||||
|
||||
const authData = {
|
||||
cookies: [
|
||||
{
|
||||
name: 'sid',
|
||||
value: cookie,
|
||||
expires: maxAge,
|
||||
secure: false,
|
||||
sameSite: 'Lax',
|
||||
domain,
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
fs.mkdirSync(path.dirname(authFile), { recursive: true });
|
||||
fs.writeFileSync(authFile, JSON.stringify(authData, null, 2));
|
||||
});
|
17
x-pack/test/security_solution_playwright/tsconfig.json
Normal file
17
x-pack/test/security_solution_playwright/tsconfig.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node"],
|
||||
"outDir": "target/types"
|
||||
},
|
||||
"include": ["index.d.ts"],
|
||||
"kbn_references": [
|
||||
{
|
||||
"path": "../../plugins/security_solution/tsconfig.json",
|
||||
"force": true
|
||||
},
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
]
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
// @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
import { FtrConfigProviderContext } from '@kbn/test';
|
||||
|
||||
|
|
30
yarn.lock
30
yarn.lock
|
@ -8182,6 +8182,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@playwright/test@=1.46.0":
|
||||
version "1.46.0"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.46.0.tgz#ccea6d22c40ee7fa567e4192fafbdf2a907e2714"
|
||||
integrity sha512-/QYft5VArOrGRP5pgkrfKksqsKA6CEFyGQ/gjNe6q0y4tZ1aaPfq4gIjudr1s3D+pXyrPRdsy4opKDrjBabE5w==
|
||||
dependencies:
|
||||
playwright "1.46.0"
|
||||
|
||||
"@pmmmwh/react-refresh-webpack-plugin@^0.5.3":
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz#58f8217ba70069cc6a73f5d7e05e85b458c150e2"
|
||||
|
@ -16382,7 +16389,7 @@ dotenv-expand@^5.1.0:
|
|||
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
|
||||
integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
|
||||
|
||||
dotenv@^16.0.2, dotenv@^16.0.3:
|
||||
dotenv@^16.0.2, dotenv@^16.0.3, dotenv@^16.4.5:
|
||||
version "16.4.5"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
|
||||
integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==
|
||||
|
@ -25349,6 +25356,13 @@ playwright-chromium@=1.38.1:
|
|||
dependencies:
|
||||
playwright-core "1.38.1"
|
||||
|
||||
playwright-chromium@=1.46.0:
|
||||
version "1.46.0"
|
||||
resolved "https://registry.yarnpkg.com/playwright-chromium/-/playwright-chromium-1.46.0.tgz#f24228fec92b380ccc8f5f365b897e9d88b612f6"
|
||||
integrity sha512-UTHYZsr49XFYRQkpCfaHxL63vfu6uThxR1DrNwnU6qik/OworFcugTOJMWFMoop3QP+ThU8laAMumauLdLZXCQ==
|
||||
dependencies:
|
||||
playwright-core "1.46.0"
|
||||
|
||||
playwright-core@1.38.0:
|
||||
version "1.38.0"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.38.0.tgz#cb8e135da1c0b1918b070642372040ed9aa7009a"
|
||||
|
@ -25359,6 +25373,20 @@ playwright-core@1.38.1, playwright-core@=1.38.1:
|
|||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.38.1.tgz#75a3c470aa9576b7d7c4e274de3d79977448ba08"
|
||||
integrity sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==
|
||||
|
||||
playwright-core@1.46.0:
|
||||
version "1.46.0"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.46.0.tgz#2336ac453a943abf0dc95a76c117f9d3ebd390eb"
|
||||
integrity sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==
|
||||
|
||||
playwright@1.46.0, playwright@=1.46.0:
|
||||
version "1.46.0"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.46.0.tgz#c7ff490deae41fc1e814bf2cb62109dd9351164d"
|
||||
integrity sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==
|
||||
dependencies:
|
||||
playwright-core "1.46.0"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
playwright@=1.38.0:
|
||||
version "1.38.0"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.38.0.tgz#0ee19d38512b7b1f961c0eb44008a6fed373d206"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue