diff --git a/docs/guide/testrunner/organizesuite.md b/docs/guide/testrunner/organizesuite.md index 4900165041a..eb8ac5c2505 100644 --- a/docs/guide/testrunner/organizesuite.md +++ b/docs/guide/testrunner/organizesuite.md @@ -111,6 +111,12 @@ or run multiple specs at once: $ wdio wdio.conf.js --spec ./test/specs/signup.js,./test/specs/forgot-password.js ``` +In case of a feature (Cucumber) you can specify a single scenario by adding a line number: + +```sh +$ wdio wdio.conf.js --spec ./test/specs/login.feature:6 +``` + If the spec passed in is not a path to a spec file, it is used as a filter for the spec file names defined in your configuration file. To run all specs with the word 'dialog' in the spec file names, you could use: ```sh diff --git a/lib/utils/ConfigParser.js b/lib/utils/ConfigParser.js index 26349439d9f..4feeafee3fa 100644 --- a/lib/utils/ConfigParser.js +++ b/lib/utils/ConfigParser.js @@ -1,7 +1,7 @@ +import merge from 'deepmerge' import fs from 'fs' -import path from 'path' import glob from 'glob' -import merge from 'deepmerge' +import path from 'path' import detectSeleniumBackend from '../helpers/detectSeleniumBackend' @@ -78,6 +78,8 @@ const DEFAULT_CONFIGS = { } const FILE_EXTENSIONS = ['.js', '.ts', '.feature', '.coffee', '.es6'] +const FEATURE_FILE_SPEC_REGEX = /^(.+\.feature):[0-9]+$/ + class ConfigParser { constructor () { this._config = DEFAULT_CONFIGS @@ -273,7 +275,18 @@ class ConfigParser { const filePathList = ConfigParser.getFilePaths(config) for (let file of fileList) { - if (fs.existsSync(file) && fs.lstatSync(file).isFile()) { + const match = file.match(FEATURE_FILE_SPEC_REGEX) + let filename + /* + * check whether a file is a feature file specifying a scenario by its line number + * if this is the case extract filename part + */ + if (match) { + filename = match[1] + } else { + filename = file + } + if (fs.existsSync(filename) && fs.lstatSync(filename).isFile()) { filesToFilter.add(path.resolve(process.cwd(), file)) continue } @@ -309,15 +322,24 @@ class ConfigParser { } for (let pattern of patterns) { - let filenames = glob.sync(pattern) - - filenames = filenames.filter(filename => FILE_EXTENSIONS.includes(path.extname(filename))) - - filenames = filenames.map(filename => - path.isAbsolute(filename) ? path.normalize(filename) : path.resolve(process.cwd(), filename)) - - if (filenames.length === 0 && !omitWarnings) { - console.warn('pattern', pattern, 'did not match any file') + let filenames + /* + * check whether a pattern is a feature file specifying a scenario by its line number + * if this is the case don't glob the pattern + */ + if (pattern.match(FEATURE_FILE_SPEC_REGEX)) { + filenames = [path.isAbsolute(pattern) ? path.normalize(pattern) : path.resolve(process.cwd(), pattern)] + } else { + filenames = glob.sync(pattern) + + filenames = filenames.filter(filename => FILE_EXTENSIONS.includes(path.extname(filename))) + + filenames = filenames.map(filename => + path.isAbsolute(filename) ? path.normalize(filename) : path.resolve(process.cwd(), filename)) + + if (filenames.length === 0 && !omitWarnings) { + console.warn('pattern', pattern, 'did not match any file') + } } files = merge(files, filenames, MERGE_OPTIONS) diff --git a/test/spec/unit/configparser.js b/test/spec/unit/configparser.js index 240d6bc531f..7f92d670718 100644 --- a/test/spec/unit/configparser.js +++ b/test/spec/unit/configparser.js @@ -52,6 +52,15 @@ describe('ConfigParser', () => { specs.should.include(path.resolve(__dirname, '..', 'functional/end.js')) }) + it('should allow to specify a feature file with a scenario line number', () => { + let configParser = new ConfigParser() + configParser.addConfigFile(path.resolve(FIXTURES_PATH, 'exclude.conf.js')) + let featureFileSpec = path.resolve(FIXTURES_PATH, 'dummy.feature:6') + configParser.merge({ specs: featureFileSpec }) + let specs = configParser.getSpecs() + specs.should.include(featureFileSpec) + }) + it('should throw when suite is not defined', () => { let configParser = new ConfigParser() configParser.addConfigFile(path.resolve(FIXTURES_PATH, 'exclude.conf.js'))