mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* [Osquery] Add Osquery cypress tests to buildkite (#115902) * hardcode agent version Co-authored-by: Patryk Kopyciński <patryk.kopycinski@elastic.co> Co-authored-by: Patryk Kopycinski <contact@patrykkopycinski.com>
This commit is contained in:
parent
e203bf7520
commit
124f057fc5
29 changed files with 1048 additions and 557 deletions
11
.buildkite/pipelines/pull_request/osquery_cypress.yml
Normal file
11
.buildkite/pipelines/pull_request/osquery_cypress.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
steps:
|
||||
- command: .buildkite/scripts/steps/functional/osquery_cypress.sh
|
||||
label: 'Osquery Cypress Tests'
|
||||
agents:
|
||||
queue: ci-group-6
|
||||
depends_on: build
|
||||
timeout_in_minutes: 120
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: '*'
|
||||
limit: 1
|
|
@ -86,6 +86,16 @@ const uploadPipeline = (pipelineContent) => {
|
|||
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/fleet_cypress.yml'));
|
||||
}
|
||||
|
||||
if (
|
||||
(await doAnyChangesMatch([
|
||||
/^x-pack\/plugins\/osquery/,
|
||||
/^x-pack\/test\/osquery_cypress/,
|
||||
])) ||
|
||||
process.env.GITHUB_PR_LABELS.includes('ci:all-cypress-suites')
|
||||
) {
|
||||
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/osquery_cypress.yml'));
|
||||
}
|
||||
|
||||
if (await doAnyChangesMatch([/^x-pack\/plugins\/uptime/])) {
|
||||
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/uptime.yml'));
|
||||
}
|
||||
|
|
20
.buildkite/scripts/steps/functional/osquery_cypress.sh
Executable file
20
.buildkite/scripts/steps/functional/osquery_cypress.sh
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/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-osquery-cypress
|
||||
|
||||
echo "--- Osquery Cypress tests"
|
||||
|
||||
cd "$XPACK_DIR"
|
||||
|
||||
checks-reporter-with-killswitch "Osquery Cypress Tests" \
|
||||
node scripts/functional_tests \
|
||||
--debug --bail \
|
||||
--kibana-install-dir "$KIBANA_BUILD_LOCATION" \
|
||||
--config test/osquery_cypress/cli_config.ts
|
|
@ -83,7 +83,7 @@
|
|||
"**/hoist-non-react-statics": "^3.3.2",
|
||||
"**/html-minifier/uglify-js": "^3.14.3",
|
||||
"**/isomorphic-fetch/node-fetch": "^2.6.1",
|
||||
"**/istanbul-instrumenter-loader/schema-utils": "1.0.0",
|
||||
"**/istanbul-lib-coverage": "^3.2.0",
|
||||
"**/json-schema": "^0.4.0",
|
||||
"**/minimist": "^1.2.5",
|
||||
"**/node-jose/node-forge": "^0.10.0",
|
||||
|
@ -433,6 +433,7 @@
|
|||
"@babel/types": "^7.16.0",
|
||||
"@bazel/ibazel": "^0.15.10",
|
||||
"@bazel/typescript": "^3.8.0",
|
||||
"@cypress/code-coverage": "^3.9.11",
|
||||
"@cypress/snapshot": "^2.1.7",
|
||||
"@cypress/webpack-preprocessor": "^5.6.0",
|
||||
"@elastic/eslint-config-kibana": "link:bazel-bin/packages/elastic-eslint-config-kibana",
|
||||
|
@ -696,7 +697,9 @@
|
|||
"cypress-file-upload": "^5.0.8",
|
||||
"cypress-multi-reporters": "^1.5.0",
|
||||
"cypress-pipe": "^2.0.0",
|
||||
"cypress-react-selector": "^2.3.13",
|
||||
"cypress-real-events": "^1.5.1",
|
||||
"cypress-recurse": "^1.13.1",
|
||||
"debug": "^2.6.9",
|
||||
"delete-empty": "^2.0.0",
|
||||
"dependency-check": "^4.1.0",
|
||||
|
@ -751,7 +754,6 @@
|
|||
"http-proxy": "^1.18.1",
|
||||
"is-glob": "^4.0.1",
|
||||
"is-path-inside": "^3.0.2",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jest": "^26.6.3",
|
||||
"jest-canvas-mock": "^2.3.1",
|
||||
"jest-circus": "^26.6.3",
|
||||
|
@ -788,7 +790,7 @@
|
|||
"ncp": "^2.0.0",
|
||||
"node-sass": "^6.0.1",
|
||||
"null-loader": "^3.0.0",
|
||||
"nyc": "^15.0.1",
|
||||
"nyc": "^15.1.0",
|
||||
"oboe": "^2.1.4",
|
||||
"parse-link-header": "^1.0.1",
|
||||
"pbf": "3.2.1",
|
||||
|
|
15
test/scripts/jenkins_osquery_cypress.sh
Executable file
15
test/scripts/jenkins_osquery_cypress.sh
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source test/scripts/jenkins_test_setup_xpack.sh
|
||||
|
||||
echo " -> Running osquery cypress tests"
|
||||
cd "$XPACK_DIR"
|
||||
|
||||
checks-reporter-with-killswitch "Osquery Cypress Tests" \
|
||||
node scripts/functional_tests \
|
||||
--debug --bail \
|
||||
--kibana-install-dir "$KIBANA_INSTALL_DIR" \
|
||||
--config test/osquery_cypress/cli_config.ts
|
||||
|
||||
echo ""
|
||||
echo ""
|
|
@ -173,6 +173,14 @@ def functionalXpack(Map params = [:]) {
|
|||
}
|
||||
}
|
||||
|
||||
whenChanged([
|
||||
'x-pack/plugins/osquery/',
|
||||
]) {
|
||||
if (githubPr.isPr()) {
|
||||
task(kibanaPipeline.functionalTestProcess('xpack-osqueryCypress', './test/scripts/jenkins_osquery_cypress.sh'))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
4
x-pack/plugins/osquery/.nycrc
Normal file
4
x-pack/plugins/osquery/.nycrc
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"excludeAfterRemap": true,
|
||||
"include": ["public"]
|
||||
}
|
|
@ -1,14 +1,16 @@
|
|||
{
|
||||
"baseUrl": "http://localhost:5620",
|
||||
"defaultCommandTimeout": 60000,
|
||||
"execTimeout": 120000,
|
||||
"pageLoadTimeout": 120000,
|
||||
"nodeVersion": "system",
|
||||
"defaultCommandTimeout": 6000,
|
||||
"execTimeout": 12000,
|
||||
"pageLoadTimeout": 12000,
|
||||
"retries": {
|
||||
"runMode": 2
|
||||
},
|
||||
"screenshotsFolder": "../../../target/kibana-osquery/cypress/screenshots",
|
||||
"trashAssetsBeforeRuns": false,
|
||||
"video": false,
|
||||
"videosFolder": "../../../target/kibana-osquery/cypress/videos",
|
||||
"viewportHeight": 900,
|
||||
"viewportWidth": 1440
|
||||
}
|
||||
"viewportWidth": 1440,
|
||||
"experimentalStudio": true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 { FLEET_AGENT_POLICIES, navigateTo } from '../tasks/navigation';
|
||||
import { addIntegration } from '../tasks/integrations';
|
||||
import { checkResults, inputQuery, selectAllAgents, submitQuery } from '../tasks/live_query';
|
||||
import { login } from '../tasks/login';
|
||||
|
||||
describe('Add Integration', () => {
|
||||
const integration = 'Osquery Manager';
|
||||
|
||||
before(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
it.skip('should open Osquery app', () => {
|
||||
cy.visit('/app/osquery/live_queries');
|
||||
cy.wait(3000);
|
||||
cy.contains('Live queries history', { timeout: 60000 });
|
||||
cy.contains('New live query').click();
|
||||
cy.wait(3000);
|
||||
cy.contains('Saved queries').click();
|
||||
cy.wait(3000);
|
||||
cy.contains('Saved queries', { timeout: 60000 });
|
||||
cy.contains('Add saved query').click();
|
||||
cy.wait(3000);
|
||||
cy.contains('Packs').click();
|
||||
cy.wait(3000);
|
||||
cy.contains('Packs', { timeout: 60000 });
|
||||
cy.contains('Add pack').click();
|
||||
cy.wait(3000);
|
||||
});
|
||||
|
||||
it('should display Osquery integration in the Policies list once installed ', () => {
|
||||
addAndVerifyIntegration();
|
||||
});
|
||||
|
||||
it.skip('should run live query', () => {
|
||||
navigateTo('/app/osquery/live_queries/new');
|
||||
cy.wait(1000);
|
||||
selectAllAgents();
|
||||
inputQuery();
|
||||
submitQuery();
|
||||
checkResults();
|
||||
});
|
||||
|
||||
function addAndVerifyIntegration() {
|
||||
navigateTo(FLEET_AGENT_POLICIES);
|
||||
cy.contains('Default Fleet Server policy').click();
|
||||
cy.contains('Add integration').click();
|
||||
|
||||
cy.contains(integration).click();
|
||||
addIntegration();
|
||||
cy.contains('osquery_manager-');
|
||||
}
|
||||
});
|
|
@ -1,35 +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.
|
||||
*/
|
||||
|
||||
import { HEADER } from '../screens/osquery';
|
||||
import { OSQUERY_NAVIGATION_LINK } from '../screens/navigation';
|
||||
|
||||
import { OSQUERY, NEW_LIVE_QUERY, openNavigationFlyout, navigateTo } from '../tasks/navigation';
|
||||
import { addIntegration } from '../tasks/integrations';
|
||||
import { checkResults, inputQuery, selectAllAgents, submitQuery } from '../tasks/live_query';
|
||||
|
||||
describe('Osquery Manager', () => {
|
||||
before(() => addIntegration(Cypress.env('OSQUERY_POLICY')));
|
||||
|
||||
it('Runs live queries', () => {
|
||||
navigateTo(NEW_LIVE_QUERY);
|
||||
selectAllAgents();
|
||||
inputQuery();
|
||||
submitQuery();
|
||||
checkResults();
|
||||
});
|
||||
|
||||
it('Displays Osquery on the navigation flyout once installed ', () => {
|
||||
openNavigationFlyout();
|
||||
cy.get(OSQUERY_NAVIGATION_LINK).should('exist');
|
||||
});
|
||||
|
||||
it('Displays Live queries history title when navigating to Osquery', () => {
|
||||
navigateTo(OSQUERY);
|
||||
cy.get(HEADER).should('have.text', 'Live queries history');
|
||||
});
|
||||
});
|
|
@ -4,8 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
// / <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
|
@ -22,8 +21,11 @@
|
|||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
module.exports = (_on, _config) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
module.exports = (on: any, config: any) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies
|
||||
require('@cypress/code-coverage/task')(on, config);
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
return config;
|
||||
};
|
11
x-pack/plugins/osquery/cypress/screens/fleet.ts
Normal file
11
x-pack/plugins/osquery/cypress/screens/fleet.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 const ADD_AGENT_BUTTON = 'addAgentButton';
|
||||
|
||||
export const AGENT_POLICIES_TAB = 'fleet-agent-policies-tab';
|
||||
export const ENROLLMENT_TOKENS_TAB = 'fleet-enrollment-tokens-tab';
|
|
@ -5,7 +5,22 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export const ADD_POLICY_BTN = '[data-test-subj="addIntegrationPolicyButton"]';
|
||||
export const CREATE_PACKAGE_POLICY_SAVE_BTN = '[data-test-subj="createPackagePolicySaveButton"]';
|
||||
export const ADD_POLICY_BTN = 'addIntegrationPolicyButton';
|
||||
export const CREATE_PACKAGE_POLICY_SAVE_BTN = 'createPackagePolicySaveButton';
|
||||
|
||||
export const DATA_COLLECTION_SETUP_STEP = 'dataCollectionSetupStep';
|
||||
export const INTEGRATIONS_CARD = '.euiCard__titleAnchor';
|
||||
|
||||
export const INTEGRATION_NAME_LINK = 'integrationNameLink';
|
||||
|
||||
export const CONFIRM_MODAL_BTN = 'confirmModalConfirmButton';
|
||||
export const CONFIRM_MODAL_BTN_SEL = `[data-test-subj=${CONFIRM_MODAL_BTN}]`;
|
||||
|
||||
export const SETTINGS_TAB = 'tab-settings';
|
||||
export const POLICIES_TAB = 'tab-policies';
|
||||
|
||||
export const UPDATE_PACKAGE_BTN = 'updatePackageBtn';
|
||||
export const LATEST_VERSION = 'latestVersion';
|
||||
|
||||
export const PACKAGE_VERSION = 'packageVersionText';
|
||||
export const SAVE_PACKAGE_CONFIRM = '[data-test-subj=confirmModalConfirmButton]';
|
||||
|
|
271
x-pack/plugins/osquery/cypress/support/coverage.ts
Normal file
271
x-pack/plugins/osquery/cypress/support/coverage.ts
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
||||
// / <reference types="cypress" />
|
||||
// @ts-check
|
||||
|
||||
const dayjs = require('dayjs');
|
||||
const duration = require('dayjs/plugin/duration');
|
||||
// const { filterSpecsFromCoverage } = require('./support-utils');
|
||||
|
||||
dayjs.extend(duration);
|
||||
|
||||
|
||||
/**
|
||||
* Sends collected code coverage object to the backend code
|
||||
* via "cy.task".
|
||||
*/
|
||||
const sendCoverage = (coverage: any, pathname = '/') => {
|
||||
logMessage(`Saving code coverage for **${pathname}**`);
|
||||
|
||||
// const withoutSpecs = filterSpecsFromCoverage(coverage);
|
||||
const appCoverageOnly = filterSupportFilesFromCoverage(coverage);
|
||||
|
||||
// stringify coverage object for speed
|
||||
cy.task('combineCoverage', JSON.stringify(appCoverageOnly), {
|
||||
log: false,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Consistently logs the given string to the Command Log
|
||||
* so the user knows the log message is coming from this plugin.
|
||||
* @param {string} s Message to log.
|
||||
*/
|
||||
const logMessage = (s: string) => {
|
||||
cy.log(`${s} \`[@cypress/code-coverage]\``);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes support file from the coverage object.
|
||||
* If there are more files loaded from support folder, also removes them
|
||||
*/
|
||||
const filterSupportFilesFromCoverage = (totalCoverage: any) => {
|
||||
const integrationFolder = Cypress.config('integrationFolder');
|
||||
const supportFile = Cypress.config('supportFile');
|
||||
|
||||
/** @type {string} Cypress run-time config has the support folder string */
|
||||
// @ts-ignore
|
||||
const supportFolder = Cypress.config('supportFolder');
|
||||
|
||||
const isSupportFile = (filename: string) => filename === supportFile;
|
||||
|
||||
let coverage = Cypress._.omitBy(totalCoverage, (fileCoverage, filename) =>
|
||||
isSupportFile(filename)
|
||||
);
|
||||
|
||||
// check the edge case
|
||||
// if we have files from support folder AND the support folder is not same
|
||||
// as the integration, or its prefix (this might remove all app source files)
|
||||
// then remove all files from the support folder
|
||||
if (!integrationFolder.startsWith(supportFolder)) {
|
||||
// remove all covered files from support folder
|
||||
coverage = Cypress._.omitBy(totalCoverage, (fileCoverage, filename) =>
|
||||
filename.startsWith(supportFolder)
|
||||
);
|
||||
}
|
||||
return coverage;
|
||||
};
|
||||
|
||||
const registerHooks = () => {
|
||||
let windowCoverageObjects: any[];
|
||||
|
||||
const hasE2ECoverage = () => Boolean(windowCoverageObjects.length);
|
||||
|
||||
// @ts-ignore
|
||||
const hasUnitTestCoverage = () => Boolean(window.__coverage__);
|
||||
|
||||
before(() => {
|
||||
// we need to reset the coverage when running
|
||||
// in the interactive mode, otherwise the counters will
|
||||
// keep increasing every time we rerun the tests
|
||||
const logInstance = Cypress.log({
|
||||
name: 'Coverage',
|
||||
message: ['Reset [@cypress/code-coverage]'],
|
||||
});
|
||||
|
||||
cy.task(
|
||||
'resetCoverage',
|
||||
{
|
||||
// @ts-ignore
|
||||
isInteractive: Cypress.config('isInteractive'),
|
||||
},
|
||||
{ log: false }
|
||||
).then(() => {
|
||||
logInstance.end();
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// each object will have the coverage and url pathname
|
||||
// to let the user know the coverage has been collected
|
||||
windowCoverageObjects = [];
|
||||
|
||||
const saveCoverageObject = (win: any) => {
|
||||
console.log('wwwww', win, win.windows?.__coverage__, win.__coverage__);
|
||||
// if application code has been instrumented, the app iframe "window" has an object
|
||||
const applicationSourceCoverage = win.__coverage__;
|
||||
if (!applicationSourceCoverage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
Cypress._.find(windowCoverageObjects, {
|
||||
coverage: applicationSourceCoverage,
|
||||
})
|
||||
) {
|
||||
// this application code coverage object is already known
|
||||
// which can happen when combining `window:load` and `before` callbacks
|
||||
return;
|
||||
}
|
||||
|
||||
windowCoverageObjects.push({
|
||||
coverage: applicationSourceCoverage,
|
||||
pathname: win.location.pathname,
|
||||
});
|
||||
};
|
||||
|
||||
// save reference to coverage for each app window loaded in the test
|
||||
cy.on('window:load', saveCoverageObject);
|
||||
|
||||
// save reference if visiting a page inside a before() hook
|
||||
cy.window({ log: false }).then(saveCoverageObject);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// save coverage after the test
|
||||
// because now the window coverage objects have been updated
|
||||
windowCoverageObjects.forEach((cover) => {
|
||||
sendCoverage(cover.coverage, cover.pathname);
|
||||
});
|
||||
|
||||
if (!hasE2ECoverage()) {
|
||||
if (hasUnitTestCoverage()) {
|
||||
logMessage(`👉 Only found unit test code coverage.`);
|
||||
} else {
|
||||
const expectBackendCoverageOnly = Cypress._.get(
|
||||
Cypress.env('codeCoverage'),
|
||||
'expectBackendCoverageOnly',
|
||||
false
|
||||
);
|
||||
if (!expectBackendCoverageOnly) {
|
||||
logMessage(`
|
||||
⚠️ Could not find any coverage information in your application
|
||||
by looking at the window coverage object.
|
||||
Did you forget to instrument your application?
|
||||
See [code-coverage#instrument-your-application](https://github.com/cypress-io/code-coverage#instrument-your-application)
|
||||
`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
after(() => {
|
||||
// I wish I could fail the tests if there is no code coverage information
|
||||
// but throwing an error here does not fail the test run due to
|
||||
// https://github.com/cypress-io/cypress/issues/2296
|
||||
|
||||
// there might be server-side code coverage information
|
||||
// we should grab it once after all tests finish
|
||||
// @ts-ignore
|
||||
const baseUrl = Cypress.config('baseUrl') || cy.state('window').origin;
|
||||
// @ts-ignore
|
||||
const runningEndToEndTests = baseUrl !== Cypress.config('proxyUrl');
|
||||
const specType = Cypress._.get(Cypress.spec, 'specType', 'integration');
|
||||
const isIntegrationSpec = specType === 'integration';
|
||||
|
||||
if (runningEndToEndTests && isIntegrationSpec) {
|
||||
// we can only request server-side code coverage
|
||||
// if we are running end-to-end tests,
|
||||
// otherwise where do we send the request?
|
||||
const url = Cypress._.get(Cypress.env('codeCoverage'), 'url', '/__coverage__');
|
||||
cy.request({
|
||||
url,
|
||||
log: false,
|
||||
failOnStatusCode: false,
|
||||
})
|
||||
.then((r) => Cypress._.get(r, 'body.coverage', null))
|
||||
.then((coverage) => {
|
||||
if (!coverage) {
|
||||
// we did not get code coverage - this is the
|
||||
// original failed request
|
||||
const expectBackendCoverageOnly = Cypress._.get(
|
||||
Cypress.env('codeCoverage'),
|
||||
'expectBackendCoverageOnly',
|
||||
false
|
||||
);
|
||||
if (expectBackendCoverageOnly) {
|
||||
throw new Error(`Expected to collect backend code coverage from ${url}`);
|
||||
} else {
|
||||
// we did not really expect to collect the backend code coverage
|
||||
return;
|
||||
}
|
||||
}
|
||||
sendCoverage(coverage, 'backend');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
after(() => {
|
||||
// collect and merge frontend coverage
|
||||
|
||||
// if spec bundle has been instrumented (using Cypress preprocessor)
|
||||
// then we will have unit test coverage
|
||||
// NOTE: spec iframe is NOT reset between the tests, so we can grab
|
||||
// the coverage information only once after all tests have finished
|
||||
// @ts-ignore
|
||||
const unitTestCoverage = window.__coverage__;
|
||||
if (unitTestCoverage) {
|
||||
sendCoverage(unitTestCoverage, 'unit');
|
||||
}
|
||||
});
|
||||
|
||||
after(() => {
|
||||
// when all tests finish, lets generate the coverage report
|
||||
const logInstance = Cypress.log({
|
||||
name: 'Coverage',
|
||||
message: ['Generating report [@cypress/code-coverage]'],
|
||||
});
|
||||
cy.task('coverageReport', null, {
|
||||
timeout: dayjs.duration(3, 'minutes').asMilliseconds(),
|
||||
log: false,
|
||||
}).then((coverageReportFolder) => {
|
||||
logInstance.set('consoleProps', () => ({
|
||||
'coverage report folder': coverageReportFolder,
|
||||
}));
|
||||
logInstance.end();
|
||||
return coverageReportFolder;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// to disable code coverage commands and save time
|
||||
// pass environment variable coverage=false
|
||||
// cypress run --env coverage=false
|
||||
// or
|
||||
// CYPRESS_coverage=false cypress run
|
||||
// see https://on.cypress.io/environment-variables
|
||||
|
||||
// to avoid "coverage" env variable being case-sensitive, convert to lowercase
|
||||
const cyEnvs = Cypress._.mapKeys(Cypress.env(), (value, key) => key.toLowerCase());
|
||||
|
||||
if (cyEnvs.coverage === false) {
|
||||
console.log('Skipping code coverage hooks');
|
||||
} else if (Cypress.env('codeCoverageTasksRegistered') !== true) {
|
||||
// register a hook just to log a message
|
||||
before(() => {
|
||||
logMessage(`
|
||||
⚠️ Code coverage tasks were not registered by the plugins file.
|
||||
See [support issue](https://github.com/cypress-io/code-coverage/issues/179)
|
||||
for possible workarounds.
|
||||
`);
|
||||
});
|
||||
} else {
|
||||
registerHooks();
|
||||
}
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
// / <reference types="cypress" />
|
||||
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
|
@ -22,6 +24,23 @@
|
|||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
// import './coverage';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
getBySel: typeof cy.get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function getBySel(selector: string, ...args: any[]) {
|
||||
return cy.get(`[data-test-subj=${selector}]`, ...args);
|
||||
}
|
||||
|
||||
Cypress.Commands.add('getBySel', getBySel);
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
|
|
@ -5,18 +5,54 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CREATE_PACKAGE_POLICY_SAVE_BTN, SAVE_PACKAGE_CONFIRM } from '../screens/integrations';
|
||||
import {
|
||||
ADD_POLICY_BTN,
|
||||
CONFIRM_MODAL_BTN,
|
||||
CONFIRM_MODAL_BTN_SEL,
|
||||
CREATE_PACKAGE_POLICY_SAVE_BTN,
|
||||
DATA_COLLECTION_SETUP_STEP,
|
||||
} from '../screens/integrations';
|
||||
|
||||
import { navigateTo, OSQUERY_INTEGRATION_PAGE } from './navigation';
|
||||
export const addIntegration = () => {
|
||||
cy.getBySel(ADD_POLICY_BTN).click();
|
||||
cy.getBySel(DATA_COLLECTION_SETUP_STEP).find('.euiLoadingSpinner').should('not.exist');
|
||||
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN).click();
|
||||
// sometimes agent is assigned to default policy, sometimes not
|
||||
closeModalIfVisible();
|
||||
|
||||
// TODO: allow adding integration version strings to this
|
||||
export const addIntegration = (policyId: string) => {
|
||||
navigateTo(OSQUERY_INTEGRATION_PAGE, { qs: { policyId } });
|
||||
cy.get(CREATE_PACKAGE_POLICY_SAVE_BTN).click();
|
||||
cy.get(SAVE_PACKAGE_CONFIRM).click();
|
||||
// XXX: there is a race condition between the test going to the ui powered by the agent, and the agent having the integration ready to go
|
||||
// so we wait.
|
||||
// TODO: actually make this wait til the agent has been updated with the proper integration
|
||||
cy.wait(5000);
|
||||
return cy.reload();
|
||||
cy.getBySel(CREATE_PACKAGE_POLICY_SAVE_BTN, { timeout: 60000 }).should('not.exist');
|
||||
};
|
||||
|
||||
function closeModalIfVisible() {
|
||||
cy.get('body').then(($body) => {
|
||||
if ($body.find(CONFIRM_MODAL_BTN_SEL).length) {
|
||||
cy.getBySel(CONFIRM_MODAL_BTN).click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const deleteIntegrations = async (integrationName: string) => {
|
||||
const ids: string[] = [];
|
||||
cy.contains(integrationName)
|
||||
.each(($a) => {
|
||||
const href = $a.attr('href') as string;
|
||||
ids.push(href.substr(href.lastIndexOf('/') + 1));
|
||||
})
|
||||
.then(() => {
|
||||
cy.request({
|
||||
url: `/api/fleet/package_policies/delete`,
|
||||
headers: { 'kbn-xsrf': 'cypress' },
|
||||
body: `{ "packagePolicyIds": ${JSON.stringify(ids)} }`,
|
||||
method: 'POST',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const installPackageWithVersion = (integration: string, version: string) => {
|
||||
cy.request({
|
||||
url: `/api/fleet/epm/packages/${integration}-${version}`,
|
||||
headers: { 'kbn-xsrf': 'cypress' },
|
||||
body: '{ "force": true }',
|
||||
method: 'POST',
|
||||
});
|
||||
};
|
||||
|
|
|
@ -5,12 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
AGENT_FIELD,
|
||||
ALL_AGENTS_OPTION,
|
||||
LIVE_QUERY_EDITOR,
|
||||
SUBMIT_BUTTON,
|
||||
} from '../screens/live_query';
|
||||
import { AGENT_FIELD, ALL_AGENTS_OPTION, LIVE_QUERY_EDITOR } from '../screens/live_query';
|
||||
|
||||
export const selectAllAgents = () => {
|
||||
cy.get(AGENT_FIELD).first().click();
|
||||
|
@ -19,7 +14,7 @@ export const selectAllAgents = () => {
|
|||
|
||||
export const inputQuery = () => cy.get(LIVE_QUERY_EDITOR).type('select * from processes;');
|
||||
|
||||
export const submitQuery = () => cy.get(SUBMIT_BUTTON).contains('Submit').click();
|
||||
export const submitQuery = () => cy.contains('Submit').click();
|
||||
|
||||
export const checkResults = () =>
|
||||
cy.get('[data-test-subj="dataGridRowCell"]').should('have.lengthOf.above', 0);
|
||||
cy.get('[data-test-subj="dataGridRowCell"]', { timeout: 60000 }).should('have.lengthOf.above', 0);
|
||||
|
|
295
x-pack/plugins/osquery/cypress/tasks/login.ts
Normal file
295
x-pack/plugins/osquery/cypress/tasks/login.ts
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* 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 * as yaml from 'js-yaml';
|
||||
import Url, { UrlObject } from 'url';
|
||||
import { ROLES } from '../test';
|
||||
|
||||
/**
|
||||
* Credentials in the `kibana.dev.yml` config file will be used to authenticate
|
||||
* with Kibana when credentials are not provided via environment variables
|
||||
*/
|
||||
const KIBANA_DEV_YML_PATH = '../../../config/kibana.dev.yml';
|
||||
|
||||
/**
|
||||
* The configuration path in `kibana.dev.yml` to the username to be used when
|
||||
* authenticating with Kibana.
|
||||
*/
|
||||
const ELASTICSEARCH_USERNAME_CONFIG_PATH = 'config.elasticsearch.username';
|
||||
|
||||
/**
|
||||
* The configuration path in `kibana.dev.yml` to the password to be used when
|
||||
* authenticating with Kibana.
|
||||
*/
|
||||
const ELASTICSEARCH_PASSWORD_CONFIG_PATH = 'config.elasticsearch.password';
|
||||
|
||||
/**
|
||||
* The `CYPRESS_ELASTICSEARCH_USERNAME` environment variable specifies the
|
||||
* username to be used when authenticating with Kibana
|
||||
*/
|
||||
const ELASTICSEARCH_USERNAME = 'ELASTICSEARCH_USERNAME';
|
||||
|
||||
/**
|
||||
* The `CYPRESS_ELASTICSEARCH_PASSWORD` environment variable specifies the
|
||||
* username to be used when authenticating with Kibana
|
||||
*/
|
||||
const ELASTICSEARCH_PASSWORD = 'ELASTICSEARCH_PASSWORD';
|
||||
|
||||
/**
|
||||
* The Kibana server endpoint used for authentication
|
||||
*/
|
||||
const LOGIN_API_ENDPOINT = '/internal/security/login';
|
||||
|
||||
/**
|
||||
* cy.visit will default to the baseUrl which uses the default kibana test user
|
||||
* This function will override that functionality in cy.visit by building the baseUrl
|
||||
* directly from the environment variables set up in x-pack/test/security_solution_cypress/runner.ts
|
||||
*
|
||||
* @param role string role/user to log in with
|
||||
* @param route string route to visit
|
||||
*/
|
||||
export const getUrlWithRoute = (role: ROLES, route: string) => {
|
||||
const url = Cypress.config().baseUrl;
|
||||
const kibana = new URL(String(url));
|
||||
const theUrl = `${Url.format({
|
||||
auth: `${role}:changeme`,
|
||||
username: role,
|
||||
password: 'changeme',
|
||||
protocol: kibana.protocol.replace(':', ''),
|
||||
hostname: kibana.hostname,
|
||||
port: kibana.port,
|
||||
} as UrlObject)}${route.startsWith('/') ? '' : '/'}${route}`;
|
||||
cy.log(`origin: ${theUrl}`);
|
||||
return theUrl;
|
||||
};
|
||||
|
||||
interface User {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a URL with basic auth using the passed in user.
|
||||
*
|
||||
* @param user the user information to build the basic auth with
|
||||
* @param route string route to visit
|
||||
*/
|
||||
export const constructUrlWithUser = (user: User, route: string) => {
|
||||
const url = Cypress.config().baseUrl;
|
||||
const kibana = new URL(String(url));
|
||||
const hostname = kibana.hostname;
|
||||
const username = user.username;
|
||||
const password = user.password;
|
||||
const protocol = kibana.protocol.replace(':', '');
|
||||
const port = kibana.port;
|
||||
|
||||
const path = `${route.startsWith('/') ? '' : '/'}${route}`;
|
||||
const strUrl = `${protocol}://${username}:${password}@${hostname}:${port}${path}`;
|
||||
const builtUrl = new URL(strUrl);
|
||||
|
||||
cy.log(`origin: ${builtUrl.href}`);
|
||||
return builtUrl.href;
|
||||
};
|
||||
|
||||
export const getCurlScriptEnvVars = () => ({
|
||||
ELASTICSEARCH_URL: Cypress.env('ELASTICSEARCH_URL'),
|
||||
ELASTICSEARCH_USERNAME: Cypress.env('ELASTICSEARCH_USERNAME'),
|
||||
ELASTICSEARCH_PASSWORD: Cypress.env('ELASTICSEARCH_PASSWORD'),
|
||||
KIBANA_URL: Cypress.config().baseUrl,
|
||||
});
|
||||
|
||||
export const postRoleAndUser = (role: ROLES) => {
|
||||
const env = getCurlScriptEnvVars();
|
||||
const detectionsRoleScriptPath = `./server/lib/detection_engine/scripts/roles_users/${role}/post_detections_role.sh`;
|
||||
const detectionsRoleJsonPath = `./server/lib/detection_engine/scripts/roles_users/${role}/detections_role.json`;
|
||||
const detectionsUserScriptPath = `./server/lib/detection_engine/scripts/roles_users/${role}/post_detections_user.sh`;
|
||||
const detectionsUserJsonPath = `./server/lib/detection_engine/scripts/roles_users/${role}/detections_user.json`;
|
||||
|
||||
// post the role
|
||||
cy.exec(`bash ${detectionsRoleScriptPath} ${detectionsRoleJsonPath}`, {
|
||||
env,
|
||||
});
|
||||
|
||||
// post the user associated with the role to elasticsearch
|
||||
cy.exec(`bash ${detectionsUserScriptPath} ${detectionsUserJsonPath}`, {
|
||||
env,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteRoleAndUser = (role: ROLES) => {
|
||||
const env = getCurlScriptEnvVars();
|
||||
const detectionsUserDeleteScriptPath = `./server/lib/detection_engine/scripts/roles_users/${role}/delete_detections_user.sh`;
|
||||
|
||||
// delete the role
|
||||
cy.exec(`bash ${detectionsUserDeleteScriptPath}`, {
|
||||
env,
|
||||
});
|
||||
};
|
||||
|
||||
export const loginWithUser = (user: User) => {
|
||||
const url = Cypress.config().baseUrl;
|
||||
|
||||
cy.request({
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: url && !url.includes('localhost') ? 'cloud-basic' : 'basic',
|
||||
currentURL: '/',
|
||||
params: {
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
},
|
||||
},
|
||||
headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
|
||||
method: 'POST',
|
||||
url: constructUrlWithUser(user, LOGIN_API_ENDPOINT),
|
||||
});
|
||||
};
|
||||
|
||||
export const loginWithRole = async (role: ROLES) => {
|
||||
postRoleAndUser(role);
|
||||
const theUrl = Url.format({
|
||||
auth: `${role}:changeme`,
|
||||
username: role,
|
||||
password: 'changeme',
|
||||
protocol: Cypress.env('protocol'),
|
||||
hostname: Cypress.env('hostname'),
|
||||
port: Cypress.env('configport'),
|
||||
} as UrlObject);
|
||||
cy.log(`origin: ${theUrl}`);
|
||||
cy.request({
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: 'basic',
|
||||
currentURL: '/',
|
||||
params: {
|
||||
username: role,
|
||||
password: 'changeme',
|
||||
},
|
||||
},
|
||||
headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
|
||||
method: 'POST',
|
||||
url: getUrlWithRoute(role, LOGIN_API_ENDPOINT),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Authenticates with Kibana using, if specified, credentials specified by
|
||||
* environment variables. The credentials in `kibana.dev.yml` will be used
|
||||
* for authentication when the environment variables are unset.
|
||||
*
|
||||
* To speed the execution of tests, prefer this non-interactive authentication,
|
||||
* which is faster than authentication via Kibana's interactive login page.
|
||||
*/
|
||||
export const login = (role?: ROLES) => {
|
||||
if (role != null) {
|
||||
loginWithRole(role);
|
||||
} else if (credentialsProvidedByEnvironment()) {
|
||||
loginViaEnvironmentCredentials();
|
||||
} else {
|
||||
loginViaConfig();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns `true` if the credentials used to login to Kibana are provided
|
||||
* via environment variables
|
||||
*/
|
||||
const credentialsProvidedByEnvironment = (): boolean =>
|
||||
Cypress.env(ELASTICSEARCH_USERNAME) != null && Cypress.env(ELASTICSEARCH_PASSWORD) != null;
|
||||
|
||||
/**
|
||||
* Authenticates with Kibana by reading credentials from the
|
||||
* `CYPRESS_ELASTICSEARCH_USERNAME` and `CYPRESS_ELASTICSEARCH_PASSWORD`
|
||||
* environment variables, and POSTing the username and password directly to
|
||||
* Kibana's `/internal/security/login` endpoint, bypassing the login page (for speed).
|
||||
*/
|
||||
const loginViaEnvironmentCredentials = () => {
|
||||
const url = Cypress.config().baseUrl;
|
||||
|
||||
cy.log(
|
||||
`Authenticating via environment credentials from the \`CYPRESS_${ELASTICSEARCH_USERNAME}\` and \`CYPRESS_${ELASTICSEARCH_PASSWORD}\` environment variables`
|
||||
);
|
||||
|
||||
// programmatically authenticate without interacting with the Kibana login page
|
||||
cy.request({
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: url && !url.includes('localhost') ? 'cloud-basic' : 'basic',
|
||||
currentURL: '/',
|
||||
params: {
|
||||
username: Cypress.env(ELASTICSEARCH_USERNAME),
|
||||
password: Cypress.env(ELASTICSEARCH_PASSWORD),
|
||||
},
|
||||
},
|
||||
headers: { 'kbn-xsrf': 'cypress-creds-via-env' },
|
||||
method: 'POST',
|
||||
url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Authenticates with Kibana by reading credentials from the
|
||||
* `kibana.dev.yml` file and POSTing the username and password directly to
|
||||
* Kibana's `/internal/security/login` endpoint, bypassing the login page (for speed).
|
||||
*/
|
||||
const loginViaConfig = () => {
|
||||
cy.log(
|
||||
`Authenticating via config credentials \`${ELASTICSEARCH_USERNAME_CONFIG_PATH}\` and \`${ELASTICSEARCH_PASSWORD_CONFIG_PATH}\` from \`${KIBANA_DEV_YML_PATH}\``
|
||||
);
|
||||
|
||||
// read the login details from `kibana.dev.yaml`
|
||||
cy.readFile(KIBANA_DEV_YML_PATH).then((kibanaDevYml) => {
|
||||
const config = yaml.safeLoad(kibanaDevYml);
|
||||
|
||||
// programmatically authenticate without interacting with the Kibana login page
|
||||
cy.request({
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: 'basic',
|
||||
currentURL: '/',
|
||||
params: {
|
||||
username: 'elastic',
|
||||
password: config.elasticsearch.password,
|
||||
},
|
||||
},
|
||||
headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
|
||||
method: 'POST',
|
||||
url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the configured auth details that were used to spawn cypress
|
||||
*
|
||||
* @returns the default Elasticsearch username and password for this environment
|
||||
*/
|
||||
export const getEnvAuth = (): User => {
|
||||
if (credentialsProvidedByEnvironment()) {
|
||||
return {
|
||||
username: Cypress.env(ELASTICSEARCH_USERNAME),
|
||||
password: Cypress.env(ELASTICSEARCH_PASSWORD),
|
||||
};
|
||||
} else {
|
||||
let user: User = { username: '', password: '' };
|
||||
cy.readFile(KIBANA_DEV_YML_PATH).then((devYml) => {
|
||||
const config = yaml.safeLoad(devYml);
|
||||
user = { username: config.elasticsearch.username, password: config.elasticsearch.password };
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Authenticates with Kibana, visits the specified `url`, and waits for the
|
||||
* Kibana global nav to be displayed before continuing
|
||||
*/
|
||||
export const loginAndWaitForPage = (url: string) => {
|
||||
login();
|
||||
cy.visit(url);
|
||||
};
|
|
@ -7,7 +7,10 @@
|
|||
|
||||
import { TOGGLE_NAVIGATION_BTN } from '../screens/navigation';
|
||||
|
||||
export const OSQUERY = 'app/osquery/live_queries';
|
||||
export const INTEGRATIONS = 'app/integrations#/';
|
||||
export const FLEET = 'app/fleet/';
|
||||
export const FLEET_AGENT_POLICIES = 'app/fleet/policies';
|
||||
export const OSQUERY = 'app/osquery';
|
||||
export const NEW_LIVE_QUERY = 'app/osquery/live_queries/new';
|
||||
export const OSQUERY_INTEGRATION_PAGE = '/app/fleet/integrations/osquery_manager/add-integration';
|
||||
export const navigateTo = (page: string, opts?: Partial<Cypress.VisitOptions>) => {
|
||||
|
|
18
x-pack/plugins/osquery/cypress/test/index.ts
Normal file
18
x-pack/plugins/osquery/cypress/test/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
// For the source of these roles please consult the PR these were introduced https://github.com/elastic/kibana/pull/81866#issue-511165754
|
||||
export enum ROLES {
|
||||
soc_manager = 'soc_manager',
|
||||
reader = 'reader',
|
||||
t1_analyst = 't1_analyst',
|
||||
t2_analyst = 't2_analyst',
|
||||
hunter = 'hunter',
|
||||
rule_author = 'rule_author',
|
||||
platform_engineer = 'platform_engineer',
|
||||
detections_admin = 'detections_admin',
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
{
|
||||
"author": "Elastic",
|
||||
"name": "osquery",
|
||||
"version": "8.0.0",
|
||||
"private": true,
|
||||
"license": "Elastic-License",
|
||||
"scripts": {
|
||||
"cypress:open": "../../../node_modules/.bin/cypress open --config-file ./cypress/cypress.json",
|
||||
"cypress:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/osquery_cypress/visual_config.ts",
|
||||
"cypress:run": "../../../node_modules/.bin/cypress run --config-file ./cypress/cypress.json",
|
||||
"cypress:run-as-ci": "node ../../../scripts/functional_tests --config ../../test/osquery_cypress/cli_config.ts"
|
||||
}
|
||||
"author": "Elastic",
|
||||
"name": "osquery",
|
||||
"version": "8.0.0",
|
||||
"private": true,
|
||||
"license": "Elastic-License",
|
||||
"scripts": {
|
||||
"cypress:open": "../../../node_modules/.bin/cypress open --config-file ./cypress/cypress.json",
|
||||
"cypress:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/osquery_cypress/visual_config.ts",
|
||||
"cypress:run": "../../../node_modules/.bin/cypress run --config-file ./cypress/cypress.json",
|
||||
"cypress:run-as-ci": "node ../../../scripts/functional_tests --config ../../test/osquery_cypress/cli_config.ts",
|
||||
"nyc": "../../../node_modules/.bin/nyc report --reporter=text-summary"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,8 @@
|
|||
|
||||
import { ToolingLog } from '@kbn/dev-utils';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import { copyFile } from 'fs/promises';
|
||||
import { ChildProcess, execFileSync, spawn } from 'child_process';
|
||||
import { resolve } from 'path';
|
||||
import { unlinkSync } from 'fs';
|
||||
import { ChildProcess, spawn } from 'child_process';
|
||||
import { getLatestVersion } from './artifact_manager';
|
||||
import { Manager } from './resource_manager';
|
||||
|
||||
interface AgentManagerParams {
|
||||
|
@ -21,15 +19,12 @@ interface AgentManagerParams {
|
|||
}
|
||||
|
||||
export class AgentManager extends Manager {
|
||||
private directoryPath: string;
|
||||
private params: AgentManagerParams;
|
||||
private log: ToolingLog;
|
||||
private agentProcess?: ChildProcess;
|
||||
private requestOptions: AxiosRequestConfig;
|
||||
constructor(directoryPath: string, params: AgentManagerParams, log: ToolingLog) {
|
||||
constructor(params: AgentManagerParams, log: ToolingLog) {
|
||||
super();
|
||||
// TODO: check if the file exists
|
||||
this.directoryPath = directoryPath;
|
||||
this.log = log;
|
||||
this.params = params;
|
||||
this.requestOptions = {
|
||||
|
@ -43,27 +38,16 @@ export class AgentManager extends Manager {
|
|||
};
|
||||
}
|
||||
|
||||
public getBinaryPath() {
|
||||
return resolve(this.directoryPath, 'elastic-agent');
|
||||
}
|
||||
|
||||
public async setup() {
|
||||
this.log.info('Running agent preconfig');
|
||||
await axios.post(`${this.params.kibanaUrl}/api/fleet/agents/setup`, {}, this.requestOptions);
|
||||
|
||||
this.log.info('Updating the default agent output');
|
||||
const {
|
||||
data: {
|
||||
items: [defaultOutput],
|
||||
},
|
||||
} = await axios.get(this.params.kibanaUrl + '/api/fleet/outputs', this.requestOptions);
|
||||
|
||||
await axios.put(
|
||||
`${this.params.kibanaUrl}/api/fleet/outputs/${defaultOutput.id}`,
|
||||
{ hosts: [this.params.esHost] },
|
||||
return await axios.post(
|
||||
`${this.params.kibanaUrl}/api/fleet/agents/setup`,
|
||||
{},
|
||||
this.requestOptions
|
||||
);
|
||||
}
|
||||
|
||||
public async startAgent() {
|
||||
this.log.info('Getting agent enrollment key');
|
||||
const { data: apiKeys } = await axios.get(
|
||||
this.params.kibanaUrl + '/api/fleet/enrollment_api_keys',
|
||||
|
@ -71,25 +55,28 @@ export class AgentManager extends Manager {
|
|||
);
|
||||
const policy = apiKeys.items[1];
|
||||
|
||||
this.log.info('Enrolling the agent');
|
||||
const args = [
|
||||
'enroll',
|
||||
'--insecure',
|
||||
'-f',
|
||||
// TODO: parse the host/port out of the logs for the fleet server
|
||||
'--url=http://localhost:8220',
|
||||
`--enrollment-token=${policy.api_key}`,
|
||||
];
|
||||
const agentBinPath = this.getBinaryPath();
|
||||
execFileSync(agentBinPath, args, { stdio: 'inherit' });
|
||||
|
||||
// Copy the config file
|
||||
const configPath = resolve(__dirname, this.directoryPath, 'elastic-agent.yml');
|
||||
this.log.info(`Copying agent config from ${configPath}`);
|
||||
await copyFile(configPath, resolve('.', 'elastic-agent.yml'));
|
||||
|
||||
this.log.info('Running the agent');
|
||||
this.agentProcess = spawn(agentBinPath, ['run', '-v'], { stdio: 'inherit' });
|
||||
|
||||
const artifact = `docker.elastic.co/beats/elastic-agent:${await getLatestVersion()}`;
|
||||
this.log.info(artifact);
|
||||
|
||||
const args = [
|
||||
'run',
|
||||
'--add-host',
|
||||
'host.docker.internal:host-gateway',
|
||||
'--env',
|
||||
'FLEET_ENROLL=1',
|
||||
'--env',
|
||||
`FLEET_URL=http://host.docker.internal:8220`,
|
||||
'--env',
|
||||
`FLEET_ENROLLMENT_TOKEN=${policy.api_key}`,
|
||||
'--env',
|
||||
'FLEET_INSECURE=true',
|
||||
'--rm',
|
||||
artifact,
|
||||
];
|
||||
|
||||
this.agentProcess = spawn('docker', args, { stdio: 'inherit' });
|
||||
|
||||
// Wait til we see the agent is online
|
||||
let done = false;
|
||||
|
@ -106,6 +93,7 @@ export class AgentManager extends Manager {
|
|||
throw new Error('Agent timed out while coming online');
|
||||
}
|
||||
}
|
||||
|
||||
return { policyId: policy.policy_id as string };
|
||||
}
|
||||
|
||||
|
@ -121,6 +109,6 @@ export class AgentManager extends Manager {
|
|||
});
|
||||
delete this.agentProcess;
|
||||
}
|
||||
unlinkSync(resolve('.', 'elastic-agent.yml'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,121 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import { get } from 'lodash';
|
||||
import { execSync } from 'child_process';
|
||||
import { writeFileSync, unlinkSync, rmdirSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { ToolingLog } from '@kbn/dev-utils';
|
||||
import { Manager } from './resource_manager';
|
||||
|
||||
const archMap: { [key: string]: string } = {
|
||||
x64: 'x86_64',
|
||||
};
|
||||
|
||||
type ArtifactName = 'elastic-agent' | 'fleet-server';
|
||||
|
||||
async function getArtifact(
|
||||
artifact: string,
|
||||
urlExtractor: (data: AxiosResponse<any>, filename: string) => string,
|
||||
log: ToolingLog,
|
||||
version: string
|
||||
) {
|
||||
log.info(`Fetching ${version} of ${artifact}`);
|
||||
const agents = await axios(
|
||||
`https://artifacts-api.elastic.co/v1/versions/${version}/builds/latest`
|
||||
);
|
||||
const arch = archMap[process.arch] ?? process.arch;
|
||||
const dirName = `${artifact}-${version}-${process.platform}-${arch}`;
|
||||
const filename = dirName + '.tar.gz';
|
||||
const url = urlExtractor(agents.data, filename);
|
||||
if (!url) {
|
||||
log.error(`Could not find url for ${artifact}: ${url}`);
|
||||
throw new Error(`Unable to fetch ${artifact}`);
|
||||
}
|
||||
log.info(`Fetching ${filename} from ${url}`);
|
||||
const agent = await axios(url as string, { responseType: 'arraybuffer' });
|
||||
writeFileSync(filename, agent.data);
|
||||
execSync(`tar xvf ${filename}`);
|
||||
return resolve(filename);
|
||||
}
|
||||
|
||||
// There has to be a better way to represent partial function application
|
||||
type ArtifactFetcher = (
|
||||
log: Parameters<typeof getArtifact>[2],
|
||||
version: Parameters<typeof getArtifact>[3]
|
||||
) => ReturnType<typeof getArtifact>;
|
||||
type ArtifactFetchers = {
|
||||
[artifactName in ArtifactName]: ArtifactFetcher;
|
||||
};
|
||||
|
||||
const fetchers: ArtifactFetchers = {
|
||||
'elastic-agent': getArtifact.bind(null, 'elastic-agent', (data, filename) =>
|
||||
get(data, ['build', 'projects', 'beats', 'packages', filename, 'url'])
|
||||
),
|
||||
'fleet-server': getArtifact.bind(null, 'fleet-server', (data, filename) =>
|
||||
get(data, ['build', 'projects', 'fleet-server', 'packages', filename, 'url'])
|
||||
),
|
||||
};
|
||||
|
||||
export type FetchArtifactsParams = {
|
||||
[artifactName in ArtifactName]?: string;
|
||||
};
|
||||
|
||||
type ArtifactPaths = FetchArtifactsParams;
|
||||
export class ArtifactManager extends Manager {
|
||||
private artifacts: ArtifactPaths;
|
||||
private versions: FetchArtifactsParams;
|
||||
private log: ToolingLog;
|
||||
|
||||
constructor(versions: FetchArtifactsParams, log: ToolingLog) {
|
||||
super();
|
||||
this.versions = versions;
|
||||
this.log = log;
|
||||
this.artifacts = {};
|
||||
}
|
||||
|
||||
public fetchArtifacts = async () => {
|
||||
this.log.info('Fetching artifacts');
|
||||
await Promise.all(
|
||||
Object.keys(this.versions).map(async (name: string) => {
|
||||
const artifactName = name as ArtifactName;
|
||||
const version = this.versions[artifactName];
|
||||
if (!version) {
|
||||
this.log.warning(`No version is specified for ${artifactName}, skipping`);
|
||||
return;
|
||||
}
|
||||
const fetcher = fetchers[artifactName];
|
||||
if (!fetcher) {
|
||||
this.log.warning(`No fetcher is defined for ${artifactName}, skipping`);
|
||||
}
|
||||
|
||||
this.artifacts[artifactName] = await fetcher(this.log, version);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
public getArtifactDirectory(artifactName: string) {
|
||||
const file = this.artifacts[artifactName as ArtifactName];
|
||||
// this will break if the tarball name diverges from the directory that gets untarred
|
||||
if (!file) {
|
||||
throw new Error(`Unknown artifact ${artifactName}, unable to retreive directory`);
|
||||
}
|
||||
return file.replace('.tar.gz', '');
|
||||
}
|
||||
|
||||
protected _cleanup() {
|
||||
this.log.info('Cleaning up artifacts');
|
||||
if (this.artifacts) {
|
||||
for (const artifactName of Object.keys(this.artifacts)) {
|
||||
const file = this.artifacts[artifactName as ArtifactName];
|
||||
if (!file) {
|
||||
this.log.warning(`Unknown artifact ${artifactName} encountered during cleanup, skipping`);
|
||||
continue;
|
||||
}
|
||||
unlinkSync(file);
|
||||
rmdirSync(this.getArtifactDirectory(artifactName), { recursive: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
export async function getLatestVersion(): Promise<string> {
|
||||
return '8.0.0-SNAPSHOT';
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
// define custom es server here
|
||||
// API Keys is enabled at the top level
|
||||
'xpack.security.enabled=true',
|
||||
'http.host=0.0.0.0',
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
@ -6,44 +6,63 @@
|
|||
*/
|
||||
|
||||
import { ChildProcess, spawn } from 'child_process';
|
||||
import { copyFile } from 'fs/promises';
|
||||
import { unlinkSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { ToolingLog } from '@kbn/dev-utils';
|
||||
import axios from 'axios';
|
||||
import { Manager } from './resource_manager';
|
||||
import { getLatestVersion } from './artifact_manager';
|
||||
|
||||
export interface ElasticsearchConfig {
|
||||
esHost: string;
|
||||
user: string;
|
||||
password: string;
|
||||
port: string;
|
||||
}
|
||||
|
||||
export class FleetManager extends Manager {
|
||||
private directoryPath: string;
|
||||
private fleetProcess?: ChildProcess;
|
||||
private esConfig: ElasticsearchConfig;
|
||||
private log: ToolingLog;
|
||||
constructor(directoryPath: string, esConfig: ElasticsearchConfig, log: ToolingLog) {
|
||||
constructor(esConfig: ElasticsearchConfig, log: ToolingLog) {
|
||||
super();
|
||||
// TODO: check if the file exists
|
||||
this.esConfig = esConfig;
|
||||
this.directoryPath = directoryPath;
|
||||
this.log = log;
|
||||
}
|
||||
public async setup(): Promise<void> {
|
||||
this.log.info('Setting fleet up');
|
||||
await copyFile(resolve(__dirname, 'fleet_server.yml'), resolve('.', 'fleet-server.yml'));
|
||||
return new Promise((res, rej) => {
|
||||
const env = {
|
||||
ELASTICSEARCH_HOSTS: this.esConfig.esHost,
|
||||
ELASTICSEARCH_USERNAME: this.esConfig.user,
|
||||
ELASTICSEARCH_PASSWORD: this.esConfig.password,
|
||||
};
|
||||
const file = resolve(this.directoryPath, 'fleet-server');
|
||||
// TODO: handle logging properly
|
||||
this.fleetProcess = spawn(file, [], { stdio: 'inherit', env });
|
||||
this.fleetProcess.on('error', rej);
|
||||
// TODO: actually wait for the fleet server to start listening
|
||||
setTimeout(res, 15000);
|
||||
return new Promise(async (res, rej) => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.esConfig.esHost}/_security/service/elastic/fleet-server/credential/token`
|
||||
);
|
||||
const serviceToken = response.data.token.value;
|
||||
const artifact = `docker.elastic.co/beats/elastic-agent:${await getLatestVersion()}`;
|
||||
this.log.info(artifact);
|
||||
|
||||
const host = 'host.docker.internal';
|
||||
|
||||
const args = [
|
||||
'run',
|
||||
'-p',
|
||||
`8220:8220`,
|
||||
'--add-host',
|
||||
'host.docker.internal:host-gateway',
|
||||
'--env',
|
||||
'FLEET_SERVER_ENABLE=true',
|
||||
'--env',
|
||||
`FLEET_SERVER_ELASTICSEARCH_HOST=http://${host}:${this.esConfig.port}`,
|
||||
'--env',
|
||||
`FLEET_SERVER_SERVICE_TOKEN=${serviceToken}`,
|
||||
'--rm',
|
||||
artifact,
|
||||
];
|
||||
this.fleetProcess = spawn('docker', args, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
this.fleetProcess.on('error', rej);
|
||||
setTimeout(res, 15000);
|
||||
} catch (error) {
|
||||
rej(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -60,6 +79,5 @@ export class FleetManager extends Manager {
|
|||
});
|
||||
delete this.fleetProcess;
|
||||
}
|
||||
unlinkSync(resolve('.', 'fleet-server.yml'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
# mostly a stub config
|
||||
output:
|
||||
elasticsearch:
|
||||
hosts: '${ELASTICSEARCH_HOSTS:localhost:9220}'
|
||||
username: '${ELASTICSEARCH_USERNAME:elastic}'
|
||||
password: '${ELASTICSEARCH_PASSWORD:changeme}'
|
||||
|
||||
fleet:
|
||||
agent:
|
||||
id: 1e4954ce-af37-4731-9f4a-407b08e69e42
|
||||
logging:
|
||||
level: '${LOG_LEVEL:DEBUG}'
|
||||
|
||||
logging:
|
||||
to_stderr: true
|
||||
|
||||
http.enabled: true
|
|
@ -12,40 +12,26 @@ import { withProcRunner } from '@kbn/dev-utils';
|
|||
|
||||
import { FtrProviderContext } from './ftr_provider_context';
|
||||
|
||||
import { ArtifactManager, FetchArtifactsParams } from './artifact_manager';
|
||||
import { setupUsers } from './users';
|
||||
import { AgentManager } from './agent';
|
||||
import { FleetManager } from './fleet_server';
|
||||
|
||||
interface SetupParams {
|
||||
artifacts: FetchArtifactsParams;
|
||||
}
|
||||
|
||||
async function withFleetAgent(
|
||||
{ getService }: FtrProviderContext,
|
||||
params: SetupParams,
|
||||
runner: (runnerEnv: Record<string, string>) => Promise<void>
|
||||
) {
|
||||
const log = getService('log');
|
||||
const config = getService('config');
|
||||
|
||||
const artifactManager = new ArtifactManager(params.artifacts, log);
|
||||
await artifactManager.fetchArtifacts();
|
||||
|
||||
const esHost = Url.format(config.get('servers.elasticsearch'));
|
||||
const esConfig = {
|
||||
user: config.get('servers.elasticsearch.username'),
|
||||
password: config.get('servers.elasticsearch.password'),
|
||||
esHost,
|
||||
port: config.get('servers.elasticsearch.port'),
|
||||
};
|
||||
const fleetManager = new FleetManager(
|
||||
artifactManager.getArtifactDirectory('fleet-server'),
|
||||
esConfig,
|
||||
log
|
||||
);
|
||||
const fleetManager = new FleetManager(esConfig, log);
|
||||
|
||||
const agentManager = new AgentManager(
|
||||
artifactManager.getArtifactDirectory('elastic-agent'),
|
||||
{
|
||||
...esConfig,
|
||||
kibanaUrl: Url.format({
|
||||
|
@ -64,107 +50,56 @@ async function withFleetAgent(
|
|||
process.exit(1);
|
||||
});
|
||||
|
||||
await agentManager.setup();
|
||||
await fleetManager.setup();
|
||||
const { policyId } = await agentManager.setup();
|
||||
await setupUsers(esConfig);
|
||||
try {
|
||||
await runner({
|
||||
CYPRESS_OSQUERY_POLICY: policyId,
|
||||
});
|
||||
await runner({});
|
||||
} finally {
|
||||
fleetManager.cleanup();
|
||||
agentManager.cleanup();
|
||||
artifactManager.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
export async function OsqueryCypressCliTestRunner(context: FtrProviderContext) {
|
||||
const log = context.getService('log');
|
||||
const config = context.getService('config');
|
||||
await withFleetAgent(
|
||||
context,
|
||||
{
|
||||
artifacts: {
|
||||
'elastic-agent': '7.15.0-SNAPSHOT',
|
||||
'fleet-server': '7.15.0-SNAPSHOT',
|
||||
},
|
||||
},
|
||||
(runnerEnv) =>
|
||||
withProcRunner(log, async (procs) => {
|
||||
await procs.run('cypress', {
|
||||
cmd: 'yarn',
|
||||
args: ['cypress:run'],
|
||||
cwd: resolve(__dirname, '../../plugins/osquery'),
|
||||
env: {
|
||||
FORCE_COLOR: '1',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_baseUrl: Url.format(config.get('servers.kibana')),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_protocol: config.get('servers.kibana.protocol'),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_hostname: config.get('servers.kibana.hostname'),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_configport: config.get('servers.kibana.port'),
|
||||
CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')),
|
||||
CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'),
|
||||
CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'),
|
||||
CYPRESS_KIBANA_URL: Url.format({
|
||||
protocol: config.get('servers.kibana.protocol'),
|
||||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
}),
|
||||
...runnerEnv,
|
||||
...process.env,
|
||||
},
|
||||
wait: true,
|
||||
});
|
||||
})
|
||||
);
|
||||
await startOsqueryCypress(context, 'run');
|
||||
}
|
||||
|
||||
export async function OsqueryCypressVisualTestRunner(context: FtrProviderContext) {
|
||||
await startOsqueryCypress(context, 'open');
|
||||
}
|
||||
|
||||
function startOsqueryCypress(context: FtrProviderContext, cypressCommand: string) {
|
||||
const log = context.getService('log');
|
||||
const config = context.getService('config');
|
||||
|
||||
await withFleetAgent(
|
||||
context,
|
||||
{
|
||||
artifacts: {
|
||||
'elastic-agent': '7.15.0-SNAPSHOT',
|
||||
'fleet-server': '7.15.0-SNAPSHOT',
|
||||
},
|
||||
},
|
||||
(runnerEnv) =>
|
||||
withProcRunner(
|
||||
log,
|
||||
async (procs) =>
|
||||
await procs.run('cypress', {
|
||||
cmd: 'yarn',
|
||||
args: ['cypress:open'],
|
||||
cwd: resolve(__dirname, '../../plugins/osquery'),
|
||||
env: {
|
||||
FORCE_COLOR: '1',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_baseUrl: Url.format(config.get('servers.kibana')),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_protocol: config.get('servers.kibana.protocol'),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_hostname: config.get('servers.kibana.hostname'),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_configport: config.get('servers.kibana.port'),
|
||||
CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')),
|
||||
CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'),
|
||||
CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'),
|
||||
CYPRESS_KIBANA_URL: Url.format({
|
||||
protocol: config.get('servers.kibana.protocol'),
|
||||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
}),
|
||||
...runnerEnv,
|
||||
...process.env,
|
||||
},
|
||||
wait: true,
|
||||
})
|
||||
)
|
||||
return withFleetAgent(context, (runnerEnv) =>
|
||||
withProcRunner(log, async (procs) => {
|
||||
await procs.run('cypress', {
|
||||
cmd: 'yarn',
|
||||
args: [`cypress:${cypressCommand}`],
|
||||
cwd: resolve(__dirname, '../../plugins/osquery'),
|
||||
env: {
|
||||
FORCE_COLOR: '1',
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_baseUrl: Url.format(config.get('servers.kibana')),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_protocol: config.get('servers.kibana.protocol'),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_hostname: config.get('servers.kibana.hostname'),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
CYPRESS_configport: config.get('servers.kibana.port'),
|
||||
CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')),
|
||||
CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'),
|
||||
CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'),
|
||||
CYPRESS_KIBANA_URL: Url.format({
|
||||
protocol: config.get('servers.kibana.protocol'),
|
||||
hostname: config.get('servers.kibana.hostname'),
|
||||
port: config.get('servers.kibana.port'),
|
||||
}),
|
||||
...runnerEnv,
|
||||
...process.env,
|
||||
},
|
||||
wait: true,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
264
yarn.lock
264
yarn.lock
|
@ -1352,7 +1352,7 @@
|
|||
dependencies:
|
||||
"@cspotcode/source-map-consumer" "0.8.0"
|
||||
|
||||
"@cypress/browserify-preprocessor@^3.0.1":
|
||||
"@cypress/browserify-preprocessor@3.0.1", "@cypress/browserify-preprocessor@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@cypress/browserify-preprocessor/-/browserify-preprocessor-3.0.1.tgz#ab86335b0c061d11f5ad7df03f06b1877b836f71"
|
||||
integrity sha512-sErmFSEr5287bLMRl0POGnyFtJCs/lSk5yxrUIJUIHZ8eDvtTEr0V93xRgLjJVG54gJU4MbpHy1mRPA9VZbtQA==
|
||||
|
@ -1376,6 +1376,21 @@
|
|||
through2 "^2.0.0"
|
||||
watchify "3.11.1"
|
||||
|
||||
"@cypress/code-coverage@^3.9.11":
|
||||
version "3.9.11"
|
||||
resolved "https://registry.yarnpkg.com/@cypress/code-coverage/-/code-coverage-3.9.11.tgz#5d7d6da548d561001602b30accc7fa90dc487072"
|
||||
integrity sha512-SA+fPILiiE0UHlMAwuv592D+wbKKdLbXz7BAN2a2RvW4fLbkVn1dXLATUFYf/6LkKrLaXJ3RENsoW9JqjBLzeQ==
|
||||
dependencies:
|
||||
"@cypress/browserify-preprocessor" "3.0.1"
|
||||
chalk "4.1.2"
|
||||
dayjs "1.10.7"
|
||||
debug "4.3.2"
|
||||
execa "4.1.0"
|
||||
globby "11.0.4"
|
||||
istanbul-lib-coverage "3.0.0"
|
||||
js-yaml "3.14.1"
|
||||
nyc "15.1.0"
|
||||
|
||||
"@cypress/request@^2.88.6":
|
||||
version "2.88.6"
|
||||
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.6.tgz#a970dd675befc6bdf8a8921576c01f51cc5798e9"
|
||||
|
@ -8101,29 +8116,6 @@ axobject-query@^2.2.0:
|
|||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
||||
integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
|
||||
|
||||
babel-code-frame@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||
integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^3.0.2"
|
||||
|
||||
babel-generator@^6.18.0:
|
||||
version "6.26.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
|
||||
integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==
|
||||
dependencies:
|
||||
babel-messages "^6.23.0"
|
||||
babel-runtime "^6.26.0"
|
||||
babel-types "^6.26.0"
|
||||
detect-indent "^4.0.0"
|
||||
jsesc "^1.3.0"
|
||||
lodash "^4.17.4"
|
||||
source-map "^0.5.7"
|
||||
trim-right "^1.0.1"
|
||||
|
||||
babel-jest@^26.6.3:
|
||||
version "26.6.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056"
|
||||
|
@ -8148,13 +8140,6 @@ babel-loader@^8.2.2:
|
|||
make-dir "^3.1.0"
|
||||
schema-utils "^2.6.5"
|
||||
|
||||
babel-messages@^6.23.0:
|
||||
version "6.23.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
|
||||
integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=
|
||||
dependencies:
|
||||
babel-runtime "^6.22.0"
|
||||
|
||||
babel-plugin-add-module-exports@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-1.0.2.tgz#96cd610d089af664f016467fc4567c099cce2d9c"
|
||||
|
@ -8394,7 +8379,7 @@ babel-preset-jest@^26.6.2:
|
|||
babel-plugin-jest-hoist "^26.6.2"
|
||||
babel-preset-current-node-syntax "^1.0.0"
|
||||
|
||||
babel-runtime@6.x, babel-runtime@^6.11.6, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
|
||||
babel-runtime@6.x, babel-runtime@^6.11.6, babel-runtime@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
|
||||
|
@ -8402,52 +8387,11 @@ babel-runtime@6.x, babel-runtime@^6.11.6, babel-runtime@^6.22.0, babel-runtime@^
|
|||
core-js "^2.4.0"
|
||||
regenerator-runtime "^0.11.0"
|
||||
|
||||
babel-template@^6.16.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
|
||||
integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
babel-traverse "^6.26.0"
|
||||
babel-types "^6.26.0"
|
||||
babylon "^6.18.0"
|
||||
lodash "^4.17.4"
|
||||
|
||||
babel-traverse@^6.18.0, babel-traverse@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
|
||||
integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
|
||||
dependencies:
|
||||
babel-code-frame "^6.26.0"
|
||||
babel-messages "^6.23.0"
|
||||
babel-runtime "^6.26.0"
|
||||
babel-types "^6.26.0"
|
||||
babylon "^6.18.0"
|
||||
debug "^2.6.8"
|
||||
globals "^9.18.0"
|
||||
invariant "^2.2.2"
|
||||
lodash "^4.17.4"
|
||||
|
||||
babel-types@^6.18.0, babel-types@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
|
||||
integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
esutils "^2.0.2"
|
||||
lodash "^4.17.4"
|
||||
to-fast-properties "^1.0.3"
|
||||
|
||||
babelify@10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/babelify/-/babelify-10.0.0.tgz#fe73b1a22583f06680d8d072e25a1e0d1d1d7fb5"
|
||||
integrity sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==
|
||||
|
||||
babylon@^6.18.0:
|
||||
version "6.18.0"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
|
||||
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
|
||||
|
||||
bach@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880"
|
||||
|
@ -9448,6 +9392,14 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.
|
|||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
|
@ -11063,11 +11015,23 @@ cypress-pipe@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/cypress-pipe/-/cypress-pipe-2.0.0.tgz#577df7a70a8603d89a96dfe4092a605962181af8"
|
||||
integrity sha512-KW9s+bz4tFLucH3rBGfjW+Q12n7S4QpUSSyxiGrgPOfoHlbYWzAGB3H26MO0VTojqf9NVvfd5Kt0MH5XMgbfyg==
|
||||
|
||||
cypress-react-selector@^2.3.13:
|
||||
version "2.3.13"
|
||||
resolved "https://registry.yarnpkg.com/cypress-react-selector/-/cypress-react-selector-2.3.13.tgz#468f3b42261ed04a7a5f9036d7373cf3894e2672"
|
||||
integrity sha512-30z82/k9Mp5wgpXe/8DyVD2w5cXLmVAiGd/YEKoEto4jmkTWP9WfzSRvBbhd6mrr99HMUudlZUB3TwFgvPp3og==
|
||||
dependencies:
|
||||
resq "1.10.1"
|
||||
|
||||
cypress-real-events@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.5.1.tgz#5eeb86d2a7aad9aa6d5271e288a23e46373915cd"
|
||||
integrity sha512-Jwi/IJePcZrKyhdtVddaf+mqJrj3y1vpREMDgtWwz+oxvj5FbBpeU0ASu9zpB3bMbsMo7g//buopZIe4jx3iSA==
|
||||
|
||||
cypress-recurse@^1.13.1:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/cypress-recurse/-/cypress-recurse-1.13.1.tgz#1d026d3381e4de7cf867a5ef592c4161da325fed"
|
||||
integrity sha512-re0djeUInv0JwxhFBSIiZmrJfvUaLTjK9jWsD0oqpnvG1UXGWR69rkXMtMK5HZhxkL7GSk9JiIpm49aWpOnsFA==
|
||||
|
||||
cypress@^8.5.0:
|
||||
version "8.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.5.0.tgz#5712ca170913f8344bf167301205c4217c1eb9bd"
|
||||
|
@ -11487,10 +11451,10 @@ dateformat@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
|
||||
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
|
||||
|
||||
dayjs@^1.10.4:
|
||||
version "1.10.4"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2"
|
||||
integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==
|
||||
dayjs@1.10.7, dayjs@^1.10.4:
|
||||
version "1.10.7"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468"
|
||||
integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig==
|
||||
|
||||
debug-fabulous@1.X:
|
||||
version "1.1.0"
|
||||
|
@ -11501,7 +11465,7 @@ debug-fabulous@1.X:
|
|||
memoizee "0.4.X"
|
||||
object-assign "4.X"
|
||||
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9:
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
|
@ -11905,13 +11869,6 @@ detect-file@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
|
||||
integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
|
||||
|
||||
detect-indent@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
|
||||
integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg=
|
||||
dependencies:
|
||||
repeating "^2.0.0"
|
||||
|
||||
detect-indent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
|
||||
|
@ -13679,7 +13636,7 @@ fancy-log@^1.3.2:
|
|||
color-support "^1.1.3"
|
||||
time-stamp "^1.0.0"
|
||||
|
||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3:
|
||||
fast-deep-equal@^2.0.1, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
||||
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
|
||||
|
@ -14602,6 +14559,11 @@ get-nonce@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
|
||||
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
|
||||
|
||||
get-package-type@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
|
||||
integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
|
||||
|
||||
get-pixels@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/get-pixels/-/get-pixels-3.3.2.tgz#3f62fb8811932c69f262bba07cba72b692b4ff03"
|
||||
|
@ -14911,11 +14873,6 @@ globals@^13.6.0, globals@^13.9.0:
|
|||
dependencies:
|
||||
type-fest "^0.20.2"
|
||||
|
||||
globals@^9.18.0:
|
||||
version "9.18.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
|
||||
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
|
||||
|
||||
globalthis@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.1.tgz#40116f5d9c071f9e8fb0037654df1ab3a83b7ef9"
|
||||
|
@ -14949,6 +14906,18 @@ globby@11.0.1:
|
|||
merge2 "^1.3.0"
|
||||
slash "^3.0.0"
|
||||
|
||||
globby@11.0.4, globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.0.4:
|
||||
version "11.0.4"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5"
|
||||
integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==
|
||||
dependencies:
|
||||
array-union "^2.1.0"
|
||||
dir-glob "^3.0.1"
|
||||
fast-glob "^3.1.1"
|
||||
ignore "^5.1.4"
|
||||
merge2 "^1.3.0"
|
||||
slash "^3.0.0"
|
||||
|
||||
globby@^10.0.1:
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543"
|
||||
|
@ -14963,18 +14932,6 @@ globby@^10.0.1:
|
|||
merge2 "^1.2.3"
|
||||
slash "^3.0.0"
|
||||
|
||||
globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.0.4:
|
||||
version "11.0.4"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5"
|
||||
integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==
|
||||
dependencies:
|
||||
array-union "^2.1.0"
|
||||
dir-glob "^3.0.1"
|
||||
fast-glob "^3.1.1"
|
||||
ignore "^5.1.4"
|
||||
merge2 "^1.3.0"
|
||||
slash "^3.0.0"
|
||||
|
||||
globby@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
|
||||
|
@ -17022,27 +16979,7 @@ isstream@~0.1.2:
|
|||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
istanbul-instrumenter-loader@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz#9957bd59252b373fae5c52b7b5188e6fde2a0949"
|
||||
integrity sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==
|
||||
dependencies:
|
||||
convert-source-map "^1.5.0"
|
||||
istanbul-lib-instrument "^1.7.3"
|
||||
loader-utils "^1.1.0"
|
||||
schema-utils "^0.3.0"
|
||||
|
||||
istanbul-lib-coverage@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0"
|
||||
integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==
|
||||
|
||||
istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec"
|
||||
integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==
|
||||
|
||||
istanbul-lib-coverage@^3.2.0:
|
||||
istanbul-lib-coverage@3.0.0, istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1, istanbul-lib-coverage@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
|
||||
integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==
|
||||
|
@ -17054,19 +16991,6 @@ istanbul-lib-hook@^3.0.0:
|
|||
dependencies:
|
||||
append-transform "^2.0.0"
|
||||
|
||||
istanbul-lib-instrument@^1.7.3:
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca"
|
||||
integrity sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==
|
||||
dependencies:
|
||||
babel-generator "^6.18.0"
|
||||
babel-template "^6.16.0"
|
||||
babel-traverse "^6.18.0"
|
||||
babel-types "^6.18.0"
|
||||
babylon "^6.18.0"
|
||||
istanbul-lib-coverage "^1.2.1"
|
||||
semver "^5.3.0"
|
||||
|
||||
istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d"
|
||||
|
@ -17861,7 +17785,7 @@ js-string-escape@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
|
||||
integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^3.0.2:
|
||||
"js-tokens@^3.0.0 || ^4.0.0":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
||||
integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
|
||||
|
@ -17879,6 +17803,14 @@ js-yaml@3.14.0, js-yaml@^3.14.0:
|
|||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
js-yaml@3.14.1:
|
||||
version "3.14.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
|
||||
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
js-yaml@^3.13.1, js-yaml@^3.9.0, js-yaml@~3.13.1:
|
||||
version "3.13.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
|
||||
|
@ -17956,11 +17888,6 @@ jsdom@^16.4.0:
|
|||
ws "^7.2.3"
|
||||
xml-name-validator "^3.0.0"
|
||||
|
||||
jsesc@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
|
||||
integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
|
||||
|
||||
jsesc@^2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe"
|
||||
|
@ -20678,10 +20605,10 @@ nwsapi@^2.0.9, nwsapi@^2.2.0:
|
|||
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
|
||||
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
|
||||
|
||||
nyc@^15.0.1:
|
||||
version "15.0.1"
|
||||
resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.0.1.tgz#bd4d5c2b17f2ec04370365a5ca1fc0ed26f9f93d"
|
||||
integrity sha512-n0MBXYBYRqa67IVt62qW1r/d9UH/Qtr7SF1w/nQLJ9KxvWF6b2xCHImRAixHN9tnMMYHC2P14uo6KddNGwMgGg==
|
||||
nyc@15.1.0, nyc@^15.1.0:
|
||||
version "15.1.0"
|
||||
resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02"
|
||||
integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==
|
||||
dependencies:
|
||||
"@istanbuljs/load-nyc-config" "^1.0.0"
|
||||
"@istanbuljs/schema" "^0.1.2"
|
||||
|
@ -20691,6 +20618,7 @@ nyc@^15.0.1:
|
|||
find-cache-dir "^3.2.0"
|
||||
find-up "^4.1.0"
|
||||
foreground-child "^2.0.0"
|
||||
get-package-type "^0.1.0"
|
||||
glob "^7.1.6"
|
||||
istanbul-lib-coverage "^3.0.0"
|
||||
istanbul-lib-hook "^3.0.0"
|
||||
|
@ -24465,13 +24393,6 @@ repeating@^1.1.2:
|
|||
dependencies:
|
||||
is-finite "^1.0.0"
|
||||
|
||||
repeating@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
|
||||
integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=
|
||||
dependencies:
|
||||
is-finite "^1.0.0"
|
||||
|
||||
replace-ext@1.0.0, replace-ext@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
|
||||
|
@ -24729,6 +24650,13 @@ responselike@^2.0.0:
|
|||
dependencies:
|
||||
lowercase-keys "^2.0.0"
|
||||
|
||||
resq@1.10.1:
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/resq/-/resq-1.10.1.tgz#c05d1b3808016cceec4d485ceb375acb49565f53"
|
||||
integrity sha512-zhp1iyUH02MLciv3bIM2bNtTFx/fqRsK4Jk73jcPqp00d/sMTTjOtjdTMAcgjrQKGx5DvQ/HSpeqaMW0atGRJA==
|
||||
dependencies:
|
||||
fast-deep-equal "^2.0.1"
|
||||
|
||||
restore-cursor@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
|
||||
|
@ -25039,15 +24967,6 @@ scheduler@^0.18.0:
|
|||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
schema-utils@1.0.0, schema-utils@^0.3.0, schema-utils@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
|
||||
integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
|
||||
dependencies:
|
||||
ajv "^6.1.0"
|
||||
ajv-errors "^1.0.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
schema-utils@2.7.0, schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
|
||||
|
@ -25065,6 +24984,15 @@ schema-utils@^0.4.5:
|
|||
ajv "^6.1.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
schema-utils@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
|
||||
integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
|
||||
dependencies:
|
||||
ajv "^6.1.0"
|
||||
ajv-errors "^1.0.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
schema-utils@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
|
||||
|
@ -27324,11 +27252,6 @@ to-camel-case@^1.0.0:
|
|||
dependencies:
|
||||
to-space-case "^1.0.0"
|
||||
|
||||
to-fast-properties@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
|
||||
integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
|
||||
|
||||
to-fast-properties@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
||||
|
@ -27502,11 +27425,6 @@ trim-newlines@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30"
|
||||
integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==
|
||||
|
||||
trim-right@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
|
||||
integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
|
||||
|
||||
trim-trailing-lines@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz#7aefbb7808df9d669f6da2e438cac8c46ada7684"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue