/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: MPL-2.0 */ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { withModelValidations } from 'vault/decorators/model-validations'; import validators from 'vault/utils/validators'; import sinon from 'sinon'; import Model from '@ember-data/model'; // create class using decorator const createClass = (validations) => { @withModelValidations(validations) class Foo extends Model {} const foo = Foo.extend({ modelName: 'bar', foo: null, integer: null, }); return new foo(); }; module('Unit | Decorators | ModelValidations', function (hooks) { setupTest(hooks); hooks.beforeEach(function () { this.spy = sinon.spy(console, 'error'); }); hooks.afterEach(function () { this.spy.restore(); }); test('it should throw error when validations object is not provided', function (assert) { assert.expect(1); try { createClass(); } catch (e) { assert.strictEqual(e.message, 'Validations object must be provided to constructor for setup'); } }); test('it should log error to console when validations are not passed as array', function (assert) { const validations = { foo: { type: 'presence', message: 'Foo is required' }, }; const fooClass = createClass(validations); fooClass.validate(); const message = 'Must provide validations as an array for property "foo" on bar model'; assert.ok(this.spy.calledWith(message)); }); test('it should log error for incorrect validator type', function (assert) { const validations = { foo: [{ type: 'bar', message: 'Foo is bar' }], }; const fooClass = createClass(validations); fooClass.validate(); const message = `Validator type: "bar" not found. Available validators: ${Object.keys(validators).join( ', ' )}`; assert.ok(this.spy.calledWith(message)); }); test('it should validate', function (assert) { const message = 'This field is required'; const validations = { foo: [{ type: 'presence', message }], }; const fooClass = createClass(validations); const v1 = fooClass.validate(); assert.false(v1.isValid, 'isValid state is correct when errors exist'); assert.deepEqual( v1.state, { foo: { isValid: false, errors: [message], warnings: [] } }, 'Correct state returned when property is invalid' ); fooClass.foo = true; const v2 = fooClass.validate(); assert.true(v2.isValid, 'isValid state is correct when no errors exist'); assert.deepEqual( v2.state, { foo: { isValid: true, errors: [], warnings: [] } }, 'Correct state returned when property is valid' ); }); test('invalid form message has correct error count', function (assert) { const message = 'This field is required'; const messageII = 'This field must be a number'; const validations = { foo: [{ type: 'presence', message }], integer: [{ type: 'number', messageII }], }; const fooClass = createClass(validations); const v1 = fooClass.validate(); assert.strictEqual( v1.invalidFormMessage, 'There are 2 errors with this form.', 'error message says form as 2 errors' ); fooClass.integer = 9; const v2 = fooClass.validate(); assert.strictEqual( v2.invalidFormMessage, 'There is an error with this form.', 'error message says form has an error' ); fooClass.foo = true; const v3 = fooClass.validate(); assert.strictEqual(v3.invalidFormMessage, null, 'invalidFormMessage is null when form is valid'); }); test('it should validate warnings', function (assert) { const message = 'Value contains whitespace.'; const validations = { foo: [ { type: 'containsWhiteSpace', message, level: 'warn', }, ], }; const fooClass = createClass(validations); fooClass.foo = 'foo bar'; const { state, isValid } = fooClass.validate(); assert.true(isValid, 'Model is considered valid when there are only warnings'); assert.strictEqual(state.foo.warnings.join(' '), message, 'Warnings are returned'); }); });