Adds aria-describedby, rule bailing
This commit is contained in:
parent
af5e23098d
commit
fbc54bd72b
6
dist/formulate.esm.js
vendored
6
dist/formulate.esm.js
vendored
File diff suppressed because one or more lines are too long
10
dist/formulate.min.js
vendored
10
dist/formulate.min.js
vendored
File diff suppressed because one or more lines are too long
6
dist/formulate.umd.js
vendored
6
dist/formulate.umd.js
vendored
File diff suppressed because one or more lines are too long
6
dist/snow.min.css
vendored
6
dist/snow.min.css
vendored
File diff suppressed because one or more lines are too long
@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import context from './libs/context'
|
import context from './libs/context'
|
||||||
import { shallowEqualObjects, parseRules, snakeToCamel, has, arrayify } from './libs/utils'
|
import { shallowEqualObjects, parseRules, snakeToCamel, has, arrayify, groupBails } from './libs/utils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormulateInput',
|
name: 'FormulateInput',
|
||||||
@ -355,22 +355,43 @@ export default {
|
|||||||
let rules = parseRules(this.validation, this.$formulate.rules(this.parsedValidationRules))
|
let rules = parseRules(this.validation, this.$formulate.rules(this.parsedValidationRules))
|
||||||
// Add in ruleRegistry rules. These are added directly via injection from
|
// Add in ruleRegistry rules. These are added directly via injection from
|
||||||
// children and not part of the standard validation rule set.
|
// children and not part of the standard validation rule set.
|
||||||
rules = this.ruleRegistry.length ? rules.concat(this.ruleRegistry) : rules
|
rules = this.ruleRegistry.length ? this.ruleRegistry.concat(rules) : rules
|
||||||
this.pendingValidation = Promise.all(
|
this.pendingValidation = this.runRules(rules)
|
||||||
rules.map(([rule, args, ruleName]) => {
|
|
||||||
var res = rule({
|
|
||||||
value: this.context.model,
|
|
||||||
getFormValues: this.getFormValues.bind(this),
|
|
||||||
name: this.context.name
|
|
||||||
}, ...args)
|
|
||||||
res = (res instanceof Promise) ? res : Promise.resolve(res)
|
|
||||||
return res.then(result => result ? false : this.getMessage(ruleName, args))
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then(result => result.filter(result => result))
|
|
||||||
.then(messages => this.didValidate(messages))
|
.then(messages => this.didValidate(messages))
|
||||||
return this.pendingValidation
|
return this.pendingValidation
|
||||||
},
|
},
|
||||||
|
runRules (rules) {
|
||||||
|
const run = ([rule, args, ruleName, modifier]) => {
|
||||||
|
var res = rule({
|
||||||
|
value: this.context.model,
|
||||||
|
getFormValues: this.getFormValues.bind(this),
|
||||||
|
name: this.context.name
|
||||||
|
}, ...args)
|
||||||
|
res = (res instanceof Promise) ? res : Promise.resolve(res)
|
||||||
|
return res.then(result => result ? false : this.getMessage(ruleName, args))
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const resolveGroups = (groups, allMessages = []) => {
|
||||||
|
const ruleGroup = groups.shift()
|
||||||
|
if (Array.isArray(ruleGroup) && ruleGroup.length) {
|
||||||
|
Promise.all(ruleGroup.map(run))
|
||||||
|
.then(messages => messages.filter(m => !!m))
|
||||||
|
.then(messages => {
|
||||||
|
messages = Array.isArray(messages) ? messages : []
|
||||||
|
// The rule passed or its a non-bailing group, and there are additional groups to check, continue
|
||||||
|
if ((!messages.length || !ruleGroup.bail) && groups.length) {
|
||||||
|
return resolveGroups(groups, allMessages.concat(messages))
|
||||||
|
}
|
||||||
|
return resolve(allMessages.concat(messages))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
resolve([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolveGroups(groupBails(rules))
|
||||||
|
})
|
||||||
|
},
|
||||||
didValidate (messages) {
|
didValidate (messages) {
|
||||||
const validationChanged = !shallowEqualObjects(messages, this.validationErrors)
|
const validationChanged = !shallowEqualObjects(messages, this.validationErrors)
|
||||||
this.validationErrors = messages
|
this.validationErrors = messages
|
||||||
|
@ -119,6 +119,11 @@ function elementAttributes () {
|
|||||||
attrs.name = this.name
|
attrs.name = this.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is help text, have this element be described by it.
|
||||||
|
if (this.help) {
|
||||||
|
attrs['aria-describedby'] = `${attrs.id}-help`
|
||||||
|
}
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +147,9 @@ export default {
|
|||||||
*/
|
*/
|
||||||
matches: function ({ value }, ...stack) {
|
matches: function ({ value }, ...stack) {
|
||||||
return Promise.resolve(!!stack.find(pattern => {
|
return Promise.resolve(!!stack.find(pattern => {
|
||||||
|
if (typeof pattern === 'string' && pattern.substr(0, 1) === '/' && pattern.substr(-1) === '/') {
|
||||||
|
pattern = new RegExp(pattern.substr(1, pattern.length - 2))
|
||||||
|
}
|
||||||
if (pattern instanceof RegExp) {
|
if (pattern instanceof RegExp) {
|
||||||
return pattern.test(value)
|
return pattern.test(value)
|
||||||
}
|
}
|
||||||
@ -278,5 +281,12 @@ export default {
|
|||||||
*/
|
*/
|
||||||
url: function ({ value }) {
|
url: function ({ value }) {
|
||||||
return Promise.resolve(isUrl(value))
|
return Promise.resolve(isUrl(value))
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rule: not a true rule — more like a compiler flag.
|
||||||
|
*/
|
||||||
|
bail: function () {
|
||||||
|
return Promise.resolve(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,19 +106,19 @@ function parseRule (rule, rules) {
|
|||||||
}
|
}
|
||||||
if (Array.isArray(rule) && rule.length) {
|
if (Array.isArray(rule) && rule.length) {
|
||||||
rule = rule.map(r => r) // light clone
|
rule = rule.map(r => r) // light clone
|
||||||
const ruleName = snakeToCamel(rule.shift())
|
const [ruleName, modifier] = parseModifier(rule.shift())
|
||||||
if (typeof ruleName === 'string' && rules.hasOwnProperty(ruleName)) {
|
if (typeof ruleName === 'string' && rules.hasOwnProperty(ruleName)) {
|
||||||
return [rules[ruleName], rule, ruleName]
|
return [rules[ruleName], rule, ruleName, modifier]
|
||||||
}
|
}
|
||||||
if (typeof ruleName === 'function') {
|
if (typeof ruleName === 'function') {
|
||||||
return [ruleName, rule, ruleName]
|
return [ruleName, rule, ruleName, modifier]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof rule === 'string') {
|
if (typeof rule === 'string') {
|
||||||
const segments = rule.split(':')
|
const segments = rule.split(':')
|
||||||
const ruleName = snakeToCamel(segments.shift())
|
const [ruleName, modifier] = parseModifier(segments.shift())
|
||||||
if (rules.hasOwnProperty(ruleName)) {
|
if (rules.hasOwnProperty(ruleName)) {
|
||||||
return [rules[ruleName], segments.length ? segments.join(':').split(',') : [], ruleName]
|
return [rules[ruleName], segments.length ? segments.join(':').split(',') : [], ruleName, modifier]
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unknown validation rule ${rule}`)
|
throw new Error(`Unknown validation rule ${rule}`)
|
||||||
}
|
}
|
||||||
@ -126,6 +126,68 @@ function parseRule (rule, rules) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the rule name with the applicable modifier as an array.
|
||||||
|
* @param {string} ruleName
|
||||||
|
* @return {array} [ruleName, modifier]
|
||||||
|
*/
|
||||||
|
function parseModifier (ruleName) {
|
||||||
|
if (/^[\^]/.test(ruleName.charAt(0))) {
|
||||||
|
return [snakeToCamel(ruleName.substr(1)), ruleName.charAt(0)]
|
||||||
|
}
|
||||||
|
return [snakeToCamel(ruleName), null]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of rules, group them by bail signals. For example for this:
|
||||||
|
* bail|required|min:10|max:20
|
||||||
|
* we would expect:
|
||||||
|
* [[required], [min], [max]]
|
||||||
|
* because any sub-array failure would cause a shutdown. While
|
||||||
|
* ^required|min:10|max:10
|
||||||
|
* would return:
|
||||||
|
* [[required], [min, max]]
|
||||||
|
* and no bailing would produce:
|
||||||
|
* [[required, min, max]]
|
||||||
|
* @param {array} rules
|
||||||
|
*/
|
||||||
|
export function groupBails (rules) {
|
||||||
|
const groups = []
|
||||||
|
const bailIndex = rules.findIndex(([,, rule]) => rule.toLowerCase() === 'bail')
|
||||||
|
if (bailIndex >= 0) {
|
||||||
|
// Get all the rules until the first bail rule (dont include the bail)
|
||||||
|
const preBail = rules.splice(0, bailIndex + 1).slice(0, -1)
|
||||||
|
// Rules before the `bail` rule are non-bailing
|
||||||
|
preBail.length && groups.push(preBail)
|
||||||
|
// All remaining rules are bailing rule groups
|
||||||
|
rules.map(rule => groups.push(Object.defineProperty([rule], 'bail', { value: true })))
|
||||||
|
} else {
|
||||||
|
groups.push(rules)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups.reduce((groups, group) => {
|
||||||
|
const splitByMod = (group, bailGroup = false) => {
|
||||||
|
if (group.length < 2) {
|
||||||
|
return Object.defineProperty([group], 'bail', { value: bailGroup })
|
||||||
|
}
|
||||||
|
const splits = []
|
||||||
|
const modIndex = group.findIndex(([,,, modifier]) => modifier === '^')
|
||||||
|
if (modIndex >= 0) {
|
||||||
|
const preMod = group.splice(0, modIndex)
|
||||||
|
// rules before the modifier are non-bailing rules.
|
||||||
|
preMod.length && splits.push(...splitByMod(preMod, bailGroup))
|
||||||
|
splits.push(Object.defineProperty([group.shift()], 'bail', { value: true }))
|
||||||
|
// rules after the modifier are non-bailing rules.
|
||||||
|
group.length && splits.push(...splitByMod(group, bailGroup))
|
||||||
|
} else {
|
||||||
|
splits.push(group)
|
||||||
|
}
|
||||||
|
return splits
|
||||||
|
}
|
||||||
|
return groups.concat(splitByMod(group))
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escape a string for use in regular expressions.
|
* Escape a string for use in regular expressions.
|
||||||
* @param {string} string
|
* @param {string} string
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="context.help"
|
v-if="context.help"
|
||||||
|
:id="`${context.id}-help`"
|
||||||
:class="`formulate-input-help formulate-input-help--${context.helpPosition}`"
|
:class="`formulate-input-help formulate-input-help--${context.helpPosition}`"
|
||||||
v-text="context.help"
|
v-text="context.help"
|
||||||
/>
|
/>
|
||||||
|
@ -220,5 +220,76 @@ describe('FormulateInput', () => {
|
|||||||
await flushPromises()
|
await flushPromises()
|
||||||
expect(wrapper.find('.my-errors').html())
|
expect(wrapper.find('.my-errors').html())
|
||||||
.toBe(`<ul class="my-errors">\n <li>Text is required.</li>\n</ul>`)
|
.toBe(`<ul class="my-errors">\n <li>Text is required.</li>\n</ul>`)
|
||||||
|
// Clean up after this call — we should probably get rid of the singleton all together....
|
||||||
|
Formulate.extend({ slotComponents: { errors: 'FormulateErrors' }})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('links help text with `aria-describedby`', async () => {
|
||||||
|
const wrapper = mount(FormulateInput, {
|
||||||
|
propsData: {
|
||||||
|
type: 'text',
|
||||||
|
validation: 'required',
|
||||||
|
errorBehavior: 'live',
|
||||||
|
value: 'bar',
|
||||||
|
help: 'Some help text'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
const id = `${wrapper.vm.context.id}-help`
|
||||||
|
expect(wrapper.find('input').attributes('aria-describedby')).toBe(id)
|
||||||
|
expect(wrapper.find('.formulate-input-help').attributes().id).toBe(id)
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it does not use aria-describedby if there is no help text', async () => {
|
||||||
|
const wrapper = mount(FormulateInput, {
|
||||||
|
propsData: {
|
||||||
|
type: 'text',
|
||||||
|
validation: 'required',
|
||||||
|
errorBehavior: 'live',
|
||||||
|
value: 'bar',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await flushPromises()
|
||||||
|
expect(wrapper.find('input').attributes('aria-describedby')).toBeFalsy()
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can bail on validation when encountering the bail rule', async () => {
|
||||||
|
const wrapper = mount(FormulateInput, {
|
||||||
|
propsData: { type: 'text', validation: 'bail|required|in:xyz', errorBehavior: 'live' }
|
||||||
|
})
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.vm.context.visibleValidationErrors.length).toBe(1);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can show multiple validation errors if they occur before the bail rule', async () => {
|
||||||
|
const wrapper = mount(FormulateInput, {
|
||||||
|
propsData: { type: 'text', validation: 'required|in:xyz|bail', errorBehavior: 'live' }
|
||||||
|
})
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can avoid bail behavior by using modifier', async () => {
|
||||||
|
const wrapper = mount(FormulateInput, {
|
||||||
|
propsData: { type: 'text', validation: '^required|in:xyz|min:10,length', errorBehavior: 'live', value: '123' }
|
||||||
|
})
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('prevents later error messages when modified rule fails', async () => {
|
||||||
|
const wrapper = mount(FormulateInput, {
|
||||||
|
propsData: { type: 'text', validation: '^required|in:xyz|min:10,length', errorBehavior: 'live' }
|
||||||
|
})
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.vm.context.visibleValidationErrors.length).toBe(1);
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can bail in the middle of the rule set with a modifier', async () => {
|
||||||
|
const wrapper = mount(FormulateInput, {
|
||||||
|
propsData: { type: 'text', validation: 'required|^in:xyz|min:10,length', errorBehavior: 'live' }
|
||||||
|
})
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.vm.context.visibleValidationErrors.length).toBe(2);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -306,6 +306,18 @@ describe('matches', () => {
|
|||||||
it('passes on matching mixed regex and string', async () => {
|
it('passes on matching mixed regex and string', async () => {
|
||||||
expect(await rules.matches({ value: 'first-fourth' }, 'second', /^third/, /fourth$/)).toBe(true)
|
expect(await rules.matches({ value: 'first-fourth' }, 'second', /^third/, /fourth$/)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('fails on a regular expression encoded as a string', async () => {
|
||||||
|
expect(await rules.matches({ value: 'mypassword' }, '/[0-9]/')).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes on a regular expression encoded as a string', async () => {
|
||||||
|
expect(await rules.matches({ value: 'mypa55word' }, '/[0-9]/')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes on a regular expression containing slashes', async () => {
|
||||||
|
expect(await rules.matches({ value: 'https://' }, '/https?:///')).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
import { parseRules, parseLocale, regexForFormat, cloneDeep, isValueType, snakeToCamel } from '@/libs/utils'
|
import { parseRules, parseLocale, regexForFormat, cloneDeep, isValueType, snakeToCamel, groupBails } from '@/libs/utils'
|
||||||
import rules from '@/libs/rules'
|
import rules from '@/libs/rules'
|
||||||
import FileUpload from '@/FileUpload';
|
import FileUpload from '@/FileUpload';
|
||||||
|
|
||||||
describe('parseRules', () => {
|
describe('parseRules', () => {
|
||||||
it('parses single string rules, returning empty arguments array', () => {
|
it('parses single string rules, returning empty arguments array', () => {
|
||||||
expect(parseRules('required', rules)).toEqual([
|
expect(parseRules('required', rules)).toEqual([
|
||||||
[rules.required, [], 'required']
|
[rules.required, [], 'required', null]
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws errors for invalid validation rules', () => {
|
it('throws errors for invalid validation rules', () => {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
parseRules('required|notarule', rules)
|
parseRules('required|notarule', rules, null)
|
||||||
}).toThrow()
|
}).toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('parses arguments for a rule', () => {
|
it('parses arguments for a rule', () => {
|
||||||
expect(parseRules('in:foo,bar', rules)).toEqual([
|
expect(parseRules('in:foo,bar', rules)).toEqual([
|
||||||
[rules.in, ['foo', 'bar'], 'in']
|
[rules.in, ['foo', 'bar'], 'in', null]
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('parses multiple string rules and arguments', () => {
|
it('parses multiple string rules and arguments', () => {
|
||||||
expect(parseRules('required|in:foo,bar', rules)).toEqual([
|
expect(parseRules('required|in:foo,bar', rules)).toEqual([
|
||||||
[rules.required, [], 'required'],
|
[rules.required, [], 'required', null],
|
||||||
[rules.in, ['foo', 'bar'], 'in']
|
[rules.in, ['foo', 'bar'], 'in', null]
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('parses multiple array rules and arguments', () => {
|
it('parses multiple array rules and arguments', () => {
|
||||||
expect(parseRules(['required', 'in:foo,bar'], rules)).toEqual([
|
expect(parseRules(['required', 'in:foo,bar'], rules)).toEqual([
|
||||||
[rules.required, [], 'required'],
|
[rules.required, [], 'required', null],
|
||||||
[rules.in, ['foo', 'bar'], 'in']
|
[rules.in, ['foo', 'bar'], 'in', null]
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -39,7 +39,21 @@ describe('parseRules', () => {
|
|||||||
expect(parseRules([
|
expect(parseRules([
|
||||||
['matches', /^abc/, '1234']
|
['matches', /^abc/, '1234']
|
||||||
], rules)).toEqual([
|
], rules)).toEqual([
|
||||||
[rules.matches, [/^abc/, '1234'], 'matches']
|
[rules.matches, [/^abc/, '1234'], 'matches', null]
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('parses string rules with caret modifier', () => {
|
||||||
|
expect(parseRules('^required|min:10', rules)).toEqual([
|
||||||
|
[rules.required, [], 'required', '^'],
|
||||||
|
[rules.min, ['10'], 'min', null],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('parses array rule with caret modifier', () => {
|
||||||
|
expect(parseRules([['required'], ['^max', '10']], rules)).toEqual([
|
||||||
|
[rules.required, [], 'required', null],
|
||||||
|
[rules.max, ['10'], 'max', '^'],
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -172,3 +186,72 @@ describe('parseLocale', () => {
|
|||||||
expect(parseLocale('en')).toEqual(['en'])
|
expect(parseLocale('en')).toEqual(['en'])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('groupBails', () => {
|
||||||
|
it('wraps non bailed rules in an array', () => {
|
||||||
|
const bailGroups = groupBails([[,,'required'], [,,'min']])
|
||||||
|
expect(bailGroups).toEqual(
|
||||||
|
[ [[,,'required'], [,,'min']] ] // dont bail on either of these
|
||||||
|
)
|
||||||
|
expect(bailGroups.map(group => !!group.bail)).toEqual([false])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('splits bailed rules into two arrays array', () => {
|
||||||
|
const bailGroups = groupBails([[,,'required'], [,,'max'], [,, 'bail'], [,, 'matches'], [,,'min']])
|
||||||
|
expect(bailGroups).toEqual([
|
||||||
|
[ [,,'required'], [,,'max'] ], // dont bail on these
|
||||||
|
[ [,, 'matches'] ], // bail on this one
|
||||||
|
[ [,,'min'] ] // bail on this one
|
||||||
|
])
|
||||||
|
expect(bailGroups.map(group => !!group.bail)).toEqual([false, true, true])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('splits entire rule set when bail is at the beginning', () => {
|
||||||
|
const bailGroups = groupBails([[,, 'bail'], [,,'required'], [,,'max'], [,, 'matches'], [,,'min']])
|
||||||
|
expect(bailGroups).toEqual([
|
||||||
|
[ [,, 'required'] ], // bail on this one
|
||||||
|
[ [,, 'max'] ], // bail on this one
|
||||||
|
[ [,, 'matches'] ], // bail on this one
|
||||||
|
[ [,, 'min'] ] // bail on this one
|
||||||
|
])
|
||||||
|
expect(bailGroups.map(group => !!group.bail)).toEqual([true, true, true, true])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('splits no rules when bail is at the end', () => {
|
||||||
|
const bailGroups = groupBails([[,,'required'], [,,'max'], [,, 'matches'], [,,'min'], [,, 'bail']])
|
||||||
|
expect(bailGroups).toEqual([
|
||||||
|
[ [,, 'required'], [,, 'max'], [,, 'matches'], [,, 'min'] ] // dont bail on these
|
||||||
|
])
|
||||||
|
expect(bailGroups.map(group => !!group.bail)).toEqual([false])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('splits individual modified names into two groups when at the begining', () => {
|
||||||
|
const bailGroups = groupBails([[,,'required', '^'], [,,'max'], [,, 'matches'], [,,'min'] ])
|
||||||
|
expect(bailGroups).toEqual([
|
||||||
|
[ [,, 'required', '^'] ], // bail on this one
|
||||||
|
[ [,, 'max'], [,, 'matches'], [,, 'min'] ] // dont bail on these
|
||||||
|
])
|
||||||
|
expect(bailGroups.map(group => !!group.bail)).toEqual([true, false])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('splits individual modified names into three groups when in the middle', () => {
|
||||||
|
const bailGroups = groupBails([[,,'required'], [,,'max'], [,, 'matches', '^'], [,,'min'] ])
|
||||||
|
expect(bailGroups).toEqual([
|
||||||
|
[ [,, 'required'], [,, 'max'] ], // dont bail on these
|
||||||
|
[ [,, 'matches', '^'] ], // bail on this one
|
||||||
|
[ [,, 'min'] ] // dont bail on this
|
||||||
|
])
|
||||||
|
expect(bailGroups.map(group => !!group.bail)).toEqual([false, true, false])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('splits individual modified names into four groups when used twice', () => {
|
||||||
|
const bailGroups = groupBails([[,,'required', '^'], [,,'max'], [,, 'matches', '^'], [,,'min'] ])
|
||||||
|
expect(bailGroups).toEqual([
|
||||||
|
[ [,, 'required', '^'] ], // bail on this
|
||||||
|
[ [,, 'max'] ], // dont bail on this
|
||||||
|
[ [,, 'matches', '^'] ], // bail on this
|
||||||
|
[ [,, 'min'] ] // dont bail on this
|
||||||
|
])
|
||||||
|
expect(bailGroups.map(group => !!group.bail)).toEqual([true, false, true, false])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user