From 6f358621c09e5c2d9a9f915509aa5f57fb4a2902 Mon Sep 17 00:00:00 2001 From: Wes Tyler Date: Fri, 11 Nov 2016 19:23:40 -0600 Subject: [PATCH 1/2] Support RegExp in string.invalid --- lib/any.js | 10 ++++++++-- lib/language.js | 4 ++++ lib/string.js | 17 ++++++++++++++++- test/string.js | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/lib/any.js b/lib/any.js index 83b77cd8d..5e453a354 100755 --- a/lib/any.js +++ b/lib/any.js @@ -255,14 +255,20 @@ module.exports = internals.Any = class { invalid(value) { - const obj = this.clone(); + let obj = this.clone(); const values = Hoek.flatten(Array.prototype.slice.call(arguments)); + for (let i = 0; i < values.length; ++i) { value = values[i]; Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined'); obj._valids.remove(value); - obj._invalids.add(value, this._refs); + if (value instanceof RegExp && typeof obj._regexInvalid === 'function') { + obj = obj._regexInvalid(value); + } + else { + obj._invalids.add(value, this._refs); + } } return obj; diff --git a/lib/language.js b/lib/language.js index 88f5f1fbb..9a070c4e2 100755 --- a/lib/language.js +++ b/lib/language.js @@ -124,6 +124,10 @@ exports.errors = { base: 'with value "{{!value}}" fails to match the required pattern: {{pattern}}', name: 'with value "{{!value}}" fails to match the {{name}} pattern' }, + regexInvalid: { + base: 'with value "{{!value}}" matches the invalid pattern: {{pattern}}', + name: 'with value "{{!value}}" matches the invalid {{name}} pattern' + }, email: 'must be a valid email', uri: 'must be a valid uri', uriCustomScheme: 'must be a valid uri with a scheme matching the {{scheme}} pattern', diff --git a/lib/string.js b/lib/string.js index b65bc3f3d..dea6ca0bb 100755 --- a/lib/string.js +++ b/lib/string.js @@ -107,6 +107,22 @@ internals.String = class extends Any { }); } + _regexInvalid(pattern) { + + Hoek.assert(pattern instanceof RegExp, 'pattern must be a RegExp'); + + pattern = new RegExp(pattern.source, pattern.ignoreCase ? 'i' : undefined); // Future version should break this and forbid unsupported regex flags + + return this._test('regexInvalid', pattern, function (value, state, options) { + + if (pattern.test(value)) { + return this.createError('string.regexInvalid.base', { pattern, value }, state, options); + } + + return value; + }); + } + alphanum() { return this._test('alphanum', undefined, function (value, state, options) { @@ -488,7 +504,6 @@ internals.String = class extends Any { obj._flags.truncate = enabled === undefined ? true : !!enabled; return obj; } - }; internals.compare = function (type, compare) { diff --git a/test/string.js b/test/string.js index dbf5cccd9..c111c92f5 100755 --- a/test/string.js +++ b/test/string.js @@ -100,6 +100,43 @@ describe('string', () => { ['B', false, null, '"value" contains an invalid value'] ], done); }); + + it('invalidates RegExp patterns', (done) => { + + Helper.validate(Joi.string().invalid(/[a-bA-B]/i), [ + ['c', true], + ['a', false, null, '"value" with value "a" matches the invalid pattern: /[a-bA-B]/i'], + ['b', false, null, '"value" with value "b" matches the invalid pattern: /[a-bA-B]/i'], + ['A', false, null, '"value" with value "A" matches the invalid pattern: /[a-bA-B]/i'], + ['B', false, null, '"value" with value "B" matches the invalid pattern: /[a-bA-B]/i'] + ], done); + }); + }); + + describe('not', () => { + + it('invalidates RegExp patterns', (done) => { + + Helper.validate(Joi.string().not(/[a-bA-B]/), [ + ['a', false, null, '"value" with value "a" matches the invalid pattern: /[a-bA-B]/'], + ['b', false, null, '"value" with value "b" matches the invalid pattern: /[a-bA-B]/'], + ['A', false, null, '"value" with value "A" matches the invalid pattern: /[a-bA-B]/'], + ['B', false, null, '"value" with value "B" matches the invalid pattern: /[a-bA-B]/'] + ], done); + }); + }); + + describe('disallow', () => { + + it('invalidates RegExp patterns', (done) => { + + Helper.validate(Joi.string().disallow(/[a-bA-B]/), [ + ['a', false, null, '"value" with value "a" matches the invalid pattern: /[a-bA-B]/'], + ['b', false, null, '"value" with value "b" matches the invalid pattern: /[a-bA-B]/'], + ['A', false, null, '"value" with value "A" matches the invalid pattern: /[a-bA-B]/'], + ['B', false, null, '"value" with value "B" matches the invalid pattern: /[a-bA-B]/'] + ], done); + }); }); describe('min()', () => { From ccd018e00df8f230aee153d48761a672748c6b59 Mon Sep 17 00:00:00 2001 From: Wes Tyler Date: Fri, 11 Nov 2016 21:12:27 -0600 Subject: [PATCH 2/2] Add string regexInvalid description test --- test/string.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/string.js b/test/string.js index 66fc60ab7..cbe104695 100755 --- a/test/string.js +++ b/test/string.js @@ -3561,5 +3561,19 @@ describe('string', () => { }); done(); }); + + it('describes invalid RegExp patterns', (done) => { + + const schema = Joi.string().regex(/[0-9]/).invalid(/[a-zA-Z]/); + const description = schema.describe(); + + expect(description.rules).to.be.an.array(); + expect(description.rules.length).to.equal(2); + expect(description.rules.filter((rule) => { + + return rule.name === 'regexInvalid'; + }).length).to.equal(1); + done(); + }); }); });