Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions lib/any.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions lib/language.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
17 changes: 16 additions & 1 deletion lib/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -488,7 +504,6 @@ internals.String = class extends Any {
obj._flags.truncate = enabled === undefined ? true : !!enabled;
return obj;
}

};

internals.compare = function (type, compare) {
Expand Down
51 changes: 51 additions & 0 deletions test/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()', () => {
Expand Down Expand Up @@ -3524,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();
});
});
});