Browse Source

Infer label automatically on Node.js and remove the `.label()` method (#102)

Fixes #98
Fixes #89
master
Sam Verschueren 6 years ago
committed by Sindre Sorhus
parent
commit
7eb02aff95
  1. 6
      package.json
  2. 42
      readme.md
  3. 41
      source/index.ts
  4. 4
      source/lib/predicates/any.ts
  5. 7
      source/lib/predicates/base-predicate.ts
  6. 25
      source/lib/predicates/predicate.ts
  7. 62
      source/lib/utils/infer-label.ts
  8. 19
      source/lib/utils/is-valid-identifier.ts
  9. 4
      source/lib/utils/node/fs.ts
  10. 4
      source/lib/utils/node/require.ts
  11. 12
      source/test/any.ts
  12. 4
      source/test/array-buffer.ts
  13. 13
      source/test/array.ts
  14. 8
      source/test/boolean.ts
  15. 3
      source/test/buffer.ts
  16. 3
      source/test/data-view.ts
  17. 8
      source/test/date.ts
  18. 21
      source/test/error.ts
  19. 6
      source/test/fixtures/create-error.ts
  20. 3
      source/test/function.ts
  21. 42
      source/test/infer-label.ts
  22. 3
      source/test/iterable.ts
  23. 16
      source/test/map.ts
  24. 3
      source/test/nan.ts
  25. 3
      source/test/null-or-undefined.ts
  26. 3
      source/test/null.ts
  27. 8
      source/test/number.ts
  28. 18
      source/test/object.ts
  29. 3
      source/test/promise.ts
  30. 3
      source/test/regexp.ts
  31. 12
      source/test/set.ts
  32. 16
      source/test/string.ts
  33. 3
      source/test/symbol.ts
  34. 29
      source/test/test.ts
  35. 3
      source/test/typed-array.ts
  36. 6
      source/test/undefined.ts
  37. 8
      source/test/weak-map.ts
  38. 8
      source/test/weak-set.ts
  39. 2
      source/types.d.ts

6
package.json

@ -56,9 +56,11 @@
"add-module-exports-webpack-plugin": "^0.1.0",
"ava": "^1.0.1",
"awesome-typescript-loader": "^5.2.1",
"callsites": "^3.0.0",
"codecov": "^3.1.0",
"del-cli": "^1.1.0",
"dot-prop": "^4.2.0",
"is-node": "^1.0.2",
"license-webpack-plugin": "^2.0.2",
"lodash.isequal": "^4.5.0",
"nyc": "^13.1.0",
@ -72,6 +74,10 @@
},
"typings": "dist/index.d.ts",
"sideEffects": false,
"ava": {
"babel": false,
"compileEnhancements": false
},
"nyc": {
"exclude": [
"dist/test"

42
readme.md

@ -14,6 +14,7 @@
- Expressive chainable API
- Lots of built-in validations
- Supports custom validations
- Automatic label inference in Node.js
- Written in TypeScript
@ -36,10 +37,10 @@ const unicorn = input => {
};
unicorn(3);
//=> ArgumentError: Expected argument to be of type `string` but received type `number`
//=> ArgumentError: Expected `input` to be of type `string` but received type `number`
unicorn('yo');
//=> ArgumentError: Expected string to have a minimum length of `5`, got `yo`
//=> ArgumentError: Expected string `input` to have a minimum length of `5`, got `yo`
```
@ -49,7 +50,13 @@ unicorn('yo');
### ow(value, predicate)
Test if `value` matches the provided `predicate`. Throws an `ArgumentError` if the test fails.
Test if `value` matches the provided `predicate`. Throws an `ArgumentError` if the test fails.
### ow(value, label, predicate)
Test if `value` matches the provided `predicate`. Throws an `ArgumentError` with the specified `label` if the test fails.
The `label` is automatically inferred in Node.js but you can override it by passing in a value for `label`. The automatic label inference doesn't work in the browser.
### ow.isValid(value, predicate)
@ -62,8 +69,21 @@ Create a reusable validator.
```ts
const checkPassword = ow.create(ow.string.minLength(6));
const password = 'foo';
checkPassword(password);
//=> ArgumentError: Expected string `password` to have a minimum length of `6`, got `foo`
```
### ow.create(label, predicate)
Create a reusable validator with a specific `label`.
```ts
const checkPassword = ow.create('password', ow.string.minLength(6));
checkPassword('foo');
//=> ArgumentError: Expected string to have a minimum length of `6`, got `foo`
//=> ArgumentError: Expected string `password` to have a minimum length of `6`, got `foo`
```
### ow.any(...predicate[])
@ -163,20 +183,6 @@ ow(5, ow.number.is(x => greaterThan(10, x)));
//=> ArgumentError: Expected `5` to be greater than `10`
```
#### label(string)
This assigns a custom label to be used in any error messages. It is useful for making error messages more user-friendly by including the name of the variable which failed validation.
This predicate does not add any additional validation.
```ts
ow('', ow.string.nonEmpty);
//=> ArgumentError: Expected string to not be empty
ow('', ow.string.label('foo').nonEmpty);
//=> ArgumentError: Expected string `foo` to not be empty
```
## Maintainers

41
source/index.ts

@ -1,6 +1,8 @@
import callsites from 'callsites';
import {inferLabel} from './lib/utils/infer-label';
import {Predicate} from './lib/predicates/predicate';
import {AnyPredicate} from './lib/predicates/any';
import {testSymbol, BasePredicate} from './lib/predicates/base-predicate';
import {testSymbol, BasePredicate, isPredicate} from './lib/predicates/base-predicate';
import {StringPredicate} from './lib/predicates/string';
import {NumberPredicate} from './lib/predicates/number';
import {BooleanPredicate} from './lib/predicates/boolean';
@ -17,12 +19,20 @@ type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint
export interface Ow {
/**
* Test if the value matches the predicate. Throws an `ArgumentError` if the test fails..
* Test if the value matches the predicate. Throws an `ArgumentError` if the test fails.
*
* @param value Value to test.
* @param predicate Predicate to test against.
*/
<T>(value: T, predicate: BasePredicate<T>): void;
/**
* Test if `value` matches the provided `predicate`. Throws an `ArgumentError` with the specified `label` if the test fails.
*
* @param value Value to test.
* @param label Label which should be used in error messages.
* @param predicate Predicate to test against.
*/
<T>(value: T, label: string, predicate: BasePredicate<T>): void;
/**
* Test the value to be a string.
*/
@ -168,6 +178,13 @@ export interface Ow {
* @param predicate Predicate used in the validator function.
*/
create<T>(predicate: BasePredicate<T>): (value: T) => void;
/**
* Create a reusable validator.
*
* @param label Label which should be used in error messages.
* @param predicate Predicate used in the validator function.
*/
create<T>(label: string, predicate: BasePredicate<T>): (value: T) => void;
/**
* Test that the value matches at least one of the given predicates.
*/
@ -184,7 +201,17 @@ export interface Ow {
any(...predicate: BasePredicate[]): AnyPredicate;
}
const main = <T>(value: T, predicate: BasePredicate<T>) => (predicate as any)[testSymbol](value, main);
const main = <T>(value: T, labelOrPredicate: BasePredicate<T> | string | undefined, predicate?: BasePredicate<T>) => {
let label: any = labelOrPredicate;
let testPredicate: any = predicate;
if (isPredicate(labelOrPredicate)) {
label = inferLabel(callsites());
testPredicate = labelOrPredicate;
}
return testPredicate[testSymbol](value, main, label);
};
Object.defineProperties(main, {
isValid: {
@ -198,7 +225,13 @@ Object.defineProperties(main, {
}
},
create: {
value: <T>(predicate: BasePredicate<T>) => (value: T) => main(value, predicate)
value: <T>(labelOrPredicate: BasePredicate<T> | string | undefined, predicate?: BasePredicate<T>) => (value: T) => {
if (isPredicate(labelOrPredicate)) {
return main(value, inferLabel(callsites()), labelOrPredicate);
}
return main(value, labelOrPredicate, predicate);
}
},
any: {
value: (...predicates: BasePredicate[]) => new AnyPredicate(predicates)

4
source/lib/predicates/any.ts

@ -11,14 +11,14 @@ export class AnyPredicate<T = any> implements BasePredicate<T> {
) {}
// tslint:disable completed-docs
[testSymbol](value: T, main: Ow) {
[testSymbol](value: T, main: Ow, label?: string) {
const errors = [
'Any predicate failed with the following errors:'
];
for (const predicate of this.predicates) {
try {
main(value, predicate);
main(value, label as string, predicate);
return;
} catch (err) {

7
source/lib/predicates/base-predicate.ts

@ -5,10 +5,15 @@ import {Ow} from '../..';
*/
export const testSymbol = Symbol('test');
/**
* @hidden
*/
export const isPredicate = (value: any): value is BasePredicate => Boolean(value && value[testSymbol]);
/**
* @hidden
*/
export interface BasePredicate<T = any> {
// tslint:disable-next-line completed-docs
[testSymbol](value: T, main: Ow): void;
[testSymbol](value: T, main: Ow, label?: string): void;
}

25
source/lib/predicates/predicate.ts

@ -41,10 +41,11 @@ export class Predicate<T = any> implements BasePredicate<T> {
const x = this.type[0].toLowerCase() + this.type.slice(1);
this.addValidator({
message: value => {
message: (value, label) => {
// We do not include type in this label as we do for other messages, because it would be redundant.
const label = this.context.label || 'argument';
return `Expected ${label} to be of type \`${this.type}\` but received type \`${is(value)}\``;
const lbl = label && label.substring(this.type.length + 1);
return `Expected ${lbl || 'argument'} to be of type \`${this.type}\` but received type \`${is(value)}\``;
},
validator: value => (is as any)[x](value)
});
@ -54,9 +55,9 @@ export class Predicate<T = any> implements BasePredicate<T> {
* @hidden
*/
// tslint:disable completed-docs
[testSymbol](value: T, main: Ow) {
const label = this.context.label
? `${this.type} ${this.context.label}`
[testSymbol](value: T, main: Ow, label?: string) {
const label2 = label
? `${this.type} \`${label}\``
: this.type;
for (const {validator, message} of this.context.validators) {
@ -64,7 +65,7 @@ export class Predicate<T = any> implements BasePredicate<T> {
if (typeof result !== 'boolean' || !result) {
// TODO: Modify the stack output to show the original `ow()` call instead of this `throw` statement
throw new ArgumentError(message(value, label, result), main);
throw new ArgumentError(message(value, label2, result), main);
}
}
}
@ -83,16 +84,6 @@ export class Predicate<T = any> implements BasePredicate<T> {
return not(this);
}
/**
* Assign a label to this predicate for use in error messages.
*
* @param value Label to assign.
*/
label(value: string) {
this.context.label = `\`${value}\``;
return this;
}
/**
* Test if the value matches a custom validation function. The validation function should return `true` if the value
* passes the function. If the function either returns `false` or a string, the function fails and the string will be

62
source/lib/utils/infer-label.ts

@ -0,0 +1,62 @@
import fs from './node/fs';
import {CallSite} from 'callsites';
import * as isNode from 'is-node';
import isValidIdentifier from './is-valid-identifier';
// Regex to extract the label out of the `ow` function call
const labelRegex = /^.*?\((.*?)[,)]/;
/**
* Infer the label of the caller.
*
* @param callsites - List of stack frames.
*/
export const inferLabel = (callsites: CallSite[]) => {
if (!isNode) {
// Exit if we are not running in a Node.js environment
return;
}
// Grab the stackframe with the `ow` function call
const functionCallStackFrame = callsites[1];
const fileName = functionCallStackFrame.getFileName();
const lineNumber = functionCallStackFrame.getLineNumber();
const columnNumber = functionCallStackFrame.getColumnNumber();
if (!fileName || lineNumber === null || columnNumber === null) {
return;
}
let content: string[] = [];
try {
content = (fs.readFileSync(fileName, 'utf8') as string).split('\n');
} catch {
return;
}
let line = content[lineNumber - 1];
if (!line) {
// Exit if the line number couldn't be found
return;
}
line = line.slice(columnNumber - 1);
const match = labelRegex.exec(line);
if (!match || !match[1]) {
// Exit if we didn't find a label
return;
}
const token = match[1];
if (isValidIdentifier(token) || isValidIdentifier(token.split('.').pop())) {
return token;
}
return;
};

19
source/lib/utils/is-valid-identifier.ts

@ -0,0 +1,19 @@
const identifierRegex = /^[a-z$_][a-z$_0-9]*$/i;
const reservedSet = new Set([
'undefined',
'null',
'true',
'false',
'super',
'this',
'Infinity',
'NaN'
]);
/**
* Test if the string is a valid JavaScript identifier.
*
* @param input String to test.
*/
export default (input: string | undefined) => input && !reservedSet.has(input) && identifierRegex.test(input);

4
source/lib/utils/node/fs.ts

@ -0,0 +1,4 @@
import nodeRequire from './require';
// Re-export the Node.js `fs` module to make sure it doesn't get bundled with front-end apps
export default nodeRequire('fs');

4
source/lib/utils/node/require.ts

@ -0,0 +1,4 @@
// Export `__non_webpack_require__` in Webpack environments to make sure it doesn't bundle modules loaded via this method
export default typeof (global as any).__non_webpack_require__ === 'function'
? (global as any).__non_webpack_require__
: eval('require'); // tslint:disable-line:no-eval

12
source/test/any.ts

@ -1,20 +1,14 @@
import test from 'ava';
import m from '..';
export const createError = (...errors: string[]) => {
return [
'Any predicate failed with the following errors:',
...errors.map(error => `- ${error}`)
].join('\n');
};
import {createAnyError} from './fixtures/create-error';
test('any', t => {
t.notThrows(() => m(1, m.any(m.number)));
t.notThrows(() => m(1, m.any(m.number, m.string)));
t.notThrows(() => m(1, m.any(m.number, m.string)));
t.notThrows(() => m(true, m.any(m.number, m.string, m.boolean)));
t.throws(() => m(1 as any, m.any(m.string)), createError('Expected argument to be of type `string` but received type `number`'));
t.throws(() => m(true as any, m.any(m.number, m.string)), createError(
t.throws(() => m(1 as any, m.any(m.string)), createAnyError('Expected argument to be of type `string` but received type `number`'));
t.throws(() => m(true as any, m.any(m.number, m.string)), createAnyError(
'Expected argument to be of type `number` but received type `boolean`',
'Expected argument to be of type `string` but received type `boolean`'
));

4
source/test/array-buffer.ts

@ -3,8 +3,8 @@ import m from '..';
test('arrayBuffer', t => {
t.notThrows(() => m(new ArrayBuffer(1), m.arrayBuffer));
t.notThrows(() => m(new ArrayBuffer(1), m.arrayBuffer.label('foo')));
t.notThrows(() => m(new ArrayBuffer(1), 'foo', m.arrayBuffer));
t.throws(() => m('foo' as any, m.arrayBuffer), 'Expected argument to be of type `ArrayBuffer` but received type `string`');
t.throws(() => m('foo' as any, m.arrayBuffer.label('foo')), 'Expected `foo` to be of type `ArrayBuffer` but received type `string`');
t.throws(() => m('foo' as any, 'foo', m.arrayBuffer), 'Expected `foo` to be of type `ArrayBuffer` but received type `string`');
t.throws(() => m(12 as any, m.arrayBuffer), 'Expected argument to be of type `ArrayBuffer` but received type `number`');
});

13
source/test/array.ts

@ -3,19 +3,16 @@ import m from '..';
test('array', t => {
t.notThrows(() => m([], m.array));
t.notThrows(() => m([], m.array.label('foo')));
t.notThrows(() => m([], 'foo', m.array));
t.throws(() => m('12' as any, m.array), 'Expected argument to be of type `array` but received type `string`');
t.throws(() => m('12' as any, m.array.label('foo')), 'Expected `foo` to be of type `array` but received type `string`');
t.throws(() => m('12' as any, 'foo', m.array), 'Expected `foo` to be of type `array` but received type `string`');
});
test('array.length', t => {
t.notThrows(() => m(['foo'], m.array.length(1)));
t.notThrows(() => m(['foo', 'bar'], m.array.length(2)));
t.notThrows(() => m(['foo', 'bar'], m.array.label('foo').length(2)));
t.notThrows(() => m(['foo', 'bar'], m.array.length(2).label('foo')));
t.throws(() => m(['foo'], m.array.length(2)), 'Expected array to have length `2`, got `1`');
t.throws(() => m(['foo'], m.array.label('foo').length(2)), 'Expected array `foo` to have length `2`, got `1`');
t.throws(() => m(['foo'], m.array.length(2).label('foo')), 'Expected array `foo` to have length `2`, got `1`');
t.throws(() => m(['foo'], 'foo', m.array.length(2)), 'Expected array `foo` to have length `2`, got `1`');
});
test('array.minLength', t => {
@ -71,8 +68,6 @@ test('array.deepEqual', t => {
test('array.ofType', t => {
t.notThrows(() => m(['foo', 'bar'], m.array.ofType(m.string)));
t.notThrows(() => m(['foo', 'bar'], m.array.ofType(m.string.minLength(3))));
t.notThrows(() => m(['foo', 'bar'], m.array.label('foo').ofType(m.string.minLength(3))));
t.throws(() => m(['foo', 'b'], m.array.ofType(m.string.minLength(3))), '(array) Expected string to have a minimum length of `3`, got `b`');
t.throws(() => m(['foo', 'b'], m.array.label('foo').ofType(m.string.minLength(3))), '(array `foo`) Expected string to have a minimum length of `3`, got `b`');
t.throws(() => m(['foo', 'b'], m.array.label('foo').ofType(m.string.label('bar').minLength(3))), '(array `foo`) Expected string `bar` to have a minimum length of `3`, got `b`');
t.throws(() => m(['foo', 'b'], 'foo', m.array.ofType(m.string.minLength(3))), '(array `foo`) Expected string to have a minimum length of `3`, got `b`');
});

8
source/test/boolean.ts

@ -3,20 +3,16 @@ import m from '..';
test('boolean', t => {
t.notThrows(() => m(true, m.boolean));
t.notThrows(() => m(true, m.boolean.label('foo')));
t.throws(() => m('12' as any, m.boolean), 'Expected argument to be of type `boolean` but received type `string`');
t.throws(() => m('12' as any, m.boolean.label('foo')), 'Expected `foo` to be of type `boolean` but received type `string`');
t.throws(() => m('12' as any, 'foo', m.boolean), 'Expected `foo` to be of type `boolean` but received type `string`');
});
test('boolean.true', t => {
t.notThrows(() => m(true, m.boolean.true));
t.notThrows(() => m(Boolean(true), m.boolean.true));
t.notThrows(() => m(Boolean(1), m.boolean.true));
t.notThrows(() => m(Boolean(1), m.boolean.label('foo').true));
t.notThrows(() => m(Boolean(1), m.boolean.true.label('foo')));
t.throws(() => m(false, m.boolean.true), 'Expected boolean to be true, got false');
t.throws(() => m(false, m.boolean.label('foo').true), 'Expected boolean `foo` to be true, got false');
t.throws(() => m(false, m.boolean.true.label('foo')), 'Expected boolean `foo` to be true, got false');
t.throws(() => m(false, 'foo', m.boolean.true), 'Expected boolean `foo` to be true, got false');
t.throws(() => m(Boolean(0), m.boolean.true), 'Expected boolean to be true, got false');
});

3
source/test/buffer.ts

@ -4,8 +4,7 @@ import m from '..';
test('buffer', t => {
t.notThrows(() => m(Buffer.alloc(2), m.buffer));
t.notThrows(() => m(Buffer.from('f'), m.buffer));
t.notThrows(() => m(Buffer.from('f'), m.buffer.label('foo')));
t.throws(() => m('foo' as any, m.buffer), 'Expected argument to be of type `Buffer` but received type `string`');
t.throws(() => m('foo' as any, m.buffer.label('foo')), 'Expected `foo` to be of type `Buffer` but received type `string`');
t.throws(() => m('foo' as any, 'foo', m.buffer), 'Expected `foo` to be of type `Buffer` but received type `string`');
t.throws(() => m(12 as any, m.buffer), 'Expected argument to be of type `Buffer` but received type `number`');
});

3
source/test/data-view.ts

@ -3,8 +3,7 @@ import m from '..';
test('dataView', t => {
t.notThrows(() => m(new DataView(new ArrayBuffer(1)), m.dataView));
t.notThrows(() => m(new DataView(new ArrayBuffer(1)), m.dataView.label('data')));
t.throws(() => m(new ArrayBuffer(1) as any, m.dataView), 'Expected argument to be of type `DataView` but received type `ArrayBuffer`');
t.throws(() => m(new ArrayBuffer(1) as any, m.dataView.label('data')), 'Expected `data` to be of type `DataView` but received type `ArrayBuffer`');
t.throws(() => m(new ArrayBuffer(1) as any, 'data', m.dataView), 'Expected `data` to be of type `DataView` but received type `ArrayBuffer`');
t.throws(() => m(12 as any, m.dataView), 'Expected argument to be of type `DataView` but received type `number`');
});

8
source/test/date.ts

@ -3,19 +3,15 @@ import m from '..';
test('date', t => {
t.notThrows(() => m(new Date(), m.date));
t.notThrows(() => m(new Date(), m.date.label('foo')));
t.throws(() => m('12' as any, m.date), 'Expected argument to be of type `date` but received type `string`');
t.throws(() => m('12' as any, m.date.label('foo')), 'Expected `foo` to be of type `date` but received type `string`');
t.throws(() => m('12' as any, 'foo', m.date), 'Expected `foo` to be of type `date` but received type `string`');
});
test('date.before', t => {
t.notThrows(() => m(new Date('2017-11-25'), m.date.before(new Date('2017-11-26'))));
t.notThrows(() => m(new Date('2017-11-25T12:00:00Z'), m.date.before(new Date('2017-11-25T12:00:01Z'))));
t.notThrows(() => m(new Date('2017-11-25T12:00:00Z'), m.date.label('foo').before(new Date('2017-11-25T12:00:01Z'))));
t.notThrows(() => m(new Date('2017-11-25T12:00:00Z'), m.date.before(new Date('2017-11-25T12:00:01Z')).label('foo')));
t.throws(() => m(new Date('2017-11-25T12:00:00Z') as any, m.date.before(new Date('2017-11-25T12:00:00Z'))), 'Expected date 2017-11-25T12:00:00.000Z to be before 2017-11-25T12:00:00.000Z');
t.throws(() => m(new Date('2017-11-25T12:00:00Z') as any, m.date.label('foo').before(new Date('2017-11-25T12:00:00Z'))), 'Expected date `foo` 2017-11-25T12:00:00.000Z to be before 2017-11-25T12:00:00.000Z');
t.throws(() => m(new Date('2017-11-25T12:00:00Z') as any, m.date.before(new Date('2017-11-25T12:00:00Z')).label('foo')), 'Expected date `foo` 2017-11-25T12:00:00.000Z to be before 2017-11-25T12:00:00.000Z');
t.throws(() => m(new Date('2017-11-25T12:00:00Z') as any, 'foo', m.date.before(new Date('2017-11-25T12:00:00Z'))), 'Expected date `foo` 2017-11-25T12:00:00.000Z to be before 2017-11-25T12:00:00.000Z');
});
test('date.after', t => {

21
source/test/error.ts

@ -10,19 +10,16 @@ class CustomError extends Error {
test('error', t => {
t.notThrows(() => m(new Error('foo'), m.error));
t.notThrows(() => m(new Error('foo'), m.error.label('err')));
t.throws(() => m('12' as any, m.error), 'Expected argument to be of type `error` but received type `string`');
t.throws(() => m('12' as any, m.error.label('err')), 'Expected `err` to be of type `error` but received type `string`');
t.throws(() => m('12' as any, 'err', m.error), 'Expected `err` to be of type `error` but received type `string`');
});
test('error.name', t => {
t.notThrows(() => m(new Error('foo'), m.error.name('Error')));
t.notThrows(() => m(new CustomError('foo'), m.error.name('CustomError')));
t.notThrows(() => m(new CustomError('foo'), m.error.label('err').name('CustomError')));
t.notThrows(() => m(new CustomError('foo'), m.error.name('CustomError').label('err')));
t.notThrows(() => m(new CustomError('foo'), 'err', m.error.name('CustomError')));
t.throws(() => m(new CustomError('foo'), m.error.name('Error')), 'Expected error to have name `Error`, got `CustomError`');
t.throws(() => m(new CustomError('foo'), m.error.label('err').name('Error')), 'Expected error `err` to have name `Error`, got `CustomError`');
t.throws(() => m(new CustomError('foo'), m.error.name('Error').label('err')), 'Expected error `err` to have name `Error`, got `CustomError`');
t.throws(() => m(new CustomError('foo'), 'err', m.error.name('Error')), 'Expected error `err` to have name `Error`, got `CustomError`');
});
test('error.message', t => {
@ -45,8 +42,8 @@ test('error.hasKeys', t => {
t.notThrows(() => m(err, m.error.hasKeys('unicorn')));
t.notThrows(() => m(err, m.error.hasKeys('unicorn', 'rainbow')));
t.throws(() => m(err, m.error.hasKeys('foo')), 'Expected error message to have keys `foo`');
t.throws(() => m(err, m.error.hasKeys('unicorn', 'foo')), 'Expected error message to have keys `unicorn`, `foo`');
t.throws(() => m(err, m.error.hasKeys('foo')), 'Expected error `err` message to have keys `foo`');
t.throws(() => m(err, m.error.hasKeys('unicorn', 'foo')), 'Expected error `err` message to have keys `unicorn`, `foo`');
});
test('error.instanceOf', t => {
@ -54,17 +51,17 @@ test('error.instanceOf', t => {
t.notThrows(() => m(new CustomError('foo'), m.error.instanceOf(Error)));
t.notThrows(() => m(new TypeError('foo'), m.error.instanceOf(Error)));
t.notThrows(() => m(new Error('foo'), m.error.instanceOf(Error)));
t.notThrows(() => m(new Error('foo'), m.error.label('err').instanceOf(Error)));
t.notThrows(() => m(new Error('foo'), 'err', m.error.instanceOf(Error)));
t.throws(() => m(new Error('foo'), m.error.instanceOf(CustomError)), 'Expected error `Error` to be of type `CustomError`');
t.throws(() => m(new Error('foo'), m.error.label('err').instanceOf(CustomError)), 'Expected error `err` `Error` to be of type `CustomError`');
t.throws(() => m(new Error('foo'), 'err', m.error.instanceOf(CustomError)), 'Expected error `err` `Error` to be of type `CustomError`');
t.throws(() => m(new TypeError('foo'), m.error.instanceOf(EvalError)), 'Expected error `TypeError` to be of type `EvalError`');
t.throws(() => m(new TypeError('foo'), m.error.label('err').instanceOf(EvalError)), 'Expected error `err` `TypeError` to be of type `EvalError`');
t.throws(() => m(new TypeError('foo'), 'err', m.error.instanceOf(EvalError)), 'Expected error `err` `TypeError` to be of type `EvalError`');
});
test('error.typeError', t => {
t.notThrows(() => m(new TypeError('foo'), m.error.typeError));
t.throws(() => m(new Error('foo'), m.error.typeError), 'Expected error `Error` to be of type `TypeError`');
t.throws(() => m(new Error('foo'), m.error.label('foo').typeError), 'Expected error `foo` `Error` to be of type `TypeError`');
t.throws(() => m(new Error('foo'), 'foo', m.error.typeError), 'Expected error `foo` `Error` to be of type `TypeError`');
});
test('error.evalError', t => {

6
source/test/fixtures/create-error.ts

@ -0,0 +1,6 @@
export const createAnyError = (...errors: string[]) => {
return [
'Any predicate failed with the following errors:',
...errors.map(error => `- ${error}`)
].join('\n');
};

3
source/test/function.ts

@ -3,8 +3,7 @@ import m from '..';
test('function', t => {
t.notThrows(() => m(() => {}, m.function)); // tslint:disable-line:no-empty
t.notThrows(() => m(() => {}, m.function.label('foo'))); // tslint:disable-line:no-empty
t.throws(() => m('foo' as any, m.function), 'Expected argument to be of type `Function` but received type `string`');
t.throws(() => m('foo' as any, m.function.label('foo')), 'Expected `foo` to be of type `Function` but received type `string`');
t.throws(() => m('foo' as any, 'foo', m.function), 'Expected `foo` to be of type `Function` but received type `string`');
t.throws(() => m(12 as any, m.function), 'Expected argument to be of type `Function` but received type `number`');
});

42
source/test/infer-label.ts

@ -0,0 +1,42 @@
import test from 'ava';
import m from '..';
import {createAnyError} from './fixtures/create-error';
test('infer label', t => {
const foo = 'f';
t.throws(() => m(foo, m.string.minLength(2)), 'Expected string `foo` to have a minimum length of `2`, got `f`');
t.throws(() => m(foo as any, m.number), 'Expected `foo` to be of type `number` but received type `string`');
});
test('infer object property label', t => {
const hello = {
world: 'f'
};
t.throws(() => m(hello.world, m.string.minLength(2)), 'Expected string `hello.world` to have a minimum length of `2`, got `f`');
});
test('overwrite inferred label', t => {
const foo = 'f';
t.throws(() => m(foo, '🦄', m.string.minLength(2)), 'Expected string `🦄` to have a minimum length of `2`, got `f`');
});
test('infer label in `any` predicate', t => {
const foo = 'f';
t.throws(() => m(foo, m.any(m.string.minLength(2), m.number)), createAnyError(
'Expected string `foo` to have a minimum length of `2`, got `f`',
'Expected `foo` to be of type `number` but received type `string`'
));
});
test('overwrite inferred label in `any` predicate', t => {
const foo = 'f';
t.throws(() => m(foo, '🦄', m.any(m.string.minLength(2), m.number)), createAnyError(
'Expected string `🦄` to have a minimum length of `2`, got `f`',
'Expected `🦄` to be of type `number` but received type `string`'
));
});

3
source/test/iterable.ts

@ -5,7 +5,6 @@ test('iterable', t => {
t.notThrows(() => m([], m.iterable));
t.notThrows(() => m('foo', m.iterable));
t.notThrows(() => m(new Map(), m.iterable));
t.notThrows(() => m(new Map(), m.iterable.label('foo')));
t.throws(() => m(12 as any, m.iterable), 'Expected argument to be of type `Iterable` but received type `number`');
t.throws(() => m(12 as any, m.iterable.label('foo')), 'Expected `foo` to be of type `Iterable` but received type `number`');
t.throws(() => m(12 as any, 'foo', m.iterable), 'Expected `foo` to be of type `Iterable` but received type `number`');
});

16
source/test/map.ts

@ -4,19 +4,15 @@ import m from '..';
test('map', t => {
t.notThrows(() => m(new Map(), m.map));
t.notThrows(() => m(new Map([['unicorn', '🦄']]), m.map));
t.notThrows(() => m(new Map([['unicorn', '🦄']]), m.map.label('foo')));
t.throws(() => m(12 as any, m.map), 'Expected argument to be of type `Map` but received type `number`');
t.throws(() => m(12 as any, m.map.label('foo')), 'Expected `foo` to be of type `Map` but received type `number`');
t.throws(() => m(12 as any, 'foo', m.map), 'Expected `foo` to be of type `Map` but received type `number`');
});
test('map.size', t => {
t.notThrows(() => m(new Map(), m.map.size(0)));
t.notThrows(() => m(new Map([['unicorn', '🦄']]), m.map.size(1)));
t.notThrows(() => m(new Map([['unicorn', '🦄']]), m.map.label('foo').size(1)));
t.notThrows(() => m(new Map([['unicorn', '🦄']]), m.map.size(1).label('foo')));
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.size(0)), 'Expected Map to have size `0`, got `1`');
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.label('foo').size(0)), 'Expected Map `foo` to have size `0`, got `1`');
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.size(0).label('foo')), 'Expected Map `foo` to have size `0`, got `1`');
t.throws(() => m(new Map([['unicorn', '🦄']]), 'foo', m.map.size(0)), 'Expected Map `foo` to have size `0`, got `1`');
});
test('map.minSize', t => {
@ -64,20 +60,16 @@ test('map.keysOfType', t => {
t.notThrows(() => m(new Map([['unicorn', '🦄']]), m.map.keysOfType(m.string)));
t.notThrows(() => m(new Map([['unicorn', '🦄'], ['rainbow', '🌈']]), m.map.keysOfType(m.string.minLength(3))));
t.notThrows(() => m(new Map([[1, '🦄']]), m.map.keysOfType(m.number)));
t.notThrows(() => m(new Map([[1, '🦄']]), m.map.label('foo').keysOfType(m.number)));
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.keysOfType(m.number)), '(Map) Expected argument to be of type `number` but received type `string`');
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.label('foo').keysOfType(m.number)), '(Map `foo`) Expected argument to be of type `number` but received type `string`');
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.label('foo').keysOfType(m.number.label('bar'))), '(Map `foo`) Expected `bar` to be of type `number` but received type `string`');
t.throws(() => m(new Map([['unicorn', '🦄']]), 'foo', m.map.keysOfType(m.number)), '(Map `foo`) Expected argument to be of type `number` but received type `string`');
});
test('map.valuesOfType', t => {
t.notThrows(() => m(new Map([['unicorn', 1]]), m.map.valuesOfType(m.number)));
t.notThrows(() => m(new Map([['unicorn', 10], ['rainbow', 11]]), m.map.valuesOfType(m.number.greaterThanOrEqual(10))));
t.notThrows(() => m(new Map([['unicorn', '🦄']]), m.map.valuesOfType(m.string)));
t.notThrows(() => m(new Map([['unicorn', '🦄']]), m.map.label('foo').valuesOfType(m.string)));
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.valuesOfType(m.number)), '(Map) Expected argument to be of type `number` but received type `string`');
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.label('foo').valuesOfType(m.number)), '(Map `foo`) Expected argument to be of type `number` but received type `string`');
t.throws(() => m(new Map([['unicorn', '🦄']]), m.map.label('foo').valuesOfType(m.number.label('bar'))), '(Map `foo`) Expected `bar` to be of type `number` but received type `string`');
t.throws(() => m(new Map([['unicorn', '🦄']]), 'foo', m.map.valuesOfType(m.number)), '(Map `foo`) Expected argument to be of type `number` but received type `string`');
});
test('map.empty', t => {

3
source/test/nan.ts

@ -5,8 +5,7 @@ test('nan', t => {
t.notThrows(() => m(NaN, m.nan));
t.notThrows(() => m(Number.NaN, m.nan));
t.notThrows(() => m(0 / 0, m.nan));
t.notThrows(() => m(0 / 0, m.nan.label('foo')));
t.throws(() => m(12, m.nan), 'Expected argument to be of type `nan` but received type `number`');
t.throws(() => m(12, m.nan.label('foo')), 'Expected `foo` to be of type `nan` but received type `number`');
t.throws(() => m(12, 'foo', m.nan), 'Expected `foo` to be of type `nan` but received type `number`');
t.throws(() => m('12' as any, m.nan), 'Expected argument to be of type `nan` but received type `string`');
});

3
source/test/null-or-undefined.ts

@ -9,7 +9,6 @@ test('nullOrUndefined', t => {
t.notThrows(() => m(undefined, m.nullOrUndefined));
t.notThrows(() => m(x, m.nullOrUndefined));
t.notThrows(() => m(y, m.nullOrUndefined));
t.notThrows(() => m(y, m.nullOrUndefined.label('foo')));
t.throws(() => m('foo' as any, m.nullOrUndefined), 'Expected argument to be of type `nullOrUndefined` but received type `string`');
t.throws(() => m('foo' as any, m.nullOrUndefined.label('foo')), 'Expected `foo` to be of type `nullOrUndefined` but received type `string`');
t.throws(() => m('foo' as any, 'foo', m.nullOrUndefined), 'Expected `foo` to be of type `nullOrUndefined` but received type `string`');
});

3
source/test/null.ts

@ -6,8 +6,7 @@ test('null', t => {
t.notThrows(() => m(null, m.null));
t.notThrows(() => m(x, m.null));
t.notThrows(() => m(x, m.null.label('foo')));
t.throws(() => m(undefined as any, m.null), 'Expected argument to be of type `null` but received type `undefined`');
t.throws(() => m(undefined as any, m.null.label('foo')), 'Expected `foo` to be of type `null` but received type `undefined`');
t.throws(() => m(undefined as any, 'foo', m.null), 'Expected `foo` to be of type `null` but received type `undefined`');
t.throws(() => m('foo' as any, m.null), 'Expected argument to be of type `null` but received type `string`');
});

8
source/test/number.ts

@ -3,20 +3,16 @@ import m from '..';
test('number', t => {
t.notThrows(() => m(1, m.number));
t.notThrows(() => m(1, m.number.label('foo')));
t.throws(() => m('12' as any, m.number), 'Expected argument to be of type `number` but received type `string`');
t.throws(() => m('12' as any, m.number.label('foo')), 'Expected `foo` to be of type `number` but received type `string`');
t.throws(() => m('12' as any, 'foo', m.number), 'Expected `foo` to be of type `number` but received type `string`');
});
test('number.inRange', t => {
t.notThrows(() => m(10, m.number.inRange(0, 20)));
t.notThrows(() => m(10, m.number.inRange(10, 20)));
t.notThrows(() => m(10, m.number.inRange(0, 10)));
t.notThrows(() => m(10, m.number.label('foo').inRange(0, 10)));
t.notThrows(() => m(10, m.number.inRange(0, 10).label('foo')));
t.throws(() => m(10 as any, m.number.inRange(0, 9)), 'Expected number to be in range [0..9], got 10');
t.throws(() => m(10 as any, m.number.label('foo').inRange(0, 9)), 'Expected number `foo` to be in range [0..9], got 10');
t.throws(() => m(10 as any, m.number.inRange(0, 9).label('foo')), 'Expected number `foo` to be in range [0..9], got 10');
t.throws(() => m(10 as any, 'foo', m.number.inRange(0, 9)), 'Expected number `foo` to be in range [0..9], got 10');
t.throws(() => m(10 as any, m.number.inRange(11, 20)), 'Expected number to be in range [11..20], got 10');
});

18
source/test/object.ts

@ -5,20 +5,16 @@ class Unicorn {} // tslint:disable-line
test('object', t => {
t.notThrows(() => m({}, m.object));
t.notThrows(() => m({}, m.object.label('foo')));
t.notThrows(() => m(new Error('foo'), m.object));
t.throws(() => m('foo' as any, m.object), 'Expected argument to be of type `object` but received type `string`');
t.throws(() => m('foo' as any, m.object.label('foo')), 'Expected `foo` to be of type `object` but received type `string`');
t.throws(() => m('foo' as any, 'foo', m.object), 'Expected `foo` to be of type `object` but received type `string`');
t.throws(() => m(1 as any, m.object), 'Expected argument to be of type `object` but received type `number`');
});
test('object.plain', t => {
t.notThrows(() => m({}, m.object.plain));
t.notThrows(() => m({}, m.object.label('foo').plain));
t.notThrows(() => m({}, m.object.plain.label('foo')));
t.throws(() => m(new Error('foo'), m.object.plain), 'Expected object to be a plain object');
t.throws(() => m(new Error('foo'), m.object.label('foo').plain), 'Expected object `foo` to be a plain object');
t.throws(() => m(new Error('foo'), m.object.plain.label('foo')), 'Expected object `foo` to be a plain object');
t.throws(() => m(new Error('foo'), 'foo', m.object.plain), 'Expected object `foo` to be a plain object');
});
test('object.empty', t => {
@ -35,10 +31,8 @@ test('object.valuesOfType', t => {
t.notThrows(() => m({unicorn: '🦄'}, m.object.valuesOfType(m.string)));
t.notThrows(() => m({unicorn: '🦄', rainbow: '🌈'}, m.object.valuesOfType(m.string)));
t.notThrows(() => m({unicorn: 1, rainbow: 2}, m.object.valuesOfType(m.number)));
t.notThrows(() => m({unicorn: 1, rainbow: 2}, m.object.label('foo').valuesOfType(m.number)));
t.throws(() => m({unicorn: '🦄', rainbow: 2}, m.object.valuesOfType(m.string)), '(object) Expected argument to be of type `string` but received type `number`');
t.throws(() => m({unicorn: '🦄', rainbow: 2}, m.object.label('foo').valuesOfType(m.string)), '(object `foo`) Expected argument to be of type `string` but received type `number`');
t.throws(() => m({unicorn: '🦄', rainbow: 2}, m.object.label('foo').valuesOfType(m.string.label('bar'))), '(object `foo`) Expected `bar` to be of type `string` but received type `number`');
t.throws(() => m({unicorn: '🦄', rainbow: 2}, 'foo', m.object.valuesOfType(m.string)), '(object `foo`) Expected argument to be of type `string` but received type `number`');
t.throws(() => m({unicorn: 'a', rainbow: 'b'}, m.object.valuesOfType(m.string.minLength(2))), '(object) Expected string to have a minimum length of `2`, got `a`');
});
@ -47,10 +41,8 @@ test('object.valuesOfTypeDeep', t => {
t.notThrows(() => m({unicorn: '🦄', rainbow: '🌈'}, m.object.deepValuesOfType(m.string)));
t.notThrows(() => m({unicorn: {key: '🦄', value: '🌈'}}, m.object.deepValuesOfType(m.string)));
t.notThrows(() => m({a: {b: {c: {d: 1}, e: 2}, f: 3}}, m.object.deepValuesOfType(m.number)));
t.notThrows(() => m({a: {b: {c: {d: 1}, e: 2}, f: 3}}, m.object.label('foo').deepValuesOfType(m.number)));
t.throws(() => m({unicorn: {key: '🦄', value: 1}}, m.object.deepValuesOfType(m.string)), '(object) Expected argument to be of type `string` but received type `number`');
t.throws(() => m({unicorn: {key: '🦄', value: 1}}, m.object.label('foo').deepValuesOfType(m.string)), '(object `foo`) Expected argument to be of type `string` but received type `number`');
t.throws(() => m({unicorn: {key: '🦄', value: 1}}, m.object.label('foo').deepValuesOfType(m.string.label('bar'))), '(object `foo`) Expected `bar` to be of type `string` but received type `number`');
t.throws(() => m({unicorn: {key: '🦄', value: 1}}, 'foo', m.object.deepValuesOfType(m.string)), '(object `foo`) Expected argument to be of type `string` but received type `number`');
t.throws(() => m({a: {b: {c: {d: 1}, e: '2'}, f: 3}}, m.object.deepValuesOfType(m.number)), '(object) Expected argument to be of type `number` but received type `string`');
});
@ -64,7 +56,7 @@ test('object.instanceOf', t => {
t.notThrows(() => m(new Error('🦄'), m.object.instanceOf(Error)));
t.notThrows(() => m(new Unicorn(), m.object.instanceOf(Unicorn)));
t.throws(() => m(new Unicorn(), m.object.instanceOf(Error)), 'Expected object `Unicorn` to be of type `Error`');
t.throws(() => m(new Unicorn(), m.object.label('foo').instanceOf(Error)), 'Expected object `foo` `Unicorn` to be of type `Error`');
t.throws(() => m(new Unicorn(), 'foo', m.object.instanceOf(Error)), 'Expected object `foo` `Unicorn` to be of type `Error`');
t.throws(() => m(new Error('🦄'), m.object.instanceOf(Unicorn)), 'Expected object `Error` to be of type `Unicorn`');
t.throws(() => m({unicorn: '🦄'}, m.object.instanceOf(Unicorn)), 'Expected object `{"unicorn":"🦄"}` to be of type `Unicorn`');
});

3
source/test/promise.ts

@ -4,8 +4,7 @@ import m from '..';
test('promise', t => {
t.notThrows(() => m(Promise.resolve(), m.promise));
t.notThrows(() => m(new Promise(resolve => resolve()), m.promise));
t.notThrows(() => m(new Promise(resolve => resolve()), m.promise.label('foo')));
t.throws(() => m('foo' as any, m.promise), 'Expected argument to be of type `Promise` but received type `string`');
t.throws(() => m('foo' as any, m.promise.label('foo')), 'Expected `foo` to be of type `Promise` but received type `string`');
t.throws(() => m('foo' as any, 'foo', m.promise), 'Expected `foo` to be of type `Promise` but received type `string`');
t.throws(() => m(12 as any, m.promise), 'Expected argument to be of type `Promise` but received type `number`');
});

3
source/test/regexp.ts

@ -4,8 +4,7 @@ import m from '..';
test('regExp', t => {
t.notThrows(() => m(/\d/, m.regExp));
t.notThrows(() => m(new RegExp('\d'), m.regExp));
t.notThrows(() => m(new RegExp('\d'), m.regExp.label('foo')));
t.throws(() => m('foo' as any, m.regExp), 'Expected argument to be of type `RegExp` but received type `string`');
t.throws(() => m('foo' as any, m.regExp.label('foo')), 'Expected `foo` to be of type `RegExp` but received type `string`');
t.throws(() => m('foo' as any, 'foo', m.regExp), 'Expected `foo` to be of type `RegExp` but received type `string`');
t.throws(() => m(12 as any, m.regExp), 'Expected argument to be of type `RegExp` but received type `number`');
});

12
source/test/set.ts

@ -4,19 +4,15 @@ import m from '..';
test('set', t => {
t.notThrows(() => m(new Set(), m.set));
t.notThrows(() => m(new Set(['🦄']), m.set));
t.notThrows(() => m(new Set(['🦄']), m.set.label('foo')));
t.throws(() => m(12 as any, m.set), 'Expected argument to be of type `Set` but received type `number`');
t.throws(() => m(12 as any, m.set.label('foo')), 'Expected `foo` to be of type `Set` but received type `number`');
t.throws(() => m(12 as any, 'foo', m.set), 'Expected `foo` to be of type `Set` but received type `number`');
});
test('set.size', t => {
t.notThrows(() => m(new Set(), m.set.size(0)));
t.notThrows(() => m(new Set(['🦄']), m.set.size(1)));
t.notThrows(() => m(new Set(['🦄']), m.set.label('foo').size(1)));
t.notThrows(() => m(new Set(['🦄']), m.set.size(1).label('foo')));
t.throws(() => m(new Set(['🦄']), m.set.size(0)), 'Expected Set to have size `0`, got `1`');
t.throws(() => m(new Set(['🦄']), m.set.label('foo').size(0)), 'Expected Set `foo` to have size `0`, got `1`');
t.throws(() => m(new Set(['🦄']), m.set.size(0).label('foo')), 'Expected Set `foo` to have size `0`, got `1`');
t.throws(() => m(new Set(['🦄']), 'foo', m.set.size(0)), 'Expected Set `foo` to have size `0`, got `1`');
});
test('set.minSize', t => {
@ -51,10 +47,8 @@ test('set.ofType', t => {
t.notThrows(() => m(new Set(['unicorn']), m.set.ofType(m.string)));
t.notThrows(() => m(new Set(['unicorn', 'rainbow']), m.set.ofType(m.string.minLength(3))));
t.notThrows(() => m(new Set([1]), m.set.ofType(m.number)));
t.notThrows(() => m(new Set([1]), m.set.label('foo').ofType(m.number)));
t.throws(() => m(new Set(['unicorn']), m.set.ofType(m.number)), '(Set) Expected argument to be of type `number` but received type `string`');
t.throws(() => m(new Set(['unicorn']), m.set.label('foo').ofType(m.number)), '(Set `foo`) Expected argument to be of type `number` but received type `string`');
t.throws(() => m(new Set(['unicorn']), m.set.label('foo').ofType(m.number.label('bar'))), '(Set `foo`) Expected `bar` to be of type `number` but received type `string`');
t.throws(() => m(new Set(['unicorn']), 'foo', m.set.ofType(m.number)), '(Set `foo`) Expected argument to be of type `number` but received type `string`');
});
test('set.empty', t => {

16
source/test/string.ts

@ -2,20 +2,19 @@ import test from 'ava';
import m from '..';
test('string', t => {
const bar: any = 12;
t.notThrows(() => m('foo', m.string));
t.notThrows(() => m('foo', m.string.label('foo')));
t.throws(() => m(12 as any, m.string), 'Expected argument to be of type `string` but received type `number`');
t.throws(() => m(12 as any, m.string.label('bar')), 'Expected `bar` to be of type `string` but received type `number`');
t.throws(() => m(12 as any, 'bar', m.string), 'Expected `bar` to be of type `string` but received type `number`');
t.throws(() => m(bar, m.string), 'Expected `bar` 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.notThrows(() => m('bar', m.string.label('bar').length(3)));
t.notThrows(() => m('bar', m.string.length(3).label('bar')));
t.throws(() => m('foo' as any, m.string.length(4)), 'Expected string to have length `4`, got `foo`');
t.throws(() => m('foo' as any, m.string.label('foo').length(4)), 'Expected string `foo` to have length `4`, got `foo`');
t.throws(() => m('foo' as any, m.string.length(4).label('foo')), 'Expected string `foo` to have length `4`, got `foo`');
t.throws(() => m('foo' as any, 'foo', m.string.length(4)), 'Expected string `foo` to have length `4`, got `foo`');
});
test('string.minLength', t => {
@ -59,7 +58,7 @@ test('string.includes', t => {
test('string.oneOf', t => {
t.notThrows(() => m('foo', m.string.oneOf(['foo', 'bar'])));
t.throws(() => m('foo', m.string.oneOf(['unicorn', 'rainbow'])), 'Expected string to be one of `["unicorn","rainbow"]`, got `foo`');
t.throws(() => m('foo', m.string.oneOf(['unicorn', 'rainbow']).label('hello')), 'Expected string `hello` to be one of `["unicorn","rainbow"]`, got `foo`');
t.throws(() => m('foo', 'hello', m.string.oneOf(['unicorn', 'rainbow'])), 'Expected string `hello` to be one of `["unicorn","rainbow"]`, got `foo`');
t.throws(() => m('foo', m.string.oneOf(['a', 'b', 'c', 'd', 'e'])), 'Expected string to be one of `["a","b","c","d","e"]`, got `foo`');
t.throws(() => m('foo', m.string.oneOf(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'])), 'Expected string to be one of `["1","2","3","4","5","6","7","8","9","10",…+3 more]`, got `foo`');
});
@ -106,9 +105,8 @@ test('string.numeric', t => {
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.notThrows(() => m('2017-03-02T10:00:00Z', m.string.label('bar').date));
t.throws(() => m('foo' as any, m.string.date), 'Expected string to be a date, got `foo`');
t.throws(() => m('foo' as any, m.string.label('bar').date), 'Expected string `bar` to be a date, got `foo`');
t.throws(() => m('foo' as any, 'bar', m.string.date), 'Expected string `bar` to be a date, got `foo`');
});
test('string.lowercase', t => {

3
source/test/symbol.ts

@ -4,7 +4,6 @@ import m from '..';
test('symbol', t => {
t.notThrows(() => m(Symbol.iterator, m.symbol));
t.notThrows(() => m(Symbol('foo'), m.symbol));
t.notThrows(() => m(Symbol('foo'), m.symbol.label('foo')));
t.throws(() => m(12 as any, m.symbol), 'Expected argument to be of type `symbol` but received type `number`');
t.throws(() => m(12 as any, m.symbol.label('foo')), 'Expected `foo` to be of type `symbol` but received type `number`');
t.throws(() => m(12 as any, 'foo', m.symbol), 'Expected `foo` to be of type `symbol` but received type `number`');
});

29
source/test/test.ts

@ -1,20 +1,22 @@
import test from 'ava';
import m from '..';
import {createError} from './any';
import {createAnyError} from './fixtures/create-error';
test('not', t => {
const foo = '';
t.notThrows(() => m('foo!', m.string.not.alphanumeric));
t.notThrows(() => m(1, m.number.not.infinite));
t.notThrows(() => m(1, m.number.not.infinite.not.greaterThan(5)));
t.throws(() => m(6, m.number.not.infinite.not.greaterThan(5)));
t.notThrows(() => m('foo!', m.string.not.alphabetical));
t.notThrows(() => m('foo!', m.string.not.alphanumeric));
t.notThrows(() => m('foo!', m.string.label('foo').not.alphanumeric));
t.notThrows(() => m('foo!', m.string.not.alphanumeric.label('foo')));
t.notThrows(() => m('foo!', 'foo', m.string.not.alphanumeric));
t.notThrows(() => m('FOO!', m.string.not.lowercase));
t.notThrows(() => m('foo!', m.string.not.uppercase));
t.throws(() => m('', m.string.not.empty), '[NOT] Expected string to be empty, got ``');
t.throws(() => m('', m.string.label('foo').not.empty), '[NOT] Expected string `foo` to be empty, got ``');
t.throws(() => m('', 'foo', m.string.not.empty), '[NOT] Expected string `foo` to be empty, got ``');
t.throws(() => m(foo, m.string.not.empty), '[NOT] Expected string `foo` to be empty, got ``');
});
test('is', t => {
@ -23,11 +25,10 @@ test('is', t => {
};
t.notThrows(() => m(1, m.number.is(x => x < 10)));
t.notThrows(() => m(1, m.number.label('foo').is(x => x < 10)));
t.throws(() => m(1, m.number.is(x => x > 10)), 'Expected number `1` to pass custom validation function');
t.throws(() => m(1, m.number.label('foo').is(x => x > 10)), 'Expected number `foo` `1` to pass custom validation function');
t.throws(() => m(1, 'foo', m.number.is(x => x > 10)), 'Expected number `foo` `1` to pass custom validation function');
t.throws(() => m(5, m.number.is(x => greaterThan(10, x))), '(number) Expected `5` to be greater than `10`');
t.throws(() => m(5, m.number.label('foo').is(x => greaterThan(10, x))), '(number `foo`) Expected `5` to be greater than `10`');
t.throws(() => m(5, 'foo', m.number.is(x => greaterThan(10, x))), '(number `foo`) Expected `5` to be greater than `10`');
});
test('isValid', t => {
@ -44,14 +45,17 @@ test('isValid', t => {
test('reusable validator', t => {
const checkUsername = m.create(m.string.minLength(3));
const value = 'x';
t.notThrows(() => checkUsername('foo'));
t.notThrows(() => checkUsername('foobar'));
t.throws(() => checkUsername('fo'), 'Expected string to have a minimum length of `3`, got `fo`');
t.throws(() => checkUsername(value), 'Expected string `value` to have a minimum length of `3`, got `x`');
t.throws(() => checkUsername(5 as any), 'Expected argument to be of type `string` but received type `number`');
});
test('reusable validator with label', t => {
const checkUsername = m.create(m.string.label('foo').minLength(3));
const checkUsername = m.create('foo', m.string.minLength(3));
t.notThrows(() => checkUsername('foo'));
t.notThrows(() => checkUsername('foobar'));
@ -64,17 +68,12 @@ test('any-reusable validator', t => {
t.notThrows(() => checkUsername('foo'));
t.notThrows(() => checkUsername('f.'));
t.throws(() => checkUsername('fo'), createError(
t.throws(() => checkUsername('fo'), createAnyError(
'Expected string to include `.`, got `fo`',
'Expected string to have a minimum length of `3`, got `fo`'
));
t.throws(() => checkUsername(5 as any), createError(
t.throws(() => checkUsername(5 as any), createAnyError(
'Expected argument to be of type `string` but received type `number`',
'Expected argument to be of type `string` but received type `number`'
));
});
test('overwrite label', t => {
t.notThrows(() => m('foo', m.string.label('foo').label('bar')));
t.throws(() => m(12 as any, m.string.label('foo').label('bar')), 'Expected `bar` to be of type `string` but received type `number`');
});

3
source/test/typed-array.ts

@ -6,9 +6,8 @@ test('typedArray', t => {
t.notThrows(() => m(new Uint8Array(2), m.typedArray));
t.notThrows(() => m(new Int32Array(2), m.typedArray));
t.notThrows(() => m(new Float64Array(2), m.typedArray));
t.notThrows(() => m(new Float64Array(2), m.typedArray.label('foo')));
t.throws(() => m('foo' as any, m.typedArray), 'Expected argument to be of type `TypedArray` but received type `string`');
t.throws(() => m('foo' as any, m.typedArray.label('foo')), 'Expected `foo` to be of type `TypedArray` but received type `string`');
t.throws(() => m('foo' as any, 'foo', m.typedArray), 'Expected `foo` to be of type `TypedArray` but received type `string`');
t.throws(() => m(12 as any, m.typedArray), 'Expected argument to be of type `TypedArray` but received type `number`');
});

6
source/test/undefined.ts

@ -7,9 +7,9 @@ test('undefined', t => {
t.notThrows(() => m(undefined, m.undefined));
t.notThrows(() => m(x, m.undefined));
t.notThrows(() => m(x, m.undefined.label('foo')));
t.throws(() => m(y as any, m.undefined), 'Expected argument to be of type `undefined` but received type `number`');
t.throws(() => m(y as any, m.undefined.label('foo')), 'Expected `foo` to be of type `undefined` but received type `number`');
t.notThrows(() => m(x, 'foo', m.undefined));
t.throws(() => m(y as any, m.undefined), 'Expected `y` to be of type `undefined` but received type `number`');
t.throws(() => m(y as any, 'foo', m.undefined), 'Expected `foo` to be of type `undefined` but received type `number`');
t.throws(() => m(null as any, m.undefined), 'Expected argument to be of type `undefined` but received type `null`');
t.throws(() => m('foo' as any, m.undefined), 'Expected argument to be of type `undefined` but received type `string`');
});

8
source/test/weak-map.ts

@ -4,9 +4,8 @@ import m from '..';
test('weakMap', t => {
t.notThrows(() => m(new WeakMap(), m.weakMap));
t.notThrows(() => m(new WeakMap([[{foo: 'bar'}, '🦄']]), m.weakMap));
t.notThrows(() => m(new WeakMap([[{foo: 'bar'}, '🦄']]), m.weakMap.label('foo')));
t.throws(() => m(12 as any, m.weakMap), 'Expected argument to be of type `WeakMap` but received type `number`');
t.throws(() => m(12 as any, m.weakMap.label('foo')), 'Expected `foo` to be of type `WeakMap` but received type `number`');
t.throws(() => m(12 as any, 'foo', m.weakMap), 'Expected `foo` to be of type `WeakMap` but received type `number`');
});
test('weakMap.hasKeys', t => {
@ -15,11 +14,8 @@ test('weakMap.hasKeys', t => {
const keys = [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}, {x: 6}, {x: 7}, {x: 8}, {x: 9}, {x: 10}];
t.notThrows(() => m(new WeakMap([[unicorn, '🦄']]), m.weakMap.hasKeys(unicorn)));
t.notThrows(() => m(new WeakMap([[unicorn, '🦄']]), m.weakMap.label('foo').hasKeys(unicorn)));
t.notThrows(() => m(new WeakMap([[unicorn, '🦄']]), m.weakMap.hasKeys(unicorn).label('foo')));
t.throws(() => m(new WeakMap([[{rainbow: true}, '🌈']]), m.weakMap.hasKeys({rainbow: true})), 'Expected WeakMap to have keys `[{"rainbow":true}]`');
t.throws(() => m(new WeakMap([[{rainbow: true}, '🌈']]), m.weakMap.label('foo').hasKeys({rainbow: true})), 'Expected WeakMap `foo` to have keys `[{"rainbow":true}]`');
t.throws(() => m(new WeakMap([[{rainbow: true}, '🌈']]), m.weakMap.hasKeys({rainbow: true}).label('foo')), 'Expected WeakMap `foo` to have keys `[{"rainbow":true}]`');
t.throws(() => m(new WeakMap([[{rainbow: true}, '🌈']]), 'foo', m.weakMap.hasKeys({rainbow: true})), 'Expected WeakMap `foo` to have keys `[{"rainbow":true}]`');
t.throws(() => m(new WeakMap([[unicorn, '🦄'], [rainbow, '🌈']]), m.weakMap.hasKeys(unicorn, {rainbow: true})), 'Expected WeakMap to have keys `[{"rainbow":true}]`');
t.throws(() => m(new WeakMap([[keys[0], 1], [keys[2], 3]]), m.weakMap.hasKeys(...keys)), 'Expected WeakMap to have keys `[{"x":2},{"x":4},{"x":5},{"x":6},{"x":7}]`');
});

8
source/test/weak-set.ts

@ -9,9 +9,8 @@ test('weakSet', t => {
t.notThrows(() => m(new WeakSet(), m.weakSet));
t.notThrows(() => m(new WeakSet([{unicorn: '🦄'}]), m.weakSet));
t.notThrows(() => m(new WeakSet([unicorn]), m.weakSet));
t.notThrows(() => m(new WeakSet([unicorn]), m.weakSet.label('foo')));
t.throws(() => m(12 as any, m.weakSet), 'Expected argument to be of type `WeakSet` but received type `number`');
t.throws(() => m(12 as any, m.weakSet.label('foo')), 'Expected `foo` to be of type `WeakSet` but received type `number`');
t.throws(() => m(12 as any, 'foo', m.weakSet), 'Expected `foo` to be of type `WeakSet` but received type `number`');
});
test('weakSet.has', t => {
@ -19,11 +18,8 @@ test('weakSet.has', t => {
t.notThrows(() => m(new WeakSet([unicorn]), m.weakSet.has(unicorn)));
t.notThrows(() => m(new WeakSet([unicorn, rainbow]), m.weakSet.has(unicorn, rainbow)));
t.notThrows(() => m(new WeakSet([unicorn, rainbow]), m.weakSet.label('foo').has(unicorn, rainbow)));
t.notThrows(() => m(new WeakSet([unicorn, rainbow]), m.weakSet.has(unicorn, rainbow).label('foo')));
t.throws(() => m(new WeakSet([unicorn, rainbow]), m.weakSet.has(rocket)), 'Expected WeakSet to have items `[{"rocket":"🚀"}]`');
t.throws(() => m(new WeakSet([unicorn, rainbow]), m.weakSet.label('foo').has(rocket)), 'Expected WeakSet `foo` to have items `[{"rocket":"🚀"}]`');
t.throws(() => m(new WeakSet([unicorn, rainbow]), m.weakSet.has(rocket).label('foo')), 'Expected WeakSet `foo` to have items `[{"rocket":"🚀"}]`');
t.throws(() => m(new WeakSet([unicorn, rainbow]), 'foo', m.weakSet.has(rocket)), 'Expected WeakSet `foo` to have items `[{"rocket":"🚀"}]`');
t.throws(() => m(new WeakSet([unicorn, rocket]), m.weakSet.has(rainbow, rocket)), 'Expected WeakSet to have items `[{"rainbow":"🌈"}]`');
t.throws(() => m(new WeakSet([keys[1], keys[3]]), m.weakSet.has(...keys)), 'Expected WeakSet to have items `[{"x":1},{"x":3},{"x":5},{"x":6},{"x":7}]`');
});

2
source/types.d.ts

@ -0,0 +1,2 @@
declare module 'is-valid-identifier';
declare module 'is-node';
Loading…
Cancel
Save