Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: CLI filtering --spec when commas in glob pattern #19557

Merged
merged 20 commits into from Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
116 changes: 114 additions & 2 deletions packages/server/lib/util/args.js
Expand Up @@ -204,6 +204,118 @@ const sanitizeAndConvertNestedArgs = (str, argname) => {
}
}

/**
* Parses the '--spec' cli parameter to return an array of valid patterns.
*
* @param {Strng} pattern pattern to parse
* @returns Array of patterns
*/
const parseSpecArgv = (pattern) => {
hang-up marked this conversation as resolved.
Show resolved Hide resolved
const TOKENS = {
OPEN: ['{', '['],
CLOSE: ['}', ']'],
}
const hasToken = [...TOKENS.OPEN, ...TOKENS.CLOSE].some((t) => {
return pattern.includes(t)
})
const hasComma = pattern.includes(',')

/**
* Slice & mutate a string.
mjhenkes marked this conversation as resolved.
Show resolved Hide resolved
*
* @param {String} str String to slice & mutate
* @param {Number} end Index to slice to
* @returns [String, String, Number]
*/
const sliceAndMutate = (str, end) => {
return [
str.slice(0, end),
str.substring(end, str.length),
str.slice(0, end).length,
]
}

/**
* Sanitizes a path's leftover commas.
*
* @param {String} path
* @returns String
*/
const sanitizeFinalPath = (path) => {
return path.split('')[0] === ',' ? path.substring(1, path.length) : path
}

if (!hasToken) {
return [].concat(pattern.split(','))
}

if (!hasComma) {
return pattern
}

// Get comma rules.
let opens = []
let closes = []
const rules = pattern
.split('')
.map((token, index) => {
if (TOKENS.OPEN.includes(token)) {
opens.push(index)
}

if (TOKENS.CLOSE.includes(token)) {
closes.push(index)
}

if (token === ',') {
const isBreakable =
index > opens[opens.length - 1] &&
index > closes[closes.length - 1] &&
opens.length === closes.length

if (isBreakable) {
return {
comma: index,
isBreakable: true,
}
}

return {
comma: index,
isBreakable: false,
}
}

return null
})
.filter(Boolean)

// Perform comma breaking logic.
let carry = pattern
let offset = 0
const partial = rules
.map((rule) => {
if (!rule.isBreakable) {
return null
}

const [res, mutated, offsettedBy] = sliceAndMutate(
carry,
rule.comma - offset,
)

offset = offsettedBy
carry = mutated

return res
})
.filter(Boolean)
.map(sanitizeFinalPath)

// In the end, carry will be left with the last path that hasn't been cut.
return [...partial, sanitizeFinalPath(carry)]
}

module.exports = {
normalizeBackslashes,

Expand Down Expand Up @@ -303,12 +415,12 @@ module.exports = {
spec = spec.substring(1, spec.length - 1)
}

options.spec = strToArray(spec).map(resolvePath)
options.spec = parseSpecArgv(spec).map(resolvePath)
} else {
options.spec = spec.map(resolvePath)
}
} catch (err) {
debug('could not pass config spec value %s', spec)
debug('could not parse config spec value %s', spec)
debug('error %o', err)

return errors.throw('COULD_NOT_PARSE_ARGUMENTS', 'spec', spec, 'spec must be a string or comma-separated list')
Expand Down
13 changes: 13 additions & 0 deletions packages/server/test/unit/util/args_spec.js
Expand Up @@ -164,6 +164,19 @@ describe('lib/util/args', () => {
return snapshot('invalid spec error', stripAnsi(err.message))
}
})

it('should be correctly parsing globs with lists & ranges', function () {
const options = this.setup('--spec', 'cypress/integration/{[!a]*.spec.js,sub1,{sub2,sub3/sub4}}/*.js')

expect(options.spec[0]).to.eq(`${cwd}/cypress/integration/{[!a]*.spec.js,sub1,{sub2,sub3/sub4}}/*.js`)
})

it('should be correctly parsing globs with a mix of lists, ranges & regular paths', function () {
const options = this.setup('--spec', 'cypress/integration/{[!a]*.spec.js,sub1,{sub2,sub3/sub4}}/*.js,cypress/integration/foo.spec.js')

expect(options.spec[0]).to.eq(`${cwd}/cypress/integration/{[!a]*.spec.js,sub1,{sub2,sub3/sub4}}/*.js`)
expect(options.spec[1]).to.eq(`${cwd}/cypress/integration/foo.spec.js`)
})
})

context('--tag', () => {
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Expand Up @@ -23013,9 +23013,9 @@ is-glob@^3.0.0, is-glob@^3.1.0:
is-extglob "^2.1.0"

is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"

Expand Down