Repository: jquense/yup
Pull request #637 (2019-09-25 14:33:43)

feat: Add defined() validation to mix
After seeing this comment https://github.com/jquense/yup/issues/153#issuecomment-349679982 in an issue about the error when mixing `required` with `nullable` I thought it would be nice to have this validation.


Patch:
diff --git a/.babelrc b/.babelrc
deleted file mode 100644
index d91c557..0000000
--- a/.babelrc
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "presets": [["jason", { "debug": true, "ignoreBrowserslistConfig": true }]],
-  "env": {
-    "test": {
-      "sourceMaps": "inline",
-      "presets": [
-        [
-          "jason",
-          {
-            "target": "node",
-            "targets": { "node": "current" }
-          }
-        ]
-      ]
-    }
-  }
-}
diff --git a/.babelrc.js b/.babelrc.js
new file mode 100644
index 0000000..31d1f4a
--- /dev/null
+++ b/.babelrc.js
@@ -0,0 +1,25 @@
+module.exports = api => ({
+  presets: [
+    [
+      'jason',
+      api.env() !== 'test'
+        ? {
+            ignoreBrowserslistConfig: true,
+            modules: api.env() === 'modules' ? false : 'commonjs',
+          }
+        : {
+            target: 'node',
+            targets: { node: 'current' },
+          },
+    ],
+  ],
+  plugins: [
+    api.env() === 'modules' && [
+      'transform-rename-import',
+      {
+        original: 'lodash',
+        replacement: 'lodash-es',
+      },
+    ],
+  ].filter(Boolean),
+});
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 3703070..2ce1745 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -11,7 +11,9 @@ assignees: ''
 A clear and concise description of what the bug is.
 
 **To Reproduce**
-Provide a running code snippet in using https://repl.it/@jquense/yup or similar 
+
+Write a **runnable** test case using the code sandbox template: https://codesandbox.io/s/yup-test-case-gg1g1 
+In the `index.test.js` file change the passing test to a failing one demostrating your issue
 
 > NOTE: if you do not provide a runnable reproduction the chances of getting feedback are significantly lower
 
diff --git a/.gitignore b/.gitignore
index 928486e..2164f1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,4 @@ $RECYCLE.BIN/
 
 # Ignore build files
 lib/
+es/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6aa2e5..bf8888c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,46 @@
+# [0.28.0](https://github.com/jquense/yup/compare/v0.26.10...v0.28.0) (2019-12-16)
+
+
+### Bug Fixes
+
+* [#473](https://github.com/jquense/yup/issues/473) make concat compatible with (not)oneOf ([#492](https://github.com/jquense/yup/issues/492)) ([8d21cc9](https://github.com/jquense/yup/commit/8d21cc9))
+* array path resolve for descendants ([#669](https://github.com/jquense/yup/issues/669)) ([d31e34d](https://github.com/jquense/yup/commit/d31e34d))
+* change @babel/runtime version to be a range ([#488](https://github.com/jquense/yup/issues/488)) ([1c9b362](https://github.com/jquense/yup/commit/1c9b362)), closes [#486](https://github.com/jquense/yup/issues/486)
+* concat of mixed and subtype ([#444](https://github.com/jquense/yup/issues/444)) ([7705972](https://github.com/jquense/yup/commit/7705972))
+* default message for test with object ([#453](https://github.com/jquense/yup/issues/453)) ([f1be37f](https://github.com/jquense/yup/commit/f1be37f))
+* noUnknown() overriding ([#452](https://github.com/jquense/yup/issues/452)) ([3047b33](https://github.com/jquense/yup/commit/3047b33))
+* string.matches() and regex global flag ([#450](https://github.com/jquense/yup/issues/450)) ([a8935b7](https://github.com/jquense/yup/commit/a8935b7))
+* synchronous conditional object validation with unknown dependencies ([#598](https://github.com/jquense/yup/issues/598)) ([1081c41](https://github.com/jquense/yup/commit/1081c41))
+* typo README  (about excludeEmptyString) ([#441](https://github.com/jquense/yup/issues/441)) ([d02ff5e](https://github.com/jquense/yup/commit/d02ff5e))
+* unix epoc bug in date parser ([#655](https://github.com/jquense/yup/issues/655)) ([0d14827](https://github.com/jquense/yup/commit/0d14827))
+
+
+### Features
+
+* add _isFilled as overrideable `mixed` method to control required behavior ([#459](https://github.com/jquense/yup/issues/459)) ([5b01f18](https://github.com/jquense/yup/commit/5b01f18))
+* add function test names to email and url ([#292](https://github.com/jquense/yup/issues/292)) ([7e94395](https://github.com/jquense/yup/commit/7e94395))
+* aliases `optional()` and `unknown()` ([#460](https://github.com/jquense/yup/issues/460)) ([51e8661](https://github.com/jquense/yup/commit/51e8661))
+* allow toggling strict() ([#457](https://github.com/jquense/yup/issues/457)) ([851d421](https://github.com/jquense/yup/commit/851d421))
+* allow withMutation() nesting ([#456](https://github.com/jquense/yup/issues/456)) ([e53ea8c](https://github.com/jquense/yup/commit/e53ea8c))
+* do concat in mutation mode ([#461](https://github.com/jquense/yup/issues/461)) ([02be4ca](https://github.com/jquense/yup/commit/02be4ca))
+* finalize resolve() ([#447](https://github.com/jquense/yup/issues/447)) ([afc5119](https://github.com/jquense/yup/commit/afc5119))
+* replace integer check with Number.isInteger ([#405](https://github.com/jquense/yup/issues/405)) ([1c18442](https://github.com/jquense/yup/commit/1c18442))
+* support self references ([#443](https://github.com/jquense/yup/issues/443)) ([1cac515](https://github.com/jquense/yup/commit/1cac515)), closes [/github.com/jquense/yup/blob/d02ff5e59e004b4c5189d1b9fc0055cff45c61df/src/Reference.js#L3](https://github.com//github.com/jquense/yup/blob/d02ff5e59e004b4c5189d1b9fc0055cff45c61df/src/Reference.js/issues/L3)
+* use the alternate object index path syntax if the key contains dots (fixes [#536](https://github.com/jquense/yup/issues/536)) ([#539](https://github.com/jquense/yup/issues/539)) ([13e8c76](https://github.com/jquense/yup/commit/13e8c76))
+
+
+### BREAKING CHANGES
+
+* use Number.isInteger. This works correctly for large numbers.
+
+Related to https://github.com/jquense/yup/pull/147
+* reach() no longer resolves the returned schema meaning it's conditions have not been processed yet; prefer validateAt/castAt where it makes sense
+* required no longer shows up twice in describe() output for array and strings, which also no longer override required
+
+
+
+
+
 # [0.27.0](https://github.com/jquense/yup/compare/v0.26.10...v0.27.0) (2019-03-14)
 
 
diff --git a/README.md b/README.md
index a842449..23846fc 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,8 @@
 # Yup
 
-Yup is a JavaScript object schema validator and object parser. The API and style is ~~stolen~~ heavily inspired
-by [Joi](https://github.com/hapijs/joi), which is an amazing library but is generally too large and difficult
-to package for use in a browser. Yup is leaner: in the same spirit, without some of the fancy features.
-You can use it on the server as well, but in that case you might as well just use Joi.
+Yup is a JavaScript schema builder for value parsing and validation. Define a schema, transform a value to match, validate the shape of an existing value, or both. Yup schema are extremely expressive and allow modeling complex, interdependent validations, or value transformations.
 
-Yup is also a good bit less opinionated than joi, allowing for custom transformations and async validation.
-It also allows "stacking" conditions via `when` for properties that depend on more than one other sibling or
-child property. Yup separates the parsing and validating functions into separate steps so it can be used to parse
-json separate from validating it, via the `cast` method.
+Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leaner and built with client-side validation as its primary use-case. Yup separates the parsing and validating functions into separate steps. `cast()` transforms data while `validate` checks that the input is the correct shape. Each can be performed together (such as HTML form validation) or seperately (such as deserializing trusted data from APIs).
 
 **Try it out:** https://runkit.com/jquense/yup#
 
@@ -47,6 +41,7 @@ json separate from validating it, via the `cast` method.
     - [`mixed.nullable(isNullable: boolean = true): Schema`](#mixednullableisnullable-boolean--true-schema)
     - [`mixed.required(message?: string | function): Schema`](#mixedrequiredmessage-string--function-schema)
     - [`mixed.notRequired(): Schema`](#mixednotrequired-schema)
+    - [`mixed.defined(): Schema`](#mixeddefined-schema)
     - [`mixed.typeError(message: string): Schema`](#mixedtypeerrormessage-string-schema)
     - [`mixed.oneOf(arrayOfValues: Array<any>, message?: string | function): Schema` Alias: `equals`](#mixedoneofarrayofvalues-arrayany-message-string--function-schema-alias-equals)
     - [`mixed.notOneOf(arrayOfValues: Array<any>, message?: string | function)`](#mixednotoneofarrayofvalues-arrayany-message-string--function)
@@ -122,11 +117,7 @@ npm install -D @types/yup
 
 ## Usage
 
-You define and create schema objects. Schema objects are immutable, so each call of a method returns a _new_ schema object.
-
-**try it out using tonicdev! https://tonicdev.com/570c52590a85f71200eb09ba/yup**
-
-When using es module syntax, yup exports everything as a named export
+You define and create schema objects. Schema objects are immutable, so each call of a method returns a _new_ schema object. When using es module syntax, yup exports everything as a named export
 
 ```js
 import * as yup from 'yup'; // for everything
@@ -201,6 +192,35 @@ schema.validate({ name: 'jimmy', age: 11 }).catch(function(err) {
 });
 ```
 
+If you need multi-language support, Yup has got you covered. The function `setLocale` accepts functions that can be used to generate error objects with translation keys and values. Just get this output and feed it into your favorite i18n library.
+
+```js
+import { setLocale } from 'yup';
+
+setLocale({
+  // use constant translation keys for messages without values
+  mixed: {
+    default: 'field_invalid',
+  },
+  // use functions to generate an error object that includes the value from the schema
+  number: {
+    min: ({ min }) => ({ key: 'field_too_short', values: { min } }),
+    max: ({ max }) => ({ key: 'field_too_big', values: { max } }),
+  },
+});
+
+// now use Yup schemas AFTER you defined your custom dictionary
+let schema = yup.object().shape({
+  name: yup.string(),
+  age: yup.number().min(18),
+});
+
+schema.validate({ name: 'jimmy', age: 11 }).catch(function(err) {
+  err.name; // => 'ValidationError'
+  err.errors; // => [{ key: 'field_too_short', values: { min: 18 } }]
+});
+```
+
 ## API
 
 ### `yup`
@@ -257,7 +277,7 @@ yup.addMethod(yup.date, 'format', function(formats, parseStrict) {
 
     value = Moment(originalValue, formats, parseStrict);
 
-    return date.isValid() ? date.toDate() : invalidDate;
+    return value.isValid() ? value.toDate() : new Date('');
   });
 });
 ```
@@ -284,10 +304,10 @@ schema.cast({ foo: { bar: 'boom' } }, { context: { x: 5 } });
 #### `yup.lazy((value: any) => Schema): Lazy`
 
 Creates a schema that is evaluated at validation/cast time. Useful for creating
-recursive schema like Trees, for polymophic fields and arrays.
+recursive schema like Trees, for polymorphic fields and arrays.
 
 **CAUTION!** When defining parent-child recursive object schema, you want to reset the `default()`
-to `undefined` on the child otherwise the object will infinitely nest itself when you cast it!.
+to `undefined` on the child—otherwise the object will infinitely nest itself when you cast it!
 
 ```js
 let node = object({
@@ -382,7 +402,7 @@ Options = {
 }
 ```
 
-- `strict`: only validate the input, and skip and coercion or transformation
+- `strict`: only validate the input, and skip any coercion or transformation
 - `abortEarly`: return from validation methods on the first error rather
   than after all validations run.
 - `stripUnknown`: remove unspecified keys from objects.
@@ -566,6 +586,10 @@ Mark the schema as required. All field values apart from `undefined` and `null`
 
 Mark the schema as not required. Passing `undefined` as value will not fail validation.
 
+#### `mixed.defined(): Schema`
+
+Mark the schema as required but nullable. All field values apart from `undefined` meet this requirement.
+
 #### `mixed.typeError(message: string): Schema`
 
 Define an error message for failed type checks. The `${value}` and `${type}` interpolation can
@@ -576,6 +600,9 @@ be used in the `message` argument.
 Whitelist a set of values. Values added are automatically removed from any blacklist if they are in it.
 The `${values}` interpolation can be used in the `message` argument.
 
+Note that `undefined` does not fail this validator, even when `undefined` is not included in `arrayOfValues`.
+If you don't want `undefined` to be a valid value, you can use `mixed.required`.
+
 ```js
 let schema = yup.mixed().oneOf(['jimmy', 42]);
 
@@ -668,7 +695,7 @@ All tests must provide a `name`, an error `message` and a validation function th
 `true` or `false` or a `ValidationError`. To make a test async return a promise that resolves `true`
 or `false` or a `ValidationError`.
 
-for the `message` argument you can provide a string which is will interpolate certain values
+for the `message` argument you can provide a string which will interpolate certain values
 if specified using the `${param}` syntax. By default all test messages are passed a `path` value
 which is valuable in nested schemas.
 
@@ -699,8 +726,8 @@ test functions are called with a special context, or `this` value, that exposes
 - `this.schema`: the resolved schema object that the test is running against.
 - `this.options`: the `options` object that validate() or isValid() was called with
 - `this.parent`: in the case of nested schema, this is the value of the parent object
-- `this.createError(Object: { path: String, message: String })`: create and return a
-  validation error. Useful for dynamically setting the `path`, or more likely, the error `message`.
+- `this.createError(Object: { path: String, message: String, params: Object })`: create and return a
+  validation error. Useful for dynamically setting the `path`, `params`, or more likely, the error `message`.
   If either option is omitted it will use the current path, or default message.
 
 #### `mixed.test(options: object): Schema`
@@ -768,10 +795,10 @@ module.exports = function(formats = 'MMM dd, yyyy') {
     // check to see if the previous transform already parsed the date
     if (this.isType(value)) return value;
 
-    // the default coercion failed so lets try it with Moment.js instead
+    // the default coercion failed so let's try it with Moment.js instead
     value = Moment(originalValue, formats);
 
-    // if its valid return the date object, otherwise return an `InvalidDate`
+    // if it's valid return the date object, otherwise return an `InvalidDate`
     return value.isValid() ? value.toDate() : new Date('');
   });
 };
@@ -1125,7 +1152,7 @@ let invalidDate = new Date('');
 
 module.exports = yup.date().transform(function(value, originalValue) {
   if (this.isType(value)) return value;
-  // the default coercion transform failed so lets try it with Moment instead
+  // the default coercion transform failed so let's try it with Moment instead
   value = Moment(originalValue, parseFormats);
   return value.isValid() ? value.toDate() : invalidDate;
 });
@@ -1135,7 +1162,7 @@ Alternatively, each schema is a normal JavaScript constructor function that you
 using the normal patterns. Generally you should not inherit from `mixed` unless you know what you are doing,
 better to think of it as an abstract class. The other types are fair game though.
 
-You should keep in mind some basic guidelines when extending schemas
+You should keep in mind some basic guidelines when extending schemas:
 
 - never mutate an existing schema, always `clone()` and then mutate the new one before returning it.
   Built-in methods like `test` and `transform` take care of this for you, so you can safely use them (see below) without worrying
@@ -1159,7 +1186,7 @@ function parseDateFromFormats(formats, parseStrict) {
   });
 }
 
-// `addMethod` doesn't do anything special it's
+// `addMethod` doesn't do anything special; it's
 // equivalent to: yup.date.prototype.format = parseDateFromFormats
 yup.addMethod(yup.date, 'format', parseDateFromFormats);
 ```
@@ -1167,8 +1194,8 @@ yup.addMethod(yup.date, 'format', parseDateFromFormats);
 **Creating new Types**
 
 Yup schema use the common constructor pattern for modeling inheritance. You can use any
-utility or pattern that works with that pattern. The below demonstrates using the es6 class
-syntax since its less verbose, but you absolutely aren't required to use it.
+utility or pattern that works with that pattern. The below demonstrates using the ES6 class
+syntax since it's less verbose, but you absolutely aren't required to use it.
 
 ```js
 let DateSchema = yup.date;
diff --git a/package.json b/package.json
index a63c118..ebdb51c 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,9 @@
 {
   "name": "yup",
-  "version": "0.27.0",
+  "version": "0.28.1",
   "description": "Dead simple Object schema validation",
   "main": "lib/index.js",
+  "module": "es/index.js",
   "runkitExampleFilename": "./runkit-example.js",
   "scripts": {
     "test": "npm run lint && npm run test-all -- --runInBand",
@@ -13,17 +14,22 @@
     "precommit": "lint-staged",
     "toc": "doctoc README.md --github",
     "release": "rollout",
-    "build": "babel src --out-dir lib --delete-dir-on-start && npm run toc",
+    "build": "yarn build:commonjs && yarn build:modules && npm run toc",
+    "build:commonjs": "babel src --out-dir lib --delete-dir-on-start",
+    "build:modules": "babel src --out-dir es --delete-dir-on-start --env-name modules",
     "prepublishOnly": "npm run build"
   },
   "files": [
+    "es",
     "lib"
   ],
   "repository": {
     "type": "git",
-    "url": "https://github.com/jquense/yup.git"
+    "url": "git+https://github.com/jquense/yup.git"
+  },
+  "author": {
+    "name": "@monasticpanic Jason Quense"
   },
-  "author": "@monasticpanic Jason Quense",
   "license": "MIT",
   "bugs": {
     "url": "https://github.com/jquense/yup/issues"
@@ -63,6 +69,7 @@
     "babel-core": "^7.0.0-bridge.0",
     "babel-eslint": "^9.0.0",
     "babel-jest": "^22.4.3",
+    "babel-plugin-transform-rename-import": "^2.3.0",
     "babel-preset-jason": "^6.0.1",
     "benchmark": "^2.0.0",
     "chai": "^4.2.0",
@@ -91,8 +98,11 @@
     "@babel/runtime": "^7.0.0",
     "fn-name": "~2.0.1",
     "lodash": "^4.17.11",
+    "lodash-es": "^4.17.11",
     "property-expr": "^1.5.0",
     "synchronous-promise": "^2.0.6",
     "toposort": "^2.0.2"
-  }
+  },
+  "readme": "ERROR: No README data found!",
+  "_id": "yup@0.28.0"
 }
diff --git a/src/array.js b/src/array.js
index c053963..e66fead 100644
--- a/src/array.js
+++ b/src/array.js
@@ -46,8 +46,11 @@ inherits(ArraySchema, MixedSchema, {
     if (!this._typeCheck(value) || !this._subType) return value;
 
     let isChanged = false;
-    const castArray = value.map(v => {
-      const castElement = this._subType.cast(v, _opts);
+    const castArray = value.map((v, idx) => {
+      const castElement = this._subType.cast(v, {
+        ..._opts,
+        path: makePath`${_opts.path}[${idx}]`,
+      });
       if (castElement !== v) {
         isChanged = true;
       }
diff --git a/src/date.js b/src/date.js
index 08c1210..c50e906 100644
--- a/src/date.js
+++ b/src/date.js
@@ -21,7 +21,8 @@ function DateSchema() {
       if (this.isType(value)) return value;
 
       value = isoParse(value);
-      return value ? new Date(value) : invalidDate;
+      // 0 is a valid timestamp equivalent to 1970-01-01T00:00:00Z(unix epoch) or before.
+      return !isNaN(value) ? new Date(value) : invalidDate;
     });
   });
 }
diff --git a/src/locale.js b/src/locale.js
index 0b719a1..df2e589 100644
--- a/src/locale.js
+++ b/src/locale.js
@@ -20,6 +20,7 @@ export let mixed = {
 
     return msg;
   },
+  defined: '${path} must be defined',
 };
 
 export let string = {
diff --git a/src/mixed.js b/src/mixed.js
index cb8818c..700f702 100644
--- a/src/mixed.js
+++ b/src/mixed.js
@@ -540,6 +540,17 @@ const proto = (SchemaType.prototype = {
         ),
     };
   },
+
+  defined(message = locale.defined) {
+    return this.nullable().test({
+      message,
+      name: 'defined',
+      exclusive: true,
+      test(value) {
+        return value !== undefined;
+      },
+    });
+  },
 });
 
 for (const method of ['validate', 'validateSync'])
diff --git a/yarn.lock b/yarn.lock
index 54d3ccd..e0394d9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1403,6 +1403,11 @@ babel-plugin-syntax-object-rest-spread@^6.13.0:
   version "6.13.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
 
+babel-plugin-transform-rename-import@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-transform-rename-import/-/babel-plugin-transform-rename-import-2.3.0.tgz#5d9d645f937b0ca5c26a24b2510a06277b6ffd9b"
+  integrity sha512-dPgJoT57XC0PqSnLgl2FwNvxFrWlspatX2dkk7yjKQj5HHGw071vAcOf+hqW8ClqcBDMvEbm6mevn5yHAD8mlQ==
+
 babel-preset-jason@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/babel-preset-jason/-/babel-preset-jason-6.0.1.tgz#078e053d187340b5d82eb3f3a0aefb7735033ed2"
@@ -4923,6 +4928,11 @@ locate-path@^3.0.0:
     p-locate "^3.0.0"
     path-exists "^3.0.0"
 
+lodash-es@^4.17.11:
+  version "4.17.15"
+  resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
+  integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
+
 lodash._reinterpolate@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"


Test patch:
diff --git a/test/array.js b/test/array.js
index ff303ef..5c3bec5 100644
--- a/test/array.js
+++ b/test/array.js
@@ -175,4 +175,19 @@ describe('Array types', () => {
 
     inst.cast(null).should.eql([]);
   });
+
+  it('should pass resolved path to descendants', async () => {
+    let value = ['2', '3'];
+    let expectedPaths = ['[0]', '[1]'];
+
+    let itemSchema = string().when([], function(_, context) {
+      let path = context.path || '';
+      path.should.be.oneOf(expectedPaths);
+      return string().required();
+    });
+
+    await array()
+      .of(itemSchema)
+      .validate(value);
+  });
 });
diff --git a/test/mixed.js b/test/mixed.js
index 1a9957c..5a30e25 100644
--- a/test/mixed.js
+++ b/test/mixed.js
@@ -930,4 +930,41 @@ describe('Mixed Types ', () => {
       },
     });
   });
+
+  describe('defined', () => {
+    it('should fail when value is undefined', async () => {
+      let inst = object({
+        prop: string().defined(),
+      });
+
+      await inst
+        .validate({})
+        .should.be.rejected()
+        .then(function(err) {
+          err.message.should.equal('prop must be defined');
+        });
+    });
+
+    it('should pass when value is null', async () => {
+      let inst = object({
+        prop: string().defined(),
+      });
+
+      await inst
+        .isValid({ prop: null })
+        .should.eventually()
+        .equal(true);
+    });
+
+    it('should pass when value is not undefined nor null', async () => {
+      let inst = object({
+        prop: string().defined(),
+      });
+
+      await inst
+        .isValid({ prop: 'prop value' })
+        .should.eventually()
+        .equal(true);
+    });
+  });
 });