Repository: baianat/vee-validate (js)

Issue:
validator.verify does not use i18n
Hello,

I am using vee-validate's validator.verify programmatically to validate ElementUi forms sort of like the "vee-element" does.

I got to the part where i need to localize the application, i am already using vue-i18n within my app and managed to merge element's localization to same dictionary, setup async localization etc. Only missing localizations are validation messages.

Since i am not using it as `Vue.use(VeeValidate,{i18n:i18nInstance})` and `VeeValidate.Validator` constructor options does not get an i18n option, it just uses built-in localization mechanism.

I currently worked around this with a hacky solution but i would much prefer if validator.verify also was able to use vue-i18n.


Pull request description:
refactor: plugin class
This refactors the plugin to use a class to install itself and to hold its config and dependencies. It also allows using the plugin programmatically without installing it. Much like VueI18n.

This allows configuring vee-validate without having to call `Vue.use`. Which helps using `i18n` with the `verify` API.

```js
import VeeValidate from 'vee-validate';

const vee = new VeeValidate({
  // config...
});
```

closes #1638 


Patch:
diff --git a/docs/configuration.md b/docs/configuration.md
index 8b2188fa..9be55ed1 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -21,8 +21,7 @@ const config = {
   i18nRootKey: 'validations', // the nested key under which the validation messages will be located
   inject: true,
   locale: 'en',
-  strict: true,
-  validity: false,
+  validity: false
 };
 
 Vue.use(VeeValidate, config);
@@ -43,5 +42,4 @@ Vue.use(VeeValidate, config);
 | i18nRootKey   | `string` | `'validations'` | The key name of the validation messages for each locale. |
 | inject        | `boolean` | `true` | Specifies if a validator instance should be injected automatically for all components. (See [Component Injections](advanced/#component-injections) for more information.) |
 | locale        | `string` | `'en'` | The default language for the validation messages. |
-| strict        | `boolean` | `true`    | Validation attempt on non-existent fields will result in an invalid state |
 | validity      | `boolean` | `false` | Set custom validity [Constraint validation](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation) on native HTML inputs. |
diff --git a/src/config.js b/src/config.js
deleted file mode 100644
index 44f34a7c..00000000
--- a/src/config.js
+++ /dev/null
@@ -1,74 +0,0 @@
-import { assign, getPath } from './utils';
-import Dictionary from './core/localization/default';
-import I18nDictionary from './core/localization/i18n';
-
-// @flow
-
-const defaultConfig = {
-  locale: 'en',
-  delay: 0,
-  errorBagName: 'errors',
-  dictionary: null,
-  strict: true,
-  fieldsBagName: 'fields',
-  classes: false,
-  classNames: null,
-  events: 'input',
-  inject: true,
-  fastExit: true,
-  aria: true,
-  validity: false,
-  i18n: null,
-  i18nRootKey: 'validation'
-};
-
-let currentConfig = assign({}, defaultConfig);
-const dependencies = {
-  dictionary: new Dictionary({
-    en: {
-      messages: {},
-      attributes: {},
-      custom: {}
-    }
-  })
-};
-
-export default class Config {
-  static get default () {
-    return defaultConfig;
-  }
-
-  static get current () {
-    return currentConfig;
-  }
-
-  static dependency (key) {
-    return dependencies[key];
-  }
-
-  /**
-   * Merges the config with a new one.
-   */
-  static merge (config) {
-    currentConfig = assign({}, currentConfig, config);
-    if (currentConfig.i18n) {
-      Config.register('dictionary', new I18nDictionary(currentConfig.i18n, currentConfig.i18nRootKey));
-    }
-  }
-
-  /**
-   * Registers a dependency.
-   */
-  static register (key: string, value: any) {
-    dependencies[key] = value;
-  }
-
-  /**
-   * Resolves the working config from a Vue instance.
-   */
-  static resolve (context: Object): MapObject {
-    const selfConfig = getPath('$options.$_veeValidate', context, {});
-
-    return assign({}, Config.current, selfConfig);
-  }
-};
diff --git a/src/core/resolver.js b/src/core/resolver.js
index f155fd91..e46a01dc 100644
--- a/src/core/resolver.js
+++ b/src/core/resolver.js
@@ -1,4 +1,4 @@
-import Config from '../config';
+import { pluginInstance as VeeValidate } from '../plugin';
 import {
   getScope,
   getDataAttribute,
@@ -20,7 +20,7 @@ import {
 export default class Resolver {
   static generate (el, binding, vnode) {
     const model = Resolver.resolveModel(binding, vnode);
-    const options = Config.resolve(vnode.context);
+    const options = VeeValidate.resolveConfig(vnode.context);
 
     return {
       name: Resolver.resolveName(el, vnode),
@@ -150,8 +150,8 @@ export default class Resolver {
       events = config && config.events;
     }
 
-    if (!events && Config.current.events) {
-      events = Config.current.events;
+    if (!events && VeeValidate.config.events) {
+      events = VeeValidate.config.events;
     }
 
     // resolve the model event if its configured for custom components.
diff --git a/src/core/validator.js b/src/core/validator.js
index b693a2a3..308ee204 100644
--- a/src/core/validator.js
+++ b/src/core/validator.js
@@ -2,7 +2,7 @@ import ErrorBag from './errorBag';
 import FieldBag from './fieldBag';
 import RuleContainer from './ruleContainer';
 import Field from './field';
-import Config from '../config';
+import { pluginInstance as VeeValidate } from '../plugin';
 import {
   isObject,
   getPath,
@@ -19,10 +19,7 @@ import {
 
 // @flow
 
-let STRICT_MODE: boolean = true;
-
 export default class Validator {
-  strict: boolean;
   errors: ErrorBag;
   fields: FieldBag;
   flags: MapObject;
@@ -31,7 +28,6 @@ export default class Validator {
   reset: (matcher) => Promise<void>;
 
   constructor (validations?: MapObject, options?: MapObject = { fastExit: true }) {
-    this.strict = STRICT_MODE;
     this.errors = new ErrorBag();
     this.fields = new FieldBag();
     this._createFields(validations);
@@ -67,15 +63,15 @@ export default class Validator {
    * Getter for the dictionary.
    */
   get dictionary (): IDictionary {
-    return Config.dependency('dictionary');
+    return VeeValidate.i18nDriver;
   }
 
   static get dictionary () {
-    return Config.dependency('dictionary');
+    return VeeValidate.i18nDriver;
   }
 
   get _vm () {
-    return Config.dependency('vm');
+    return VeeValidate._vm;
   }
 
   /**
@@ -102,8 +98,8 @@ export default class Validator {
   static set locale (value) {
     const hasChanged = value !== Validator.dictionary.locale;
     Validator.dictionary.locale = value;
-    if (hasChanged && Config.dependency('vm')) {
-      Config.dependency('vm').$emit('localeChanged');
+    if (hasChanged && this._vm) {
+      this._vm.$emit('localeChanged');
     }
   }
 
@@ -140,15 +136,6 @@ export default class Validator {
     return RuleContainer.isTargetRule(name);
   }
 
-  /**
-   * Sets the operating mode for all newly created validators.
-   * strictMode = true: Values without a rule are invalid and cause failure.
-   * strictMode = false: Values without a rule are valid and are skipped.
-   */
-  static setStrictMode (strictMode?: boolean = true) {
-    STRICT_MODE = strictMode;
-  }
-
   /**
    * Adds and sets the current locale for the validator.
    */
@@ -654,11 +641,9 @@ export default class Validator {
   }
 
   /**
-   * Handles when a field is not found depending on the strict flag.
+   * Handles when a field is not found.
    */
   _handleFieldNotFound (name: string, scope?: string | null) {
-    if (!this.strict) return Promise.resolve(true);
-
     const fullName = isNullOrUndefined(scope) ? name : `${!isNullOrUndefined(scope) ? scope + '.' : ''}${name}`;
 
     return Promise.reject(createError(
diff --git a/src/dictionary.js b/src/dictionary.js
new file mode 100644
index 00000000..80d3baea
--- /dev/null
+++ b/src/dictionary.js
@@ -0,0 +1,35 @@
+import { createError } from './utils';
+import DefaultDictionary from './core/localization/default';
+
+const drivers = {
+  default: new DefaultDictionary({
+    en: {
+      messages: {},
+      attributes: {},
+      custom: {}
+    }
+  })
+};
+
+let currentDriver = 'default';
+
+export default class DictionaryResolver {
+  static _checkDriverName (driver) {
+    if (!driver) {
+      throw createError('you must provide a name to the dictionary drive');
+    }
+  }
+
+  static setDriver (driver, implementation = null) {
+    this._checkDriverName(driver);
+    if (implementation) {
+      drivers[driver] = implementation;
+    }
+
+    currentDriver = driver || 'default';
+  }
+
+  static getDriver () {
+    return drivers[currentDriver];
+  }
+};
diff --git a/src/index.esm.js b/src/index.esm.js
index b607b072..d3c434b0 100644
--- a/src/index.esm.js
+++ b/src/index.esm.js
@@ -1,8 +1,7 @@
-import install from './install';
+import VeeValidate from './plugin';
 import directive from './directive';
 import mixin from './mixin';
 import en from '../locale/en';
-import use from './use';
 import * as Rules from './rules';
 import mapFields from './core/mapFields';
 import Validator from './core/validator';
@@ -21,11 +20,11 @@ const rulesPlugin = ({ Validator }) => {
   Validator.localize('en', en);
 };
 
-use(rulesPlugin);
+const install = VeeValidate.install;
+VeeValidate.use(rulesPlugin);
 
 export {
   install,
-  use,
   directive,
   mixin,
   mapFields,
@@ -36,15 +35,4 @@ export {
   version
 };
 
-export default {
-  install,
-  use,
-  directive,
-  mixin,
-  mapFields,
-  Validator,
-  ErrorBag,
-  ErrorComponent,
-  Rules,
-  version
-};
+export default VeeValidate;
diff --git a/src/index.js b/src/index.js
index 600a56f7..6a873bd6 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,7 +1,7 @@
 import * as Rules from './rules';
-import en from '../locale/en';
-import minimal from './index.minimal';
+import VeeValidate from './plugin';
 import { assign } from './utils';
+import en from '../locale/en';
 
 // rules plugin definition.
 const rulesPlugin = ({ Validator }) => {
@@ -13,9 +13,7 @@ const rulesPlugin = ({ Validator }) => {
   Validator.localize('en', en);
 };
 
-// install the rules via the plugin API.
-minimal.use(rulesPlugin);
-
-minimal.Rules = Rules;
+VeeValidate.use(rulesPlugin);
+VeeValidate.Rules = Rules;
 
-export default minimal;
+export default VeeValidate;
diff --git a/src/index.minimal.esm.js b/src/index.minimal.esm.js
index f29ba0d1..d8cea5b8 100644
--- a/src/index.minimal.esm.js
+++ b/src/index.minimal.esm.js
@@ -1,13 +1,14 @@
-import Validator from './core/validator';
+import VeeValidate from './plugin';
 import directive from './directive';
 import mixin from './mixin';
-import install from './install';
-import use from './use';
+import Validator from './core/validator';
 import ErrorBag from './core/errorBag';
 import mapFields from './core/mapFields';
 import { ErrorComponent } from './components';
 
 const version = '__VERSION__';
+const install = VeeValidate.install;
+const use = VeeValidate.use;
 
 export {
   install,
@@ -21,14 +22,4 @@ export {
   ErrorComponent
 };
 
-export default {
-  install,
-  use,
-  directive,
-  mixin,
-  mapFields,
-  Validator,
-  ErrorBag,
-  ErrorComponent,
-  version
-};
+export default VeeValidate;
diff --git a/src/index.minimal.js b/src/index.minimal.js
index ffea935f..59325839 100644
--- a/src/index.minimal.js
+++ b/src/index.minimal.js
@@ -1,20 +1,3 @@
-import install from './install';
-import directive from './directive';
-import mixin from './mixin';
-import use from './use';
-import Validator from './core/validator';
-import mapFields from './core/mapFields';
-import ErrorBag from './core/errorBag';
-import { ErrorComponent } from './components';
+import VeeValidate from './plugin';
 
-export default {
-  install,
-  use,
-  directive,
-  mixin,
-  mapFields,
-  Validator,
-  ErrorBag,
-  ErrorComponent,
-  version: '__VERSION__'
-};
+export default VeeValidate;
diff --git a/src/install.js b/src/install.js
deleted file mode 100644
index 547a8257..00000000
--- a/src/install.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import mixin from './mixin';
-import directive from './directive';
-import Config from './config';
-import Validator from './core/validator';
-import { warn, detectPassiveSupport } from './utils';
-
-let Vue;
-
-function install (_Vue, options = {}) {
-  if (Vue && _Vue === Vue) {
-    if (process.env.NODE_ENV !== 'production') {
-      warn('already installed, Vue.use(VeeValidate) should only be called once.');
-    }
-    return;
-  }
-
-  detectPassiveSupport();
-  Vue = _Vue;
-  const validator = new Validator(null, options);
-  const localVue = new Vue({
-    data: () => ({
-      errors: validator.errors,
-      fields: validator.fields
-    })
-  });
-  Config.register('vm', localVue);
-  Config.register('validator', validator);
-  Config.merge(options);
-
-  const { dictionary, i18n } = Config.current;
-
-  if (dictionary) {
-    validator.localize(dictionary); // merge the dictionary.
-  }
-
-  const onLocaleChanged = () => {
-    validator.errors.regenerate();
-  };
-
-  // watch locale changes using localVue instance or i18n.
-  if (!i18n) {
-    if (typeof window !== 'undefined') {
-      localVue.$on('localeChanged', onLocaleChanged);
-    }
-  } else {
-    i18n._vm.$watch('locale', onLocaleChanged);
-  }
-
-  if (!i18n && options.locale) {
-    validator.localize(options.locale); // set the locale
-  }
-
-  Validator.setStrictMode(Config.current.strict);
-
-  Vue.mixin(mixin);
-  Vue.directive('validate', directive);
-};
-
-export default install;
diff --git a/src/mixin.js b/src/mixin.js
index 482ac83d..6e13b69d 100644
--- a/src/mixin.js
+++ b/src/mixin.js
@@ -1,4 +1,4 @@
-import Config from './config';
+import { pluginInstance as VeeValidate } from './plugin';
 import Validator from './core/validatorDecorator';
 import { isObject, isBuiltInComponent } from './utils';
 
@@ -33,21 +33,21 @@ export default {
 
     // if its a root instance set the config if it exists.
     if (!this.$parent) {
-      Config.merge(this.$options.$_veeValidate || {});
+      VeeValidate.configure(this.$options.$_veeValidate || {});
     }
 
-    const options = Config.resolve(this);
+    const options = VeeValidate.resolveConfig(this);
 
     // if its a root instance, inject anyways, or if it requested a new instance.
     if (!this.$parent || (this.$options.$_veeValidate && /new/.test(this.$options.$_veeValidate.validator))) {
-      this.$validator = new Validator(Config.dependency('validator'), this);
+      this.$validator = new Validator(VeeValidate._validator, this);
     }
 
     const requested = requestsValidator(this.$options.inject);
 
     // if automatic injection is enabled and no instance was requested.
     if (! this.$validator && options.inject && !requested) {
-      this.$validator = new Validator(Config.dependency('validator'), this);
+      this.$validator = new Validator(VeeValidate._validator, this);
     }
 
     // don't inject errors or fieldBag as no validator was resolved.
diff --git a/src/plugin.js b/src/plugin.js
new file mode 100644
index 00000000..773c5b69
--- /dev/null
+++ b/src/plugin.js
@@ -0,0 +1,159 @@
+import dictionary from './dictionary';
+import mixin from './mixin';
+import directive from './directive';
+import { assign, getPath, warn, isCallable, detectPassiveSupport } from './utils';
+import Validator from './core/validator';
+import ErrorBag from './core/errorBag';
+import mapFields from './core/mapFields';
+import I18nDictionary from './core/localization/i18n';
+
+// @flow
+
+const defaultConfig = {
+  locale: 'en',
+  delay: 0,
+  errorBagName: 'errors',
+  dictionary: null,
+  fieldsBagName: 'fields',
+  classes: false,
+  classNames: null,
+  events: 'input',
+  inject: true,
+  fastExit: true,
+  aria: true,
+  validity: false,
+  i18n: null,
+  i18nRootKey: 'validation'
+};
+
+let Vue;
+let pendingPlugins;
+export let currentConfig = assign({}, defaultConfig);
+export let pluginInstance;
+
+class VeeValidate {
+  static version: string
+  static install: () => void
+  static Validator: Function<Validator>
+
+  _vm: any
+
+  constructor (config) {
+    this.configure(config);
+    pluginInstance = this;
+    this._validator = new Validator(null, { fastExit: config && config.fastExit });
+    this._initVM(this.config);
+    this._initI18n(this.config);
+  }
+
+  static setI18nDriver (driver: string, instance): void {
+    dictionary.setDriver(driver, instance);
+  }
+
+  static configure (cfg) {
+    currentConfig = assign({}, currentConfig, cfg);
+  }
+
+  static use (plugin: (ctx: PluginContext, options?: any) => any, options?: any = {}) {
+    if (!isCallable(plugin)) {
+      return warn('The plugin must be a callable function');
+    }
+
+    // Don't install plugins until vee-validate is installed.
+    if (!pluginInstance) {
+      if (!pendingPlugins) {
+        pendingPlugins = [];
+      }
+      pendingPlugins.push({ plugin, options });
+      return;
+    }
+
+    plugin({ Validator, ErrorBag, Rules: Validator.rules }, options);
+  };
+
+  static install (_Vue, opts) {
+    if (Vue && _Vue === Vue) {
+      if (process.env.NODE_ENV !== 'production') {
+        warn('already installed, Vue.use(VeeValidate) should only be called once.');
+      }
+      return;
+    }
+
+    Vue = _Vue;
+    pluginInstance = new VeeValidate(opts);
+
+    detectPassiveSupport();
+
+    Vue.mixin(mixin);
+    Vue.directive('validate', directive);
+    if (pendingPlugins) {
+      pendingPlugins.forEach(({ plugin, options }) => {
+        VeeValidate.use(plugin, options);
+      });
+      pendingPlugins = null;
+    }
+  }
+
+  get i18nDriver (): IDictionary {
+    return dictionary.getDriver();
+  }
+
+  static get i18nDriver (): IDictionary {
+    return dictionary.getDriver();
+  }
+
+  get config () {
+    return currentConfig;
+  }
+
+  _initVM (config) {
+    this._vm = new Vue({
+      data: () => ({
+        errors: this._validator.errors,
+        fields: this._validator.fields
+      })
+    });
+  }
+
+  _initI18n (config) {
+    const { dictionary, i18n, i18nRootKey, locale } = config;
+    const onLocaleChanged = () => {
+      this._validator.errors.regenerate();
+    };
+
+    // i18 is being used for localization.
+    if (i18n) {
+      VeeValidate.setI18nDriver('i18n', new I18nDictionary(i18n, i18nRootKey));
+      i18n._vm.$watch('locale', onLocaleChanged);
+    } else if (typeof window !== 'undefined') {
+      this._vm.$on('localeChanged', onLocaleChanged);
+    }
+
+    if (dictionary) {
+      this.i18nDriver.merge(dictionary);
+    }
+
+    if (locale && !i18n) {
+      this._validator.localize(locale);
+    }
+  }
+
+  configure (cfg) {
+    VeeValidate.configure(cfg);
+  }
+
+  resolveConfig (ctx) {
+    const selfConfig = getPath('$options.$_veeValidate', ctx, {});
+
+    return assign({}, this.config, selfConfig);
+  }
+}
+
+VeeValidate.version = '__VERSION__';
+VeeValidate.mixin = mixin;
+VeeValidate.directive = directive;
+VeeValidate.Validator = Validator;
+VeeValidate.ErrorBag = ErrorBag;
+VeeValidate.mapFields = mapFields;
+
+export default VeeValidate;
diff --git a/src/use.js b/src/use.js
deleted file mode 100644
index ae6fd1e2..00000000
--- a/src/use.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import Validator from './core/validator';
-import ErrorBag from './core/errorBag';
-import { warn, isCallable } from './utils';
-
-// @flow
-
-function use (plugin: (ctx: PluginContext, options?: any) => any, options?: any = {}) {
-  if (!isCallable(plugin)) {
-    return warn('The plugin must be a callable function');
-  }
-
-  plugin({ Validator, ErrorBag, Rules: Validator.rules }, options);
-};
-
-export default use;


Test patch:
diff --git a/tests/integration/lifecycle.js b/tests/integration/lifecycle.js
index cf04046d..1dcc6b4b 100644
--- a/tests/integration/lifecycle.js
+++ b/tests/integration/lifecycle.js
@@ -1,4 +1,4 @@
-import { shallow, createLocalVue } from '@vue/test-utils';
+import { mount, createLocalVue } from '@vue/test-utils';
 import VeeValidate from '@/index';
 import BasicComponent from './components/Basic';
 
@@ -6,14 +6,14 @@ test('bind: warns if no validator instance was found in the directive context',
   global.console.warn = jest.fn();
   const Vue = createLocalVue();
   Vue.directive('validate', VeeValidate.directive);
-  shallow(BasicComponent, { localVue: Vue });
+  mount(BasicComponent, { localVue: Vue });
   expect(global.console.warn).toHaveBeenCalled();
 });
 
 test('unbind: does not detach the field if it does not exist', () => {
   const Vue = createLocalVue();
   Vue.use(VeeValidate);
-  const wrapper = shallow(BasicComponent, { localVue: Vue });
+  const wrapper = mount(BasicComponent, { localVue: Vue });
   const validator = wrapper.vm.$validator;
   const detach = validator.detach.bind(validator);
   validator.detach = jest.fn(detach);
@@ -25,7 +25,7 @@ test('unbind: does not detach the field if it does not exist', () => {
 test('destroy: removes vm associated errors when the vm is destroyed', async () => {
   const Vue = createLocalVue();
   Vue.use(VeeValidate);
-  const wrapper = shallow(BasicComponent, { localVue: Vue });
+  const wrapper = mount(BasicComponent, { localVue: Vue });
   const input = wrapper.find('input');
   input.value = '';
   await wrapper.vm.$validator.validate();
diff --git a/tests/unit/config.js b/tests/unit/config.js
deleted file mode 100644
index 6648decc..00000000
--- a/tests/unit/config.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import Config from '@/config';
-import Dictionary from '@/core/localization/i18n';
-
-test('it stores the default config', () => {
-  expect(Config.default).toEqual({
-    locale: 'en',
-    delay: 0,
-    errorBagName: 'errors',
-    dictionary: null,
-    strict: true,
-    fieldsBagName: 'fields',
-    classes: false,
-    classNames: null,
-    events: 'input',
-    inject: true,
-    fastExit: true,
-    aria: true,
-    validity: false,
-    i18n: null,
-    i18nRootKey: 'validation'
-  });
-});
-
-test('it merges new config with the current one', () => {
-  expect(Config.current.aria).toBe(true);
-  Config.merge({ aria: false });
-  expect(Config.current.aria).toBe(false);
-});
-
-describe('resolves the working config from a vue instance', () => {
-  test('when no config is set', () => {
-    const conf = Config.resolve({
-      $options: {}
-    });
-
-    expect(conf.validity).toBe(false);
-  });
-
-  test('when config is set', () => {
-    const conf = Config.resolve({
-      $options: {
-        $_veeValidate: {
-          validity: true
-        }
-      }
-    });
-
-    expect(conf.validity).toBe(true);
-  });
-});
-
-test('can fetch/register dependencies', () => {
-  expect(Config.dependency('key')).toBe(undefined);
-  const myDep = {};
-  Config.register('key', myDep);
-  expect(Config.dependency('key')).toBe(myDep);
-});
-
-test('initializes i18n dictionary', () => {
-  expect(Config.dependency('dictionary') instanceof Dictionary).toBe(false);
-  Config.merge({
-    i18n: { locale: 'en' }
-  });
-
-  expect(Config.dependency('dictionary') instanceof Dictionary).toBe(true);
-});
diff --git a/tests/unit/validator.js b/tests/unit/validator.js
index b1b9c6e8..b5326407 100644
--- a/tests/unit/validator.js
+++ b/tests/unit/validator.js
@@ -1,13 +1,10 @@
 import { createLocalVue } from '@vue/test-utils';
 import Field from '@/core/field';
 import VeeValidate from '@/index';
-import Config from '@/config';
 
 const Vue = createLocalVue();
-
-Config.register('vm', new Vue());
-
 const Validator = VeeValidate.Validator;
+VeeValidate.install(Vue);
 
 // Converts the value to a boolean and returns it in a promise.
 Validator.extend('promised', (value) => {
@@ -20,7 +17,6 @@ Validator.extend('promised', (value) => {
 
 beforeEach(() => {
   Validator.localize('en');
-  Validator.setStrictMode(true);
 });
 
 // All tests are serial because the locale is shared across all validators
@@ -136,56 +132,6 @@ test('fails validation on a one-of-many failure', async () => {
   })).toBe(false);
 });
 
-test('bypasses values without rules in strictMode = off', async () => {
-  Validator.setStrictMode(false);
-  const v = new Validator({
-    imp: 'required'
-  });
-  const result = await v.validateAll({
-    imp: 'Tyrion Lannister',
-    headless: 'Ned Stark'
-  });
-
-  expect(result).toBe(true);
-  expect(v.errors.all()).toEqual([]);
-});
-
-test('can set strict mode on specific instances', async () => {
-  const v = new Validator({
-    imp: 'required'
-  });
-
-  try {
-    await v.validateAll({
-      imp: 'Tyrion Lannister',
-      headless: 'Ned Stark'
-    });
-  } catch (error) {
-    expect(error.message).toBe(
-      `[vee-validate] Validating a non-existant field: "headless". Use "attach()" first.`
-    );
-  }
-
-  v.strict = false;
-  let result = await v.validateAll({
-    imp: 'Tyrion Lannister',
-    headless: 'Ned Stark'
-  });
-
-  expect(result).toBe(true); // strict = false.
-
-  try {
-    await (new Validator({ imp: 'required' }).validateAll({
-      imp: 'Tyrion Lannister',
-      headless: 'Ned Stark'
-    })); // strict = true because this is a different instance.
-  } catch (error) {
-    expect(error.message).toBe(
-      `[vee-validate] Validating a non-existant field: "headless". Use "attach()" first.`
-    );
-  }
-});
-
 test('formats error messages', async () => {
   const v = new Validator({
     email: 'required|email',
@@ -235,7 +181,7 @@ test('can display errors with custom field names', async () => {
   expect(v.errors.first('field')).toBe('The pretty field must be at least 5 characters.');
 });
 
-test('fails when trying to validate a non-existant field when strict mode is true.', async () => {
+test('fails when trying to validate a non-existant field.', async () => {
   const v = new Validator({
     email: 'required|email',
     name: 'required|min:3',
@@ -851,12 +797,6 @@ test('resolves a field by name and scope', async () => {
   expect(v._resolveField('field', 0)).toBe(field);
 });
 
-test('handles unresolved fields when strict is false by returning true', async () => {
-  const v = new Validator();
-  v.strict = false;
-  expect(await v.validate('#plasd')).toBe(true);
-});
-
 test('updates classes after validating a field', async () => {
   const v = new Validator();
   const field = v.attach({ name: 'field', rules: 'alpha', el: document.createElement('input'), classes: true });
diff --git a/tests/unit/validatorDecorator.js b/tests/unit/validatorDecorator.js
index 48c6a3c3..f6bfe59b 100644
--- a/tests/unit/validatorDecorator.js
+++ b/tests/unit/validatorDecorator.js
@@ -1,6 +1,12 @@
+import { createLocalVue } from '@vue/test-utils';
 import Validator from '@/core/validator';
+import VeeValidate from '@/plugin';
 import Decorator from '@/core/validatorDecorator';
 
+const Vue = createLocalVue();
+
+VeeValidate.install(Vue);
+
 test('decorates validateAll()', () => {
   const vm = { _uid: 0 };
   const base = new Validator();