[ftr] add ability to get included/excluded test counts (#25760)
## Summary This pr adds a `--test-stats` flag to the functional test runner that will allow extracting information from a specific config/tags combo about how many tests will run and how many are excluded. This is necessary as part of https://github.com/elastic/kibana/pull/22359 so we can effectively ignore specific configs (avoiding starting es and kibana) when there aren't any tests that will be run. ### Checklist Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR. ~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~ ~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~ ~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~ ~~- [ ] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios~~ ~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~ ### For maintainers ~~- [ ] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~~ ~~- [ ] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~~
This commit is contained in:
parent
df8514fa09
commit
f7832a514c
|
@ -48,6 +48,7 @@ cmd
|
|||
.option('--exclude [file]', 'Path to a test file that should not be loaded', collectExcludePaths(), [])
|
||||
.option('--include-tag [tag]', 'A tag to be included, pass multiple times for multiple tags', collectIncludeTags(), [])
|
||||
.option('--exclude-tag [tag]', 'A tag to be excluded, pass multiple times for multiple tags', collectExcludeTags(), [])
|
||||
.option('--test-stats', 'Print the number of tests (included and excluded) to STDERR', false)
|
||||
.option('--verbose', 'Log everything', false)
|
||||
.option('--quiet', 'Only log errors', false)
|
||||
.option('--silent', 'Log nothing', false)
|
||||
|
@ -86,8 +87,16 @@ const functionalTestRunner = createFunctionalTestRunner({
|
|||
|
||||
async function run() {
|
||||
try {
|
||||
if (cmd.testStats) {
|
||||
process.stderr.write(JSON.stringify(
|
||||
await functionalTestRunner.getTestStats(),
|
||||
null,
|
||||
2
|
||||
) + '\n');
|
||||
} else {
|
||||
const failureCount = await functionalTestRunner.run();
|
||||
process.exitCode = failureCount ? 1 : 0;
|
||||
}
|
||||
} catch (err) {
|
||||
await teardown(err);
|
||||
} finally {
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
import {
|
||||
createLifecycle,
|
||||
readConfigFile,
|
||||
createProviderCollection,
|
||||
ProviderCollection,
|
||||
readProviderSpec,
|
||||
setupMocha,
|
||||
runTests,
|
||||
} from './lib';
|
||||
|
@ -36,8 +37,61 @@ export function createFunctionalTestRunner({ log, configFile, configOverrides })
|
|||
log.verbose('ending %j lifecycle phase', name);
|
||||
});
|
||||
|
||||
|
||||
class FunctionalTestRunner {
|
||||
async run() {
|
||||
return await this._run(async (config, coreProviders) => {
|
||||
const providers = new ProviderCollection(log, [
|
||||
...coreProviders,
|
||||
...readProviderSpec('Service', config.get('services')),
|
||||
...readProviderSpec('PageObject', config.get('pageObjects'))
|
||||
]);
|
||||
|
||||
await providers.loadAll();
|
||||
|
||||
const mocha = await setupMocha(lifecycle, log, config, providers);
|
||||
await lifecycle.trigger('beforeTests');
|
||||
log.info('Starting tests');
|
||||
|
||||
return await runTests(lifecycle, log, mocha);
|
||||
});
|
||||
}
|
||||
|
||||
async getTestStats() {
|
||||
return await this._run(async (config, coreProviders) => {
|
||||
// replace the function of a provider so that it returns a promise-like object which
|
||||
// never "resolves", essentially disabling all the services while still allowing us
|
||||
// to load the test files and populate the mocha suites
|
||||
const stubProvider = provider => ({
|
||||
...provider,
|
||||
fn: () => ({
|
||||
then: () => {}
|
||||
})
|
||||
});
|
||||
|
||||
const providers = new ProviderCollection(log, [
|
||||
...coreProviders,
|
||||
...readProviderSpec('Service', config.get('services')),
|
||||
...readProviderSpec('PageObject', config.get('pageObjects'))
|
||||
].map(stubProvider));
|
||||
|
||||
const mocha = await setupMocha(lifecycle, log, config, providers);
|
||||
|
||||
const countTests = suite => (
|
||||
suite.suites.reduce(
|
||||
(sum, suite) => sum + countTests(suite),
|
||||
suite.tests.length
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
tests: countTests(mocha.suite),
|
||||
excludedTests: mocha.excludedTests.length
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async _run(handler) {
|
||||
let runErrorOccurred = false;
|
||||
|
||||
try {
|
||||
|
@ -49,14 +103,14 @@ export function createFunctionalTestRunner({ log, configFile, configOverrides })
|
|||
return;
|
||||
}
|
||||
|
||||
const providers = createProviderCollection(lifecycle, log, config);
|
||||
await providers.loadAll();
|
||||
|
||||
const mocha = await setupMocha(lifecycle, log, config, providers);
|
||||
await lifecycle.trigger('beforeTests');
|
||||
log.info('Starting tests');
|
||||
return await runTests(lifecycle, log, mocha);
|
||||
// base level services that functional_test_runner exposes
|
||||
const coreProviders = readProviderSpec('Service', {
|
||||
lifecycle: () => lifecycle,
|
||||
log: () => log,
|
||||
config: () => config,
|
||||
});
|
||||
|
||||
return await handler(config, coreProviders);
|
||||
} catch (runError) {
|
||||
runErrorOccurred = true;
|
||||
throw runError;
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ProviderCollection,
|
||||
readProviderSpec
|
||||
} from './providers';
|
||||
|
||||
/**
|
||||
* Create a ProviderCollection that includes the Service
|
||||
* providers and PageObject providers from config, as well
|
||||
* providers for the default services, lifecycle, log, and
|
||||
* config
|
||||
*
|
||||
* @param {Lifecycle} lifecycle
|
||||
* @param {ToolingLog} log
|
||||
* @param {Config} config [description]
|
||||
* @return {ProviderCollection}
|
||||
*/
|
||||
export function createProviderCollection(lifecycle, log, config) {
|
||||
return new ProviderCollection(log, [
|
||||
...readProviderSpec('Service', {
|
||||
// base level services that functional_test_runner exposes
|
||||
lifecycle: () => lifecycle,
|
||||
log: () => log,
|
||||
config: () => config,
|
||||
|
||||
...config.get('services'),
|
||||
}),
|
||||
...readProviderSpec('PageObject', {
|
||||
...config.get('pageObjects')
|
||||
})
|
||||
]);
|
||||
}
|
|
@ -19,5 +19,5 @@
|
|||
|
||||
export { createLifecycle } from './lifecycle';
|
||||
export { readConfigFile } from './config';
|
||||
export { createProviderCollection } from './create_provider_collection';
|
||||
export { setupMocha, runTests } from './mocha';
|
||||
export { readProviderSpec, ProviderCollection } from './providers';
|
||||
|
|
|
@ -28,6 +28,14 @@
|
|||
* @param options.exclude an array of tags that will be used to exclude suites from the run
|
||||
*/
|
||||
export function filterSuitesByTags({ log, mocha, include, exclude }) {
|
||||
mocha.excludedTests = [];
|
||||
// collect all the tests from some suite, including it's children
|
||||
const collectTests = (suite) =>
|
||||
suite.suites.reduce(
|
||||
(acc, s) => acc.concat(collectTests(s)),
|
||||
suite.tests
|
||||
);
|
||||
|
||||
// if include tags were provided, filter the tree once to
|
||||
// only include branches that are included at some point
|
||||
if (include.length) {
|
||||
|
@ -47,14 +55,18 @@ export function filterSuitesByTags({ log, mocha, include, exclude }) {
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
// this suite has an included child but is not included
|
||||
// itself, so strip out its tests and recurse to filter
|
||||
// out child suites which are not included
|
||||
if (isChildIncluded(child)) {
|
||||
mocha.excludedTests = mocha.excludedTests.concat(child.tests);
|
||||
child.tests = [];
|
||||
parentSuite.suites.push(child);
|
||||
recurse(child);
|
||||
continue;
|
||||
} else {
|
||||
mocha.excludedTests = mocha.excludedTests.concat(collectTests(child));
|
||||
}
|
||||
}
|
||||
}(mocha.suite));
|
||||
|
@ -78,6 +90,8 @@ export function filterSuitesByTags({ log, mocha, include, exclude }) {
|
|||
if (isNotExcluded(child)) {
|
||||
parentSuite.suites.push(child);
|
||||
recurse(child);
|
||||
} else {
|
||||
mocha.excludedTests = mocha.excludedTests.concat(collectTests(child));
|
||||
}
|
||||
}
|
||||
}(mocha.suite));
|
||||
|
|
Loading…
Reference in a new issue