diff --git a/package.json b/package.json index 959602e..b6ebb18 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "object" ], "dependencies": { - "@sindresorhus/is": "^0.4.0" + "@sindresorhus/is": "^0.4.0", + "vali-date": "^1.0.0" }, "devDependencies": { "@types/node": "^8.0.31", diff --git a/source/lib/predicates/string.ts b/source/lib/predicates/string.ts index d75dcb8..d7b70e5 100644 --- a/source/lib/predicates/string.ts +++ b/source/lib/predicates/string.ts @@ -1,3 +1,4 @@ +import * as valiDate from 'vali-date'; import { Predicate, Context } from './predicate'; export class StringPredicate extends Predicate { @@ -6,6 +7,18 @@ export class StringPredicate extends Predicate { super('string', context); } + /** + * Test a string to have a specific length. + * + * @param length The length of the string. + */ + length(length: number) { + return this.addValidator({ + message: value => `Expected string to have length \`${length}\`, got \`${value}\``, + validator: value => value.length === length + }); + } + /** * Test a string to have a minimum length. * @@ -13,18 +26,130 @@ export class StringPredicate extends Predicate { */ minLength(length: number) { return this.addValidator({ - message: () => `Expected string length to be minimum ${length}`, + message: value => `Expected string to have a minimum length of \`${length}\`, got \`${value}\``, validator: value => value.length >= length }); } + /** + * Test a string to have a maximum length. + * + * @param length The maximum length of the string. + */ + maxLength(length: number) { + return this.addValidator({ + message: value => `Expected string to have a maximum length of \`${length}\`, got \`${value}\``, + validator: value => value.length <= length + }); + } + + /** + * Test a string against a regular expression. + * + * @param regeExp The regular expression to match the value with. + */ + matches(regExp: RegExp) { + return this.addValidator({ + message: value => `Expected string to match \`${regExp}\`, got \`${value}\``, + validator: value => regExp.test(value) + }); + } + + /** + * Test a string to start with a specific value. + * + * @param searchString The value that should be the start of the string. + */ + startsWith(searchString: string) { + return this.addValidator({ + message: value => `Expected string to start with \`${searchString}\`, got \`${value}\``, + validator: value => value.startsWith(searchString) + }); + } + + /** + * Test a string to end with a specific value. + * + * @param searchString The value that should be the end of the string. + */ + endsWith(searchString: string) { + return this.addValidator({ + message: value => `Expected string to end with \`${searchString}\`, got \`${value}\``, + validator: value => value.endsWith(searchString) + }); + } + + /** + * Test a string to include a specific value. + * + * @param searchString The value that should be included in the string. + */ + includes(searchString: string) { + return this.addValidator({ + message: value => `Expected string to include \`${searchString}\`, got \`${value}\``, + validator: value => value.includes(searchString) + }); + } + + /** + * Test a string to be empty. + */ + get empty() { + return this.addValidator({ + message: value => `Expected string to be empty, got \`${value}\``, + validator: value => value === '' + }); + } + + /** + * Test a string to be not empty. + */ + get nonEmpty() { + return this.addValidator({ + message: () => 'Expected string to not be empty', + validator: value => value !== '' + }); + } + + /** + * Test a string to be equal to a specified string. + * + * @param expected Expected value to match. + */ + equals(expected: string) { + return this.addValidator({ + message: value => `Expected string to be equal to \`${expected}\`, got \`${value}\``, + validator: value => value === expected + }); + } + /** * Test a string to be alphanumeric. */ get alphanumeric() { return this.addValidator({ - message: value => `Expected string to contain only alphanumeric characters but received \`${value}\``, + message: value => `Expected string to be alphanumeric, got \`${value}\``, validator: value => /^[a-z\d]+$/i.test(value) }); } + + /** + * Test a string to be numeric. + */ + get numeric() { + return this.addValidator({ + message: value => `Expected string to be numeric, got \`${value}\``, + validator: value => /^\d+$/i.test(value) + }); + } + + /** + * Test a string to be a valid date. + */ + get date() { + return this.addValidator({ + message: value => `Expected string to be a date, got \`${value}\``, + validator: value => valiDate(value) + }); + } } diff --git a/source/test/string.ts b/source/test/string.ts index 6eae313..50d885a 100644 --- a/source/test/string.ts +++ b/source/test/string.ts @@ -6,13 +6,77 @@ test('string', t => { t.throws(() => m(12, m.string), 'Expected argument to be of type `string` but received type `number`'); }); +test('string.length', t => { + t.notThrows(() => m('foo', m.string.length(3))); + t.notThrows(() => m('foobar', m.string.length(6))); + t.throws(() => m('foo', m.string.length(4)), 'Expected string to have length `4`, got `foo`'); +}); + test('string.minLength', t => { t.notThrows(() => m('foo', m.string.minLength(2))); t.notThrows(() => m('foo', m.string.minLength(3))); - t.throws(() => m('foo', m.string.minLength(4)), 'Expected string length to be minimum 4'); + t.throws(() => m('foo', m.string.minLength(4)), 'Expected string to have a minimum length of `4`, got `foo`'); +}); + +test('string.maxLength', t => { + t.notThrows(() => m('foo', m.string.maxLength(3))); + t.notThrows(() => m('foo', m.string.maxLength(5))); + t.throws(() => m('foo', m.string.maxLength(2)), 'Expected string to have a maximum length of `2`, got `foo`'); +}); + +test('string.matches', t => { + t.notThrows(() => m('foo', m.string.matches(/^f.o$/))); + t.notThrows(() => m('Foo', m.string.matches(/^f.o$/i))); + t.throws(() => m('Foo', m.string.matches(/^f.o$/)), 'Expected string to match `/^f.o$/`, got `Foo`'); + t.throws(() => m('bar', m.string.matches(/^f.o$/i)), 'Expected string to match `/^f.o$/i`, got `bar`'); +}); + +test('string.startsWith', t => { + t.notThrows(() => m('foo', m.string.startsWith('fo'))); + t.notThrows(() => m('foo', m.string.startsWith('f'))); + t.throws(() => m('foo', m.string.startsWith('oo')), 'Expected string to start with `oo`, got `foo`'); + t.throws(() => m('foo', m.string.startsWith('b')), 'Expected string to start with `b`, got `foo`'); +}); + +test('string.endsWith', t => { + t.notThrows(() => m('foo', m.string.endsWith('oo'))); + t.notThrows(() => m('foo', m.string.endsWith('o'))); + t.throws(() => m('foo', m.string.endsWith('fo')), 'Expected string to end with `fo`, got `foo`'); + t.throws(() => m('foo', m.string.endsWith('ar')), 'Expected string to end with `ar`, got `foo`'); +}); + +test('string.includes', t => { + t.notThrows(() => m('foo', m.string.includes('fo'))); + t.throws(() => m('foo', m.string.includes('bar')), 'Expected string to include `bar`, got `foo`'); +}); + +test('string.empty', t => { + t.notThrows(() => m('', m.string.empty)); + t.throws(() => m('foo', m.string.empty), 'Expected string to be empty, got `foo`'); +}); + +test('string.nonEmpty', t => { + t.notThrows(() => m('foo', m.string.nonEmpty)); + t.throws(() => m('', m.string.nonEmpty), 'Expected string to not be empty'); +}); + +test('string.equals', t => { + t.notThrows(() => m('foo', m.string.equals('foo'))); + t.throws(() => m('bar', m.string.equals('foo')), 'Expected string to be equal to `foo`, got `bar`'); }); test('string.alphanumeric', t => { t.notThrows(() => m('Foo123', m.string.alphanumeric)); - t.throws(() => m('Foo123!', m.string.alphanumeric), 'Expected string to contain only alphanumeric characters but received `Foo123!`'); + t.throws(() => m('Foo123!', m.string.alphanumeric), 'Expected string to be alphanumeric, got `Foo123!`'); +}); + +test('string.numeric', t => { + t.notThrows(() => m('123', m.string.numeric)); + t.throws(() => m('Foo123', m.string.numeric), 'Expected string to be numeric, got `Foo123`'); +}); + +test('string.date', t => { + t.notThrows(() => m('2017-03-02', m.string.date)); + t.notThrows(() => m('2017-03-02T10:00:00Z', m.string.date)); + t.throws(() => m('foo', m.string.date), 'Expected string to be a date, got `foo`'); });