-
-
Notifications
You must be signed in to change notification settings - Fork 422
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
Feature: Config validation #141
Conversation
Codecov Report
@@ Coverage Diff @@
## master #141 +/- ##
===========================================
- Coverage 100% 89.44% -10.56%
===========================================
Files 5 10 +5
Lines 65 161 +96
Branches 8 23 +15
===========================================
+ Hits 65 144 +79
- Misses 0 16 +16
- Partials 0 1 +1
Continue to review full report at Codecov.
|
src/generateTasks.js
Outdated
|
||
module.exports = function generateTasks(config, files) { | ||
const linters = config.linters !== undefined ? config.linters : config | ||
const resolve = file => files[file] | ||
const { linters, gitDir, globOptions } = getConfig(config) // Ensure we have a normalized config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is incompatible with node 4, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, the import syntax
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, destructuring is not supported in node 4.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that sucks. I will need to update eslint node plugin settings...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see an engines
field in package.json.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was under impression I already have it in this project but I don't. See #243
src/getConfig.js
Outdated
*/ | ||
if (isGlob(option)) { | ||
const message = ` | ||
Unknown option ${chalk.bold(`"${option}"`)} with value ${chalk.bold( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, "Unknown filter..." is better, to match https://github.com/okonet/lint-staged#filtering-files
src/getConfig.js
Outdated
* a typical mistake of mixing simple and advanced configs | ||
*/ | ||
if (isGlob(option)) { | ||
const message = ` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new line should be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than a couple of edge cases for tests, LGTM
src/getConfig.js
Outdated
const intersection = require('lodash/intersection') | ||
const defaultsDeep = require('lodash/defaultsDeep') | ||
const isObject = require('lodash/isObject') | ||
const validate = require('jest-validate').validate | ||
const unknownOptionWarning = require('jest-validate/build/warnings').unknownOptionWarning | ||
const logValidationWarning = require('jest-validate/build/utils').logValidationWarning |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can change this to const logValidationWarning = require('jest-validate').logValidationWarning
src/getConfig.js
Outdated
@@ -23,6 +29,13 @@ const defaultConfig = { | |||
verbose: false | |||
} | |||
|
|||
const exampleConfig = Object.assign({}, defaultConfig, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is very minor and optional(obviously).
Can be changed to:
const exampleConfig = Object.assign(
{
linters: {
'*.js': ['eslint --fix', 'git add'],
'*.css': 'stylelint'
}
},
defaultConfig
)
src/getConfig.js
Outdated
|
||
const comment = options.comment | ||
const name = (options.title && options.title.warning) || 'WARNING' | ||
logValidationWarning(name, message, comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a return after this? I haven't used jest-validate
before so not too sure about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! Added. Thanks!
test/getConfig.spec.js
Outdated
renderer: 'custom', | ||
verbose: true | ||
} | ||
expect(getConfig(src)).toEqual(src) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If src
was being mutated by the getConfig
, this test would still pass. How about we use lodash#cloneDeep
to pass a copy of src
to getConfig
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. Nice catch!
test/getConfig.spec.js
Outdated
expect(console.printHistory()).toMatchSnapshot() | ||
}) | ||
|
||
it('should not throw and print nothing for valid config', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A small gripe but this test title is ambiguous. Should it not throw and not print nothing? should not throw and should print nothing for valid config
is clearer.
'*.js': ['eslint --fix', 'git add'] | ||
} | ||
expect(() => validateConfig(getConfig(validSimpleConfig))).not.toThrow() | ||
expect(console.printHistory()).toMatchSnapshot() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add a validate for advanced config?
* a typical mistake of mixing simple and advanced configs | ||
*/ | ||
if (isGlob(option)) { | ||
const message = ` Unknown option ${chalk.bold(`"${option}"`)} with value ${chalk.bold( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realised this would not be correct, ignore.
This fails the snapshot right now but could we change this to the following:
const formattedOption = format(config[option], {
inlineCharacterLimit: Number.POSITIVE_INFINITY
})
const message = ` Unknown option ${chalk.bold(`"${option}"`)} with value ${chalk.bold(
formattedOption
)} was found in the config root.
You probably trying to mix simple and advanced config formats.
Adding
${chalk.bold(`"linters": {
"${option}": ${formattedOption}
}`)}
will fix it and remove this message.`
Also, You probably trying ➡ You are probably trying
.
Edit: Just realised, the message printed is not valid JSON. Should be changed to the following:
const message = ` Unknown option ${chalk.bold(`"${option}"`)} with value ${chalk.bold(
format(config[option], { inlineCharacterLimit: Number.POSITIVE_INFINITY })
)} was found in the config root.
You probably trying to mix simple and advanced config formats.
Adding
${chalk.bold(
`"linters": ${JSON.stringify({
[option]: config[option]
})}`
)})
will fix it and remove this message.`
src/getConfig.js
Outdated
|
||
${chalk.bold(`"linters": { | ||
"${option}": ${format(config[option], { inlineCharacterLimit: Number.POSITIVE_INFINITY }, ' ')} | ||
"${option}": ${JSON.stringify(config[option], null, '\t ')} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This results in inconsistent indentation. Massaged snapshot equivalent:
"linters": {
"*.js": [
"eslint --fix",
"git add"
]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I've noticed that. Do you know how to fix that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't add the indentation while formatting using JSON.stringify
. This will print the commands on a single line but that's okay I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, okay, I guess that can be improved later on
src/getConfig.js
Outdated
logValidationWarning(name, message, comment) | ||
} | ||
// If it is not glob pattern, when use default jest-validate reporter | ||
return unknownOptionWarning |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unknownOptionWarning
is a function. So should we be invoking it here instead of returning it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right! I thought I've updated that already :-/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mentioned this before but a return
after logValidationWarning
is also required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See 0d210f4
@luftywiranda13 @sudo-suhas I've updated the branch. I'd like to merge it today. Thanks for reviews. That was very valuable feedback. Final "approve", please? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
test/getConfig.spec.js
Outdated
}) | ||
|
||
it('should not throw and should print nothing for advanced valid config', () => { | ||
const validSimpleConfig = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you rename this to validAdvancedConfig
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
test/getConfig.spec.js
Outdated
expect(() => validateConfig(getConfig(invalidAdvancedConfig))).toThrowErrorMatchingSnapshot() | ||
}) | ||
|
||
it('should not throw and print validation warnings for mixed config', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change title similarly - should not throw and should print validation warnings for mixed config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks super great to me 😁
🍾 |
@okonet @sudo-suhas |
@luftywiranda13 No need to apologise. We are doing this in whatever time we can find. I am sure you'll fill in for us when needed. |
@luftywiranda13 I'm doubling it: no need to apologize! Thanks for all you help so far and I'm looking forward for more good stuff coming from you! Enjoy your time! |
This PR implements config validation 🎉
Why
Invalid config is a constant source of issues for this library. To reduce the amount of such tickets and to make deprecations easier, I've introduced
jest-validate
to this repository. Additionally, lots of refactoring and additional tests were added in the process.Previous scope:
This PR refactors the code in a way it's more testable and adds tests for uncovered parts. One big change is thegetConfig
function that is solely concerned about how configuration is done. This is the place where we will add config validation in the future.The coverage went down because of this PR but in reality it should have been increased. TherunAll
function has least coverage right now and this is where I want to spend some time after this is merged.