mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
UI/Fix form validation issues (#15560)
* clean up validators * fix getter overriding user input * add changelog * remove asString option * move invalid check up * remove asString everywhere * revert input value defaults * undo form disabling if validation errors * address comments * remove or * add validation message to form, create pseudo loading icon * whole alert disappears with refresh * glimmerize alert-inline * add tests * rename variables for consistency * spread attributes to glimmerized component * address comments * add validation test
This commit is contained in:
3
changelog/15560.txt
Normal file
3
changelog/15560.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:bug
|
||||||
|
ui: fix form validations ignoring default values and disabling submit button
|
||||||
|
```
|
||||||
@@ -47,7 +47,7 @@ export default Component.extend({
|
|||||||
|
|
||||||
// validation related properties
|
// validation related properties
|
||||||
modelValidations: null,
|
modelValidations: null,
|
||||||
isFormInvalid: false,
|
invalidFormAlert: null,
|
||||||
|
|
||||||
mountIssue: false,
|
mountIssue: false,
|
||||||
|
|
||||||
@@ -88,10 +88,24 @@ export default Component.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
checkModelValidity(model) {
|
||||||
|
const { isValid, state, invalidFormMessage } = model.validate();
|
||||||
|
this.setProperties({
|
||||||
|
modelValidations: state,
|
||||||
|
invalidFormAlert: invalidFormMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
},
|
||||||
|
|
||||||
mountBackend: task(
|
mountBackend: task(
|
||||||
waitFor(function* () {
|
waitFor(function* () {
|
||||||
const mountModel = this.mountModel;
|
const mountModel = this.mountModel;
|
||||||
const { type, path } = mountModel;
|
const { type, path } = mountModel;
|
||||||
|
// only submit form if validations pass
|
||||||
|
if (!this.checkModelValidity(mountModel)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let capabilities = null;
|
let capabilities = null;
|
||||||
try {
|
try {
|
||||||
capabilities = yield this.store.findRecord('capabilities', `${path}/config`);
|
capabilities = yield this.store.findRecord('capabilities', `${path}/config`);
|
||||||
@@ -120,7 +134,6 @@ export default Component.extend({
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.httpStatus === 403) {
|
if (err.httpStatus === 403) {
|
||||||
this.mountIssue = true;
|
this.mountIssue = true;
|
||||||
this.set('isFormInvalid', this.mountIssue);
|
|
||||||
this.flashMessages.danger(
|
this.flashMessages.danger(
|
||||||
'You do not have access to the sys/mounts endpoint. The secret engine was not mounted.'
|
'You do not have access to the sys/mounts endpoint. The secret engine was not mounted.'
|
||||||
);
|
);
|
||||||
@@ -163,12 +176,8 @@ export default Component.extend({
|
|||||||
actions: {
|
actions: {
|
||||||
onKeyUp(name, value) {
|
onKeyUp(name, value) {
|
||||||
this.mountModel.set(name, value);
|
this.mountModel.set(name, value);
|
||||||
const { isValid, state } = this.mountModel.validate();
|
|
||||||
this.setProperties({
|
|
||||||
modelValidations: state,
|
|
||||||
isFormInvalid: !isValid,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onTypeChange(path, value) {
|
onTypeChange(path, value) {
|
||||||
if (path === 'type') {
|
if (path === 'type') {
|
||||||
this.wizard.set('componentState', value);
|
this.wizard.set('componentState', value);
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export function withModelValidations(validations) {
|
|||||||
validate() {
|
validate() {
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
const state = {};
|
const state = {};
|
||||||
|
let errorCount = 0;
|
||||||
|
|
||||||
for (const key in this._validations) {
|
for (const key in this._validations) {
|
||||||
const rules = this._validations[key];
|
const rules = this._validations[key];
|
||||||
@@ -110,9 +111,18 @@ export function withModelValidations(validations) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
errorCount += state[key].errors.length;
|
||||||
state[key].isValid = !state[key].errors.length;
|
state[key].isValid = !state[key].errors.length;
|
||||||
}
|
}
|
||||||
return { isValid, state };
|
|
||||||
|
return { isValid, state, invalidFormMessage: this.generateErrorCountMessage(errorCount) };
|
||||||
|
}
|
||||||
|
|
||||||
|
generateErrorCountMessage(errorCount) {
|
||||||
|
if (errorCount < 1) return null;
|
||||||
|
// returns count specific message: 'There is an error/are N errors with this form.'
|
||||||
|
let isPlural = errorCount > 1 ? `are ${errorCount} errors` : false;
|
||||||
|
return `There ${isPlural ? isPlural : 'is an error'} with this form.`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const LIST_EXCLUDED_BACKENDS = ['system', 'identity'];
|
|||||||
const validations = {
|
const validations = {
|
||||||
path: [{ type: 'presence', message: "Path can't be blank." }],
|
path: [{ type: 'presence', message: "Path can't be blank." }],
|
||||||
maxVersions: [
|
maxVersions: [
|
||||||
{ type: 'number', options: { asString: true }, message: 'Maximum versions must be a number.' },
|
{ type: 'number', message: 'Maximum versions must be a number.' },
|
||||||
{ type: 'length', options: { min: 1, max: 16 }, message: 'You cannot go over 16 characters.' },
|
{ type: 'length', options: { min: 1, max: 16 }, message: 'You cannot go over 16 characters.' },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { withModelValidations } from 'vault/decorators/model-validations';
|
|||||||
|
|
||||||
const validations = {
|
const validations = {
|
||||||
maxVersions: [
|
maxVersions: [
|
||||||
{ type: 'number', options: { asString: true }, message: 'Maximum versions must be a number.' },
|
{ type: 'number', message: 'Maximum versions must be a number.' },
|
||||||
{ type: 'length', options: { min: 1, max: 16 }, message: 'You cannot go over 16 characters.' },
|
{ type: 'length', options: { min: 1, max: 16 }, message: 'You cannot go over 16 characters.' },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
type="submit"
|
type="submit"
|
||||||
data-test-mount-submit="true"
|
data-test-mount-submit="true"
|
||||||
class="button is-primary {{if this.mountBackend.isRunning 'loading'}}"
|
class="button is-primary {{if this.mountBackend.isRunning 'loading'}}"
|
||||||
disabled={{or this.mountBackend.isRunning this.isFormInvalid}}
|
disabled={{this.mountBackend.isRunning}}
|
||||||
>
|
>
|
||||||
{{#if (eq this.mountType "auth")}}
|
{{#if (eq this.mountType "auth")}}
|
||||||
Enable Method
|
Enable Method
|
||||||
@@ -81,6 +81,11 @@
|
|||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{{#if this.invalidFormAlert}}
|
||||||
|
<div class="control">
|
||||||
|
<AlertInline @type="danger" @paddingTop={{true}} @message={{this.invalidFormAlert}} @mimicRefresh={{true}} />
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<button
|
<button
|
||||||
data-test-mount-next
|
data-test-mount-next
|
||||||
|
|||||||
@@ -3,20 +3,20 @@ import { isPresent } from '@ember/utils';
|
|||||||
export const presence = (value) => isPresent(value);
|
export const presence = (value) => isPresent(value);
|
||||||
|
|
||||||
export const length = (value, { nullable = false, min, max } = {}) => {
|
export const length = (value, { nullable = false, min, max } = {}) => {
|
||||||
let isValid = nullable;
|
if (!min && !max) return;
|
||||||
if (typeof value === 'string') {
|
// value could be an integer if the attr has a default value of some number
|
||||||
const underMin = min && value.length < min;
|
const valueLength = value?.toString().length;
|
||||||
const overMax = max && value.length > max;
|
if (valueLength) {
|
||||||
isValid = underMin || overMax ? false : true;
|
const underMin = min && valueLength < min;
|
||||||
|
const overMax = max && valueLength > max;
|
||||||
|
return underMin || overMax ? false : true;
|
||||||
}
|
}
|
||||||
return isValid;
|
return nullable;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const number = (value, { nullable = false, asString } = {}) => {
|
export const number = (value, { nullable = false } = {}) => {
|
||||||
if (!value) return nullable;
|
// since 0 is falsy, !value returns true even though 0 is a valid number
|
||||||
if (typeof value === 'string' && !asString) {
|
if (!value && value !== 0) return nullable;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !isNaN(value);
|
return !isNaN(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
19
ui/lib/core/addon/components/alert-inline.hbs
Normal file
19
ui/lib/core/addon/components/alert-inline.hbs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<div
|
||||||
|
{{did-update this.refresh @message}}
|
||||||
|
class={{concat "message-inline" this.paddingTop this.isMarginless this.sizeSmall}}
|
||||||
|
data-test-inline-alert
|
||||||
|
...attributes
|
||||||
|
>
|
||||||
|
{{#if this.isRefreshing}}
|
||||||
|
<Icon @name="loading" class="loading" />
|
||||||
|
{{else}}
|
||||||
|
<Icon @name={{this.alertType.glyph}} class={{this.alertType.glyphClass}} />
|
||||||
|
<p class={{this.textClass}} data-test-inline-error-message>
|
||||||
|
{{#if (has-block)}}
|
||||||
|
{{yield}}
|
||||||
|
{{else}}
|
||||||
|
{{@message}}
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import Component from '@ember/component';
|
import Component from '@glimmer/component';
|
||||||
import { computed } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
|
import { later } from '@ember/runloop';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
import { messageTypes } from 'core/helpers/message-types';
|
import { messageTypes } from 'core/helpers/message-types';
|
||||||
import layout from '../templates/components/alert-inline';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module AlertInline
|
* @module AlertInline
|
||||||
@@ -12,31 +13,51 @@ import layout from '../templates/components/alert-inline';
|
|||||||
* <AlertInline @type="danger" @message="{{model.keyId}} is not a valid lease ID"/>
|
* <AlertInline @type="danger" @message="{{model.keyId}} is not a valid lease ID"/>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param type=null{String} - The alert type. This comes from the message-types helper.
|
* @param type=null{String} - The alert type passed to the message-types helper.
|
||||||
* @param [message=null]{String} - The message to display within the alert.
|
* @param [message=null]{String} - The message to display within the alert.
|
||||||
* @param [sizeSmall=false]{Boolean} - Whether or not to display a small font with padding below of alert message.
|
|
||||||
* @param [paddingTop=false]{Boolean} - Whether or not to add padding above component.
|
* @param [paddingTop=false]{Boolean} - Whether or not to add padding above component.
|
||||||
* @param [isMarginless=false]{Boolean} - Whether or not to remove margin bottom below component.
|
* @param [isMarginless=false]{Boolean} - Whether or not to remove margin bottom below component.
|
||||||
|
* @param [sizeSmall=false]{Boolean} - Whether or not to display a small font with padding below of alert message.
|
||||||
|
* @param [mimicRefresh=false]{Boolean} - If true will display a loading icon when attributes change (e.g. when a form submits and the alert message changes).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default Component.extend({
|
export default class AlertInlineComponent extends Component {
|
||||||
layout,
|
@tracked isRefreshing = false;
|
||||||
type: null,
|
|
||||||
message: null,
|
|
||||||
sizeSmall: false,
|
|
||||||
paddingTop: false,
|
|
||||||
classNames: ['message-inline'],
|
|
||||||
classNameBindings: ['sizeSmall:size-small', 'paddingTop:padding-top', 'isMarginless:is-marginless'],
|
|
||||||
|
|
||||||
textClass: computed('type', function () {
|
get mimicRefresh() {
|
||||||
if (this.type == 'danger') {
|
return this.args.mimicRefresh || false;
|
||||||
return messageTypes([this.type]).glyphClass;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
get paddingTop() {
|
||||||
}),
|
return this.args.paddingTop ? ' padding-top' : '';
|
||||||
|
}
|
||||||
|
|
||||||
alertType: computed('type', function () {
|
get isMarginless() {
|
||||||
return messageTypes([this.type]);
|
return this.args.isMarginless ? ' is-marginless' : '';
|
||||||
}),
|
}
|
||||||
});
|
|
||||||
|
get sizeSmall() {
|
||||||
|
return this.args.sizeSmall ? ' size-small' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
get textClass() {
|
||||||
|
if (this.args.type === 'danger') {
|
||||||
|
return this.alertType.glyphClass;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get alertType() {
|
||||||
|
return messageTypes([this.args.type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
refresh() {
|
||||||
|
if (this.mimicRefresh) {
|
||||||
|
this.isRefreshing = true;
|
||||||
|
later(() => {
|
||||||
|
this.isRefreshing = false;
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export default class FormFieldComponent extends Component {
|
|||||||
get validationError() {
|
get validationError() {
|
||||||
const validations = this.args.modelValidations || {};
|
const validations = this.args.modelValidations || {};
|
||||||
const state = validations[this.valuePath];
|
const state = validations[this.valuePath];
|
||||||
return state && !state.isValid ? state.errors.join('. ') : null;
|
return state && !state.isValid ? state.errors.join(' ') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange() {
|
onChange() {
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
<Icon @name={{this.alertType.glyph}} class={{this.alertType.glyphClass}} />
|
|
||||||
<p class={{this.textClass}} data-test-inline-error-message>
|
|
||||||
{{#if (has-block)}}
|
|
||||||
{{yield}}
|
|
||||||
{{else}}
|
|
||||||
{{@message}}
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
@@ -1,17 +1,82 @@
|
|||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
import { setupRenderingTest } from 'ember-qunit';
|
||||||
import { render } from '@ember/test-helpers';
|
import { render, settled, find, waitUntil } from '@ember/test-helpers';
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
|
||||||
module('Integration | Component | alert-inline', function (hooks) {
|
module('Integration | Component | alert-inline', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
test('it renders', async function (assert) {
|
hooks.beforeEach(function () {
|
||||||
// Set any properties with this.set('myProperty', 'value');
|
this.set('message', 'some very important alert');
|
||||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
});
|
||||||
|
|
||||||
await render(hbs`{{alert-inline type="danger" message="test message"}}`);
|
test('it renders alert message with correct class args', async function (assert) {
|
||||||
|
await render(hbs`
|
||||||
|
<AlertInline
|
||||||
|
@paddingTop={{true}}
|
||||||
|
@isMarginless={{true}}
|
||||||
|
@sizeSmall={{true}}
|
||||||
|
@message={{this.message}}
|
||||||
|
/>
|
||||||
|
`);
|
||||||
|
assert.dom('[data-test-inline-error-message]').hasText('some very important alert');
|
||||||
|
assert
|
||||||
|
.dom('[data-test-inline-alert]')
|
||||||
|
.hasAttribute('class', 'message-inline padding-top is-marginless size-small');
|
||||||
|
});
|
||||||
|
|
||||||
assert.dom(this.element).hasText('test message');
|
test('it yields to block text', async function (assert) {
|
||||||
|
await render(hbs`
|
||||||
|
<AlertInline @message={{this.message}}>
|
||||||
|
A much more important alert
|
||||||
|
</AlertInline>
|
||||||
|
`);
|
||||||
|
assert.dom('[data-test-inline-error-message]').hasText('A much more important alert');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders correctly for type=danger', async function (assert) {
|
||||||
|
this.set('type', 'danger');
|
||||||
|
await render(hbs`
|
||||||
|
<AlertInline
|
||||||
|
@type={{this.type}}
|
||||||
|
@message={{this.message}}
|
||||||
|
/>
|
||||||
|
`);
|
||||||
|
assert
|
||||||
|
.dom('[data-test-inline-error-message]')
|
||||||
|
.hasAttribute('class', 'has-text-danger', 'has danger text');
|
||||||
|
assert.dom('[data-test-icon="x-square-fill"]').exists('danger icon exists');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders correctly for type=warning', async function (assert) {
|
||||||
|
this.set('type', 'warning');
|
||||||
|
await render(hbs`
|
||||||
|
<AlertInline
|
||||||
|
@type={{this.type}}
|
||||||
|
@message={{this.message}}
|
||||||
|
/>
|
||||||
|
`);
|
||||||
|
assert.dom('[data-test-inline-error-message]').doesNotHaveAttribute('class', 'does not have styled text');
|
||||||
|
assert.dom('[data-test-icon="alert-triangle-fill"]').exists('warning icon exists');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it mimics loading when message changes', async function (assert) {
|
||||||
|
await render(hbs`
|
||||||
|
<AlertInline
|
||||||
|
@message={{this.message}}
|
||||||
|
@mimicRefresh={{true}}
|
||||||
|
/>
|
||||||
|
`);
|
||||||
|
assert
|
||||||
|
.dom('[data-test-inline-error-message]')
|
||||||
|
.hasText('some very important alert', 'it renders original message');
|
||||||
|
|
||||||
|
this.set('message', 'some changed alert!!!');
|
||||||
|
await waitUntil(() => find('[data-test-icon="loading"]'));
|
||||||
|
assert.ok(find('[data-test-icon="loading"]'), 'it shows loading icon when message changes');
|
||||||
|
await settled();
|
||||||
|
assert
|
||||||
|
.dom('[data-test-inline-error-message]')
|
||||||
|
.hasText('some changed alert!!!', 'it shows updated message');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const createClass = (validations) => {
|
|||||||
const foo = Foo.extend({
|
const foo = Foo.extend({
|
||||||
modelName: 'bar',
|
modelName: 'bar',
|
||||||
foo: null,
|
foo: null,
|
||||||
|
integer: null,
|
||||||
});
|
});
|
||||||
return new foo();
|
return new foo();
|
||||||
};
|
};
|
||||||
@@ -81,4 +82,32 @@ module('Unit | Decorators | ModelValidations', function (hooks) {
|
|||||||
'Correct state returned when property is valid'
|
'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.equal(
|
||||||
|
v1.invalidFormMessage,
|
||||||
|
'There are 2 errors with this form.',
|
||||||
|
'error message says form as 2 errors'
|
||||||
|
);
|
||||||
|
|
||||||
|
fooClass.integer = 9;
|
||||||
|
const v2 = fooClass.validate();
|
||||||
|
assert.equal(
|
||||||
|
v2.invalidFormMessage,
|
||||||
|
'There is an error with this form.',
|
||||||
|
'error message says form has an error'
|
||||||
|
);
|
||||||
|
|
||||||
|
fooClass.foo = true;
|
||||||
|
const v3 = fooClass.validate();
|
||||||
|
assert.equal(v3.invalidFormMessage, null, 'invalidFormMessage is null when form is valid');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,10 +6,18 @@ module('Unit | Util | validators', function (hooks) {
|
|||||||
setupTest(hooks);
|
setupTest(hooks);
|
||||||
|
|
||||||
test('it should validate presence', function (assert) {
|
test('it should validate presence', function (assert) {
|
||||||
let isValid = validators.presence(null);
|
let isValid;
|
||||||
assert.false(isValid);
|
const check = (value) => (isValid = validators.presence(value));
|
||||||
isValid = validators.presence(true);
|
check(null);
|
||||||
assert.true(isValid);
|
assert.false(isValid, 'Invalid when value is null');
|
||||||
|
check('');
|
||||||
|
assert.false(isValid, 'Invalid when value is empty string');
|
||||||
|
check(true);
|
||||||
|
assert.true(isValid, 'Valid when value is true');
|
||||||
|
check(0);
|
||||||
|
assert.true(isValid, 'Valid when value is 0 as integer');
|
||||||
|
check('0');
|
||||||
|
assert.true(isValid, 'Valid when value is 0 as string');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it should validate length', function (assert) {
|
test('it should validate length', function (assert) {
|
||||||
@@ -22,30 +30,44 @@ module('Unit | Util | validators', function (hooks) {
|
|||||||
check(null);
|
check(null);
|
||||||
assert.false(isValid, 'Invalid when nullable is false');
|
assert.false(isValid, 'Invalid when nullable is false');
|
||||||
check('12');
|
check('12');
|
||||||
assert.false(isValid, 'Invalid when not min length');
|
assert.false(isValid, 'Invalid when string not min length');
|
||||||
check('123456');
|
check('123456');
|
||||||
assert.false(isValid, 'Invalid when over max length');
|
assert.false(isValid, 'Invalid when string over max length');
|
||||||
check('1234');
|
check('1234');
|
||||||
assert.true(isValid, 'Valid when in between min and max length');
|
assert.true(isValid, 'Valid when string between min and max length');
|
||||||
|
check(12);
|
||||||
|
assert.false(isValid, 'Invalid when integer not min length');
|
||||||
|
check(123456);
|
||||||
|
assert.false(isValid, 'Invalid when integer over max length');
|
||||||
|
check(1234);
|
||||||
|
assert.true(isValid, 'Valid when integer between min and max length');
|
||||||
|
options.min = 1;
|
||||||
|
check(0);
|
||||||
|
assert.true(isValid, 'Valid when integer is 0 and min is 1');
|
||||||
|
check('0');
|
||||||
|
assert.true(isValid, 'Valid when string is 0 and min is 1');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it should validate number', function (assert) {
|
test('it should validate number', function (assert) {
|
||||||
let isValid;
|
let isValid;
|
||||||
const options = { nullable: true, asString: false };
|
const options = { nullable: true };
|
||||||
const check = (prop) => (isValid = validators.number(prop, options));
|
const check = (prop) => (isValid = validators.number(prop, options));
|
||||||
check(null);
|
check(null);
|
||||||
assert.true(isValid, 'Valid when nullable is true');
|
assert.true(isValid, 'Valid when nullable is true');
|
||||||
options.nullable = false;
|
options.nullable = false;
|
||||||
check(null);
|
check(null);
|
||||||
assert.false(isValid, 'Invalid when nullable is false');
|
assert.false(isValid, 'Invalid when nullable is false');
|
||||||
check('9');
|
|
||||||
assert.false(isValid, 'Invalid for string when asString is false');
|
|
||||||
check(9);
|
check(9);
|
||||||
assert.true(isValid, 'Valid for number');
|
assert.true(isValid, 'Valid for number');
|
||||||
options.asString = true;
|
|
||||||
check('9');
|
check('9');
|
||||||
assert.true(isValid, 'Valid for number as string');
|
assert.true(isValid, 'Valid for number as string');
|
||||||
check('foo');
|
check('foo');
|
||||||
assert.false(isValid, 'Invalid for string that is not a number');
|
assert.false(isValid, 'Invalid for string that is not a number');
|
||||||
|
check('12foo');
|
||||||
|
assert.false(isValid, 'Invalid for string that contains a number');
|
||||||
|
check(0);
|
||||||
|
assert.true(isValid, 'Valid for 0 as an integer');
|
||||||
|
check('0');
|
||||||
|
assert.true(isValid, 'Valid for 0 as a string');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user