1
0
mirror of synced 2025-02-12 11:09:25 +03:00

Updates vue-test-utils to 1.0.2

This commit is contained in:
Justin Schroeder 2020-05-14 16:08:54 -04:00
commit fdcf7b3dbf
21 changed files with 1313 additions and 838 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11
dist/snow.css vendored
View File

@ -121,6 +121,7 @@
width: 1em; width: 1em;
height: 1em; height: 1em;
border-radius: 1em; border-radius: 1em;
border: 0;
background-color: #41b883; background-color: #41b883;
margin-top: calc(-.5em + 2px); } margin-top: calc(-.5em + 2px); }
.formulate-input[data-classification='slider'] input::-moz-range-thumb { .formulate-input[data-classification='slider'] input::-moz-range-thumb {
@ -129,6 +130,7 @@
width: 1em; width: 1em;
height: 1em; height: 1em;
border-radius: 1em; border-radius: 1em;
border: 0;
background-color: #41b883; background-color: #41b883;
margin-top: calc(-.5em + 2px); } margin-top: calc(-.5em + 2px); }
.formulate-input[data-classification='slider'] input::-ms-thumb { .formulate-input[data-classification='slider'] input::-ms-thumb {
@ -137,6 +139,7 @@
width: 1em; width: 1em;
height: 1em; height: 1em;
border-radius: 1em; border-radius: 1em;
border: 0;
background-color: #41b883; background-color: #41b883;
margin-top: calc(-.5em + 2px); } margin-top: calc(-.5em + 2px); }
.formulate-input[data-classification='slider'] input::-webkit-slider-runnable-track { .formulate-input[data-classification='slider'] input::-webkit-slider-runnable-track {
@ -147,6 +150,14 @@
border-radius: 3px; border-radius: 3px;
margin: 0; margin: 0;
padding: 0; } padding: 0; }
.formulate-input[data-classification='slider'] input::-moz-range-track {
appearance: none;
width: 100%;
height: 4px;
background-color: #efefef;
border-radius: 3px;
margin: 0;
padding: 0; }
.formulate-input[data-classification='textarea'] textarea { .formulate-input[data-classification='textarea'] textarea {
appearance: none; appearance: none;
border-radius: .3em; border-radius: .3em;

4
dist/snow.min.css vendored

File diff suppressed because one or more lines are too long

1806
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@braid/vue-formulate", "name": "@braid/vue-formulate",
"version": "2.2.8", "version": "2.2.12",
"description": "The easiest way to build forms in Vue.", "description": "The easiest way to build forms in Vue.",
"main": "dist/formulate.umd.js", "main": "dist/formulate.umd.js",
"module": "dist/formulate.esm.js", "module": "dist/formulate.esm.js",
@ -8,9 +8,6 @@
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
"engines": {
"node": ">=11"
},
"browser": { "browser": {
"./sfc": "src/Formulate.js" "./sfc": "src/Formulate.js"
}, },
@ -45,9 +42,9 @@
}, },
"homepage": "https://www.vueformulate.com", "homepage": "https://www.vueformulate.com",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.9.0", "@babel/core": "^7.9.6",
"@babel/plugin-transform-modules-commonjs": "^7.9.0", "@babel/plugin-transform-modules-commonjs": "^7.9.6",
"@babel/preset-env": "^7.9.5", "@babel/preset-env": "^7.9.6",
"@rollup/plugin-buble": "^0.21.3", "@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-commonjs": "^11.1.0", "@rollup/plugin-commonjs": "^11.1.0",
"@rollup/plugin-node-resolve": "^7.1.3", "@rollup/plugin-node-resolve": "^7.1.3",
@ -55,11 +52,11 @@
"@vue/cli-plugin-eslint": "^4.3.1", "@vue/cli-plugin-eslint": "^4.3.1",
"@vue/cli-service": "^4.3.1", "@vue/cli-service": "^4.3.1",
"@vue/component-compiler-utils": "^3.1.2", "@vue/component-compiler-utils": "^3.1.2",
"@vue/test-utils": "^1.0.0-beta.33", "@vue/test-utils": "^1.0.2",
"autoprefixer": "^9.7.6", "autoprefixer": "^9.7.6",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-jest": "^25.3.0", "babel-jest": "^25.5.1",
"cssnano": "^4.1.10", "cssnano": "^4.1.10",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-config-standard": "^12.0.0", "eslint-config-standard": "^12.0.0",
@ -69,19 +66,19 @@
"eslint-plugin-standard": "^4.0.0", "eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^5.2.3", "eslint-plugin-vue": "^5.2.3",
"flush-promises": "^1.0.2", "flush-promises": "^1.0.2",
"jest": "^25.3.0", "jest": "^25.5.4",
"jest-vue-preprocessor": "^1.7.1", "jest-vue-preprocessor": "^1.7.1",
"node-sass": "^4.13.1", "node-sass": "^4.14.1",
"postcss": "^7.0.27", "postcss": "^7.0.30",
"postcss-cli": "^7.1.0", "postcss-cli": "^7.1.1",
"rollup": "^1.32.1", "rollup": "^1.32.1",
"rollup-plugin-auto-external": "^2.0.0", "rollup-plugin-auto-external": "^2.0.0",
"rollup-plugin-internal": "^1.0.4", "rollup-plugin-internal": "^1.0.4",
"rollup-plugin-multi-input": "^1.1.1", "rollup-plugin-multi-input": "^1.1.1",
"rollup-plugin-terser": "^5.3.0", "rollup-plugin-terser": "^5.3.0",
"rollup-plugin-vue": "^5.1.6", "rollup-plugin-vue": "^5.1.7",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
"typescript": "^3.8.3", "typescript": "^3.9.2",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-jest": "^3.0.5", "vue-jest": "^3.0.5",
"vue-runtime-helpers": "^1.1.2", "vue-runtime-helpers": "^1.1.2",

View File

@ -72,9 +72,11 @@ class Formulate {
uploadJustCompleteDuration: 1000, uploadJustCompleteDuration: 1000,
errorHandler: (err) => err, errorHandler: (err) => err,
plugins: [ en ], plugins: [ en ],
locales: {} locales: {},
idPrefix: 'formulate-'
} }
this.registry = new Map() this.registry = new Map()
this.idRegistry = {}
} }
/** /**
@ -94,6 +96,21 @@ class Formulate {
} }
} }
/**
* Produce a deterministically generated id based on the sequence by which it
* was requested. This should be *theoretically* the same SSR as client side.
* However, SSR and deterministic ids can be very challenging, so this
* implementation is open to community review.
*/
nextId (vm) {
const path = vm.$route && vm.$route.path || false
const pathPrefix = path ? vm.$route.path.replace(/[\/\\.\s]/g, '-') : 'global';
if (!Object.prototype.hasOwnProperty.call(this.idRegistry, pathPrefix)) {
this.idRegistry[pathPrefix] = 0;
}
return `${this.options.idPrefix}${pathPrefix}-${++this.idRegistry[pathPrefix]}`
}
/** /**
* Given a set of options, apply them to the pre-existing options. * Given a set of options, apply them to the pre-existing options.
* @param {Object} extendWith * @param {Object} extendWith

View File

@ -80,8 +80,7 @@
<script> <script>
import context from './libs/context' import context from './libs/context'
import { shallowEqualObjects, parseRules, snakeToCamel, arrayify, has } from './libs/utils' import { shallowEqualObjects, parseRules, snakeToCamel, has, arrayify } from './libs/utils'
import nanoid from 'nanoid/non-secure'
export default { export default {
name: 'FormulateInput', name: 'FormulateInput',
@ -238,7 +237,7 @@ export default {
}, },
data () { data () {
return { return {
defaultId: nanoid(9), defaultId: this.$formulate.nextId(this),
localAttributes: {}, localAttributes: {},
localErrors: [], localErrors: [],
internalModelProxy: this.getInitialValue(), internalModelProxy: this.getInitialValue(),
@ -314,7 +313,7 @@ export default {
this.updateLocalAttributes(this.$attrs) this.updateLocalAttributes(this.$attrs)
this.performValidation() this.performValidation()
}, },
destroyed () { beforeDestroy () {
if (!this.disableErrors && typeof this.removeErrorObserver === 'function') { if (!this.disableErrors && typeof this.removeErrorObserver === 'function') {
this.removeErrorObserver(this.setErrors) this.removeErrorObserver(this.setErrors)
} }

View File

@ -60,21 +60,21 @@ export default {
optionsWithContext () { optionsWithContext () {
const { const {
// The following are a list of items to pull out of the context object // The following are a list of items to pull out of the context object
options,
labelPosition,
attributes: { id, ...groupApplicableAttributes }, attributes: { id, ...groupApplicableAttributes },
classification,
blurHandler, blurHandler,
performValidation, classification,
hasValidationErrors,
getValidationErrors,
validationErrors,
setErrors,
visibleValidationErrors,
component, component,
getValidationErrors,
hasLabel, hasLabel,
slotComponents, hasValidationErrors,
isSubField, isSubField,
labelPosition,
options,
performValidation,
setErrors,
slotComponents,
validationErrors,
visibleValidationErrors,
...context ...context
} = this.context } = this.context
return this.options.map(option => this.groupItemContext( return this.options.map(option => this.groupItemContext(
@ -100,7 +100,9 @@ export default {
}, },
groupItemContext (context, option, groupAttributes) { groupItemContext (context, option, groupAttributes) {
const optionAttributes = {} const optionAttributes = {}
const ctx = Object.assign({}, context, option, groupAttributes, optionAttributes) const ctx = Object.assign({}, context, option, groupAttributes, optionAttributes, !context.hasGivenName ? {
name: true
} : {})
return ctx return ctx
} }
} }

View File

@ -17,6 +17,7 @@ export default {
errors: this.explicitErrors, errors: this.explicitErrors,
formShouldShowErrors: this.formShouldShowErrors, formShouldShowErrors: this.formShouldShowErrors,
getValidationErrors: this.getValidationErrors.bind(this), getValidationErrors: this.getValidationErrors.bind(this),
hasGivenName: this.hasGivenName,
hasLabel: (this.label && this.classification !== 'button'), hasLabel: (this.label && this.classification !== 'button'),
hasValidationErrors: this.hasValidationErrors.bind(this), hasValidationErrors: this.hasValidationErrors.bind(this),
help: this.help, help: this.help,
@ -46,6 +47,7 @@ export default {
}, },
// Used in sub-context // Used in sub-context
nameOrFallback, nameOrFallback,
hasGivenName,
typeContext, typeContext,
elementAttributes, elementAttributes,
logicalLabelPosition, logicalLabelPosition,
@ -106,11 +108,17 @@ function typeContext () {
*/ */
function elementAttributes () { function elementAttributes () {
const attrs = Object.assign({}, this.localAttributes) const attrs = Object.assign({}, this.localAttributes)
// pass the ID prop through to the root element
if (this.id) { if (this.id) {
attrs.id = this.id attrs.id = this.id
} else { } else {
attrs.id = this.defaultId attrs.id = this.defaultId
} }
// pass an explicitly given name prop through to the root element
if (this.hasGivenName) {
attrs.name = this.name
}
return attrs return attrs
} }
@ -204,6 +212,13 @@ function nameOrFallback () {
return this.name return this.name
} }
/**
* determine if an input has a user-defined name
*/
function hasGivenName () {
return typeof this.name !== 'boolean'
}
/** /**
* Determines if this formulate element is v-modeled or not. * Determines if this formulate element is v-modeled or not.
*/ */

View File

@ -28,13 +28,11 @@ describe('FormulateForm', () => {
const wrapper = mount(FormulateForm, { const wrapper = mount(FormulateForm, {
slots: { slots: {
default: "<button type='submit' />" default: "<button type='submit' />"
},
methods: {
formSubmitted
} }
}) })
const spy = jest.spyOn(wrapper.vm, 'formSubmitted')
wrapper.find('form').trigger('submit') wrapper.find('form').trigger('submit')
expect(formSubmitted).toBeCalled() expect(spy).toHaveBeenCalled()
}) })
it('registers its subcomponents', () => { it('registers its subcomponents', () => {
@ -45,6 +43,27 @@ describe('FormulateForm', () => {
expect(wrapper.vm.registry.keys()).toEqual(['subinput1', 'subinput2']) expect(wrapper.vm.registry.keys()).toEqual(['subinput1', 'subinput2'])
}) })
it('deregisters a subcomponents', async () => {
const wrapper = mount({
data () {
return {
active: true
}
},
template: `
<FormulateForm>
<FormulateInput v-if="active" type="text" name="subinput1" />
<FormulateInput type="checkbox" name="subinput2" />
</FormulateForm>
`
})
await flushPromises()
expect(wrapper.findComponent(FormulateForm).vm.registry.keys()).toEqual(['subinput1', 'subinput2'])
wrapper.setData({ active: false })
await flushPromises()
expect(wrapper.findComponent(FormulateForm).vm.registry.keys()).toEqual(['subinput2'])
})
it('can set a fields initial value', async () => { it('can set a fields initial value', async () => {
const wrapper = mount(FormulateForm, { const wrapper = mount(FormulateForm, {
propsData: { formulateValue: { testinput: 'has initial value' } }, propsData: { formulateValue: { testinput: 'has initial value' } },
@ -81,7 +100,7 @@ describe('FormulateForm', () => {
propsData: { formulateValue: { box1: true } }, propsData: { formulateValue: { box1: true } },
slots: { default: '<FormulateInput type="checkbox" name="box1" />' } slots: { default: '<FormulateInput type="checkbox" name="box1" />' }
}) })
expect(wrapper.find('input[type="checkbox"]').is(':checked')).toBe(true) expect(wrapper.find('input[type="checkbox"]').element.checked).toBeTruthy()
}); });
it('can set initial unchecked attribute on single checkboxes', () => { it('can set initial unchecked attribute on single checkboxes', () => {
@ -89,7 +108,7 @@ describe('FormulateForm', () => {
propsData: { formulateValue: { box1: false } }, propsData: { formulateValue: { box1: false } },
slots: { default: '<FormulateInput type="checkbox" name="box1" />' } slots: { default: '<FormulateInput type="checkbox" name="box1" />' }
}) })
expect(wrapper.find('input[type="checkbox"]').is(':checked')).toBe(false) expect(wrapper.find('input[type="checkbox"]').element.checked).toBeFalsy()
}); });
it('can set checkbox initial value with options', async () => { it('can set checkbox initial value with options', async () => {
@ -326,7 +345,7 @@ describe('FormulateForm', () => {
await flushPromises() await flushPromises()
expect(wrapper.findAll('.formulate-form-errors').length).toBe(1) expect(wrapper.findAll('.formulate-form-errors').length).toBe(1)
// Ensure that we moved the position of the errors // Ensure that we moved the position of the errors
expect(wrapper.find('h1 + *').is('.formulate-form-errors')).toBe(true) expect(wrapper.find('h1 + *').element.classList.contains('formulate-form-errors')).toBe(true)
}) })
it('allows rendering multiple locations', async () => { it('allows rendering multiple locations', async () => {
@ -443,10 +462,10 @@ describe('FormulateForm', () => {
` `
}) })
await flushPromises() await flushPromises()
expect(wrapper.find(FormulateForm).vm.errorObservers.length).toBe(1) expect(wrapper.findComponent(FormulateForm).vm.errorObservers.length).toBe(1)
wrapper.setData({ hasField: false }) wrapper.setData({ hasField: false })
await flushPromises() await flushPromises()
expect(wrapper.find(FormulateForm).vm.errorObservers.length).toBe(0) expect(wrapper.findComponent(FormulateForm).vm.errorObservers.length).toBe(0)
}) })
it('emits correct validation event on entry', async () => { it('emits correct validation event on entry', async () => {
@ -494,4 +513,26 @@ describe('FormulateForm', () => {
hasErrors: false hasErrors: false
}) })
}) })
// it('removes field data when that field is de-registered', async () => {
// const wrapper = mount({
// template: `
// <FormulateForm
// v-model="formData"
// >
// <FormulateInput type="text" name="foo" value="abc123" />
// <FormulateInput type="checkbox" name="bar" v-if="formData.foo !== 'bar'" :value="true" />
// </FormulateForm>
// `,
// data () {
// return {
// formData: {}
// }
// }
// })
// await flushPromises()
// wrapper.find('input[type="text"]').setValue('bar')
// await flushPromises()
// expect(wrapper.vm.formData).toEqual({ bar: true })
// })
}) })

View File

@ -113,7 +113,7 @@ describe('FormulateInput', () => {
value: 'bar' value: 'bar'
} }) } })
await flushPromises() await flushPromises()
expect(wrapper.contains(FormulateInputBox)).toBe(true) expect(wrapper.findComponent(FormulateInputBox).exists()).toBe(true)
}) })
it('emits correct validation event', async () => { it('emits correct validation event', async () => {

View File

@ -11,12 +11,22 @@ Vue.use(Formulate)
describe('FormulateInputBox', () => { describe('FormulateInputBox', () => {
it('renders a box element when type "checkbox" ', () => { it('renders a box element when type "checkbox" ', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox' } })
expect(wrapper.contains(FormulateInputBox)).toBe(true) expect(wrapper.findComponent(FormulateInputBox).exists()).toBe(true)
}) })
it('renders a box element when type "radio"', () => { it('renders a box element when type "radio"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'radio' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'radio' } })
expect(wrapper.contains(FormulateInputBox)).toBe(true) expect(wrapper.findComponent(FormulateInputBox).exists()).toBe(true)
})
it('passes an explicitly given name prop through to the root radio elements', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'radio', name: 'foo', options: {a: '1', b: '2'} } })
expect(wrapper.findAll('input[name="foo"]')).toHaveLength(2)
})
it('passes an explicitly given name prop through to the root checkbox elements', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', name: 'foo', options: {a: '1', b: '2'} } })
expect(wrapper.findAll('input[name="foo"]')).toHaveLength(2)
}) })
it('box inputs properly process options object in context library', () => { it('box inputs properly process options object in context library', () => {
@ -26,12 +36,12 @@ describe('FormulateInputBox', () => {
it('renders a group when type "checkbox" with options', () => { it('renders a group when type "checkbox" with options', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: {a: '1', b: '2'} } }) const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: {a: '1', b: '2'} } })
expect(wrapper.contains(FormulateInputGroup)).toBe(true) expect(wrapper.findComponent(FormulateInputGroup).exists()).toBe(true)
}) })
it('renders a group when type "radio" with options', () => { it('renders a group when type "radio" with options', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'} } }) const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'} } })
expect(wrapper.contains(FormulateInputGroup)).toBe(true) expect(wrapper.findComponent(FormulateInputGroup).exists()).toBe(true)
}) })
it('defaults labelPosition to "after" when type "checkbox"', () => { it('defaults labelPosition to "after" when type "checkbox"', () => {
@ -52,7 +62,7 @@ describe('FormulateInputBox', () => {
it('generates ids if not provided when type "radio"', () => { it('generates ids if not provided when type "radio"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'} } }) const wrapper = mount(FormulateInput, { propsData: { type: 'radio', options: {a: '1', b: '2'} } })
expect(wrapper.findAll('input[type="radio"]').is('[id]')).toBe(true) expect(wrapper.find('input[type="radio"]').attributes().id).toBeTruthy()
}) })
it('additional context does not bleed through to attributes with type "radio" and options', () => { it('additional context does not bleed through to attributes with type "radio" and options', () => {
@ -72,14 +82,14 @@ describe('FormulateInputBox', () => {
it('does not use the value attribute to be checked', () => { it('does not use the value attribute to be checked', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', value: '123' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', value: '123' } })
expect(wrapper.find('input').is(':checked')).toBe(false) expect(wrapper.find('input').element.checked).toBe(false)
}) })
it('uses the checked attribute to be checked', async () => { it('uses the checked attribute to be checked', async () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', checked: 'true' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', checked: 'true' } })
await flushPromises() await flushPromises()
await wrapper.vm.$nextTick() await wrapper.vm.$nextTick()
expect(wrapper.find('input').is(':checked')).toBe(true) expect(wrapper.find('input').element.checked).toBe(true)
}) })
it('uses the value attribute to select "type" radio when using options', async () => { it('uses the value attribute to select "type" radio when using options', async () => {
@ -204,7 +214,15 @@ describe('FormulateInputBox', () => {
it('renders no boxes when options array is empty', async () => { it('renders no boxes when options array is empty', async () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: [] } }) const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', options: [] } })
expect(wrapper.contains(FormulateInputGroup)).toBe(true) expect(wrapper.findComponent(FormulateInputGroup).exists()).toBe(true)
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false) expect(wrapper.find('input[type="checkbox"]').exists()).toBe(false)
}) })
it('renders multiple labels both with correct id', async () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'checkbox', label: 'VueFormulate FTW!'} })
const id = wrapper.find('input[type="checkbox"]').attributes('id')
const labelIds = wrapper.findAll('label').wrappers.map(label => label.attributes('for'));
expect(labelIds.length).toBe(2);
expect(labelIds.filter(labelId => labelId === id).length).toBe(2);
})
}) })

View File

@ -11,12 +11,12 @@ describe('FormulateInputButton', () => {
it('renders a button element', () => { it('renders a button element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'button' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'button' } })
expect(wrapper.contains(FormulateInputButton)).toBe(true) expect(wrapper.findComponent(FormulateInputButton).exists()).toBe(true)
}) })
it('renders a button element when type submit', () => { it('renders a button element when type submit', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'submit' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'submit' } })
expect(wrapper.contains(FormulateInputButton)).toBe(true) expect(wrapper.findComponent(FormulateInputButton).exists()).toBe(true)
}) })
it('uses value as highest priority content', () => { it('uses value as highest priority content', () => {
@ -103,3 +103,13 @@ describe('FormulateInputButton', () => {
}) })
}) })
it('passes an explicitly given name prop through to the root element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'button', name: 'foo' } })
expect(wrapper.find('button[name="foo"]').exists()).toBe(true)
})
it('additional context does not bleed through to button input attributes', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'button' } } )
expect(Object.keys(wrapper.find('button').attributes())).toEqual(["type", "id"])
})

View File

@ -12,12 +12,12 @@ describe('FormulateInputFile', () => {
it('type "file" renders a file element', () => { it('type "file" renders a file element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'file' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'file' } })
expect(wrapper.contains(FormulateInputFile)).toBe(true) expect(wrapper.findComponent(FormulateInputFile).exists()).toBe(true)
}) })
it('type "image" renders a file element', () => { it('type "image" renders a file element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'image' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'image' } })
expect(wrapper.contains(FormulateInputFile)).toBe(true) expect(wrapper.findComponent(FormulateInputFile).exists()).toBe(true)
}) })
it('forces an error-behavior live mode when upload-behavior is live and it has content', () => { it('forces an error-behavior live mode when upload-behavior is live and it has content', () => {
@ -37,6 +37,16 @@ describe('FormulateInputFile', () => {
expect(file.attributes('data-has-preview')).toBe('true') expect(file.attributes('data-has-preview')).toBe('true')
}) })
it('passes an explicitly given name prop through to the root element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'image', name: 'foo' } })
expect(wrapper.find('input[name="foo"]').exists()).toBe(true)
})
it('additional context does not bleed through to file input attributes', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'image' } } )
expect(Object.keys(wrapper.find('input[type="file"]').attributes())).toEqual(["type", "id"])
})
/** /**
* =========================================================================== * ===========================================================================
* Currently there appears to be no way to properly mock upload data in * Currently there appears to be no way to properly mock upload data in

View File

@ -28,7 +28,7 @@ describe('FormulateInputGroup', () => {
default: '<FormulateInput type="text" name="persona" />' default: '<FormulateInput type="text" name="persona" />'
} }
}) })
expect(wrapper.find(FormulateRepeatableProvider).vm.registry.has('persona')).toBeTruthy() expect(wrapper.findComponent(FormulateRepeatableProvider).vm.registry.has('persona')).toBeTruthy()
}) })
it('is not repeatable by default', async () => { it('is not repeatable by default', async () => {
@ -156,7 +156,7 @@ describe('FormulateInputGroup', () => {
submit submit
} }
}) })
const form = wrapper.find(FormulateForm) const form = wrapper.findComponent(FormulateForm)
await form.vm.formSubmitted() await form.vm.formSubmitted()
expect(submit.mock.calls.length).toBe(0); expect(submit.mock.calls.length).toBe(0);
}) })
@ -188,7 +188,7 @@ describe('FormulateInputGroup', () => {
submit submit
} }
}) })
const form = wrapper.find(FormulateForm) const form = wrapper.findComponent(FormulateForm)
await form.vm.formSubmitted() await form.vm.formSubmitted()
expect(submit.mock.calls.length).toBe(1); expect(submit.mock.calls.length).toBe(1);
}) })
@ -208,7 +208,7 @@ describe('FormulateInputGroup', () => {
</FormulateForm> </FormulateForm>
` `
}) })
const form = wrapper.find(FormulateForm) const form = wrapper.findComponent(FormulateForm)
await form.vm.formSubmitted() await form.vm.formSubmitted()
expect(wrapper.find('[data-classification="text"] .formulate-input-error').exists()).toBe(true); expect(wrapper.find('[data-classification="text"] .formulate-input-error').exists()).toBe(true);
}) })
@ -228,7 +228,7 @@ describe('FormulateInputGroup', () => {
</FormulateForm> </FormulateForm>
` `
}) })
const form = wrapper.find(FormulateForm) const form = wrapper.findComponent(FormulateForm)
await form.vm.formSubmitted() await form.vm.formSubmitted()
// Click the add more button // Click the add more button
wrapper.find('button[type="button"]').trigger('click') wrapper.find('button[type="button"]').trigger('click')
@ -251,7 +251,7 @@ describe('FormulateInputGroup', () => {
</FormulateForm> </FormulateForm>
` `
}) })
const form = wrapper.find(FormulateForm) const form = wrapper.findComponent(FormulateForm)
await form.vm.formSubmitted() await form.vm.formSubmitted()
// Click the add more button // Click the add more button
wrapper.find('button[type="button"]').trigger('click') wrapper.find('button[type="button"]').trigger('click')
@ -374,7 +374,7 @@ describe('FormulateInputGroup', () => {
} }
} }
}) })
const form = wrapper.find(FormulateForm) const form = wrapper.findComponent(FormulateForm)
await form.vm.formSubmitted() await form.vm.formSubmitted()
expect(wrapper.find('[data-classification="group"] > .formulate-input-errors').exists()).toBe(false) expect(wrapper.find('[data-classification="group"] > .formulate-input-errors').exists()).toBe(false)
}) })
@ -416,7 +416,7 @@ describe('FormulateInputGroup', () => {
} }
}) })
await flushPromises(); await flushPromises();
expect(wrapper.find(FormulateGrouping).vm.items).toEqual([{}]) expect(wrapper.findComponent(FormulateGrouping).vm.items).toEqual([{}])
}) })
it('allows repeatable groups to initialize with an empty array', async () => { it('allows repeatable groups to initialize with an empty array', async () => {
@ -438,6 +438,6 @@ describe('FormulateInputGroup', () => {
} }
}) })
await flushPromises(); await flushPromises();
expect(wrapper.find(FormulateGrouping).vm.items).toEqual([]) expect(wrapper.findComponent(FormulateGrouping).vm.items).toEqual([])
}) })
}) })

View File

@ -10,7 +10,7 @@ Vue.use(Formulate)
describe('FormulateInputSelect', () => { describe('FormulateInputSelect', () => {
it('renders select input when type is "select"', () => { it('renders select input when type is "select"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'select' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'select' } })
expect(wrapper.contains(FormulateInputSelect)).toBe(true) expect(wrapper.findComponent(FormulateInputSelect).exists()).toBe(true)
}) })
it('renders select options when options object is passed', () => { it('renders select options when options object is passed', () => {
@ -42,4 +42,14 @@ describe('FormulateInputSelect', () => {
expect(options.length).toBe(1) expect(options.length).toBe(1)
expect(options.at(0).attributes('disabled')).toBeTruthy() expect(options.at(0).attributes('disabled')).toBeTruthy()
}) })
it('passes an explicitly given name prop through to the root element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'select', options: [], name: 'foo' } })
expect(wrapper.find('select[name="foo"]').exists()).toBe(true)
})
it('additional context does not bleed through to text select attributes', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'select' } } )
expect(Object.keys(wrapper.find('select').attributes())).toEqual(["id"])
})
}) })

View File

@ -13,7 +13,7 @@ Vue.use(Formulate)
describe('FormulateInputSlider', () => { describe('FormulateInputSlider', () => {
it('renders range input when type is "range"', () => { it('renders range input when type is "range"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'range' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'range' } })
expect(wrapper.contains(FormulateInputSlider)).toBe(true) expect(wrapper.findComponent(FormulateInputSlider).exists()).toBe(true)
}) })
it('does not show value if the show-value prop is not set', () => { it('does not show value if the show-value prop is not set', () => {
@ -25,4 +25,14 @@ describe('FormulateInputSlider', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'range', showValue: 'true', value: '15', min: '0', max: '100' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'range', showValue: 'true', value: '15', min: '0', max: '100' } })
expect(wrapper.find('.formulate-input-element-range-value').text()).toBe('15') expect(wrapper.find('.formulate-input-element-range-value').text()).toBe('15')
}) })
it('passes an explicitly given name prop through to the root element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'range', name: 'foo' } })
expect(wrapper.find('input[name="foo"]').exists()).toBe(true)
})
it('additional context does not bleed through to range input attributes', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'range' } } )
expect(Object.keys(wrapper.find('input[type="range"]').attributes())).toEqual(["type", "id"])
})
}) })

View File

@ -15,62 +15,62 @@ Vue.use(Formulate)
describe('FormulateInputText', () => { describe('FormulateInputText', () => {
it('renders text input when type is "text"', () => { it('renders text input when type is "text"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders search input when type is "search"', () => { it('renders search input when type is "search"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'search' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'search' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders email input when type is "email"', () => { it('renders email input when type is "email"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'email' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'email' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders number input when type is "number"', () => { it('renders number input when type is "number"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'number' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'number' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders color input when type is "color"', () => { it('renders color input when type is "color"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'color' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'color' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders date input when type is "date"', () => { it('renders date input when type is "date"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'date' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'date' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders month input when type is "month"', () => { it('renders month input when type is "month"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'month' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'month' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders password input when type is "password"', () => { it('renders password input when type is "password"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'password' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'password' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders tel input when type is "tel"', () => { it('renders tel input when type is "tel"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'tel' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'tel' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders time input when type is "time"', () => { it('renders time input when type is "time"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'time' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'time' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders url input when type is "url"', () => { it('renders url input when type is "url"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'url' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'url' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
it('renders week input when type is "week"', () => { it('renders week input when type is "week"', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'week' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'week' } })
expect(wrapper.contains(FormulateInputText)).toBe(true) expect(wrapper.findComponent(FormulateInputText).exists()).toBe(true)
}) })
/** /**
@ -83,6 +83,26 @@ describe('FormulateInputText', () => {
expect(wrapper.find(`input[id="${wrapper.vm.context.attributes.id}"]`).exists()).toBe(true) expect(wrapper.find(`input[id="${wrapper.vm.context.attributes.id}"]`).exists()).toBe(true)
}) })
it('passes an explicitly given name prop through to the root text element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text', name: 'foo' } })
expect(wrapper.find('input[name="foo"]').exists()).toBe(true)
})
it('passes an explicitly given name prop through to the root textarea element', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'textarea', name: 'foo' } })
expect(wrapper.find('textarea[name="foo"]').exists()).toBe(true)
})
it('additional context does not bleed through to text input attributes', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } } )
expect(Object.keys(wrapper.find('input[type="text"]').attributes())).toEqual(["type", "id"])
})
it('additional context does not bleed through to textarea input attributes', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'textarea' } } )
expect(Object.keys(wrapper.find('textarea').attributes())).toEqual(["id"])
})
it('doesnt automatically add a label', () => { it('doesnt automatically add a label', () => {
const wrapper = mount(FormulateInput, { propsData: { type: 'text' } }) const wrapper = mount(FormulateInput, { propsData: { type: 'text' } })
expect(wrapper.find('label').exists()).toBe(false) expect(wrapper.find('label').exists()).toBe(false)
@ -130,15 +150,15 @@ describe('FormulateInputText', () => {
` `
}) })
await flushPromises() await flushPromises()
const firstContext = wrapper.find({ref: "first"}).vm.context const firstContext = wrapper.findComponent({ref: "first"}).vm.context
const secondContext = wrapper.find({ref: "second"}).vm.context const secondContext = wrapper.findComponent({ref: "second"}).vm.context
wrapper.find('input').setValue('new value') wrapper.find('input').setValue('new value')
await flushPromises() await flushPromises()
expect(firstContext).toBeTruthy() expect(firstContext).toBeTruthy()
expect(wrapper.vm.valueA === 'new value').toBe(true) expect(wrapper.vm.valueA === 'new value').toBe(true)
expect(wrapper.vm.valueB === 'second value').toBe(true) expect(wrapper.vm.valueB === 'second value').toBe(true)
expect(wrapper.find({ref: "first"}).vm.context === firstContext).toBe(false) expect(wrapper.findComponent({ref: "first"}).vm.context === firstContext).toBe(false)
expect(wrapper.find({ref: "second"}).vm.context === secondContext).toBe(true) expect(wrapper.findComponent({ref: "second"}).vm.context === secondContext).toBe(true)
}) })
it('uses the v-model value as the initial value', () => { it('uses the v-model value as the initial value', () => {

View File

@ -168,6 +168,7 @@
width: 1em; width: 1em;
height: 1em; height: 1em;
border-radius: 1em; border-radius: 1em;
border: 0;
background-color: $formulate-green; background-color: $formulate-green;
margin-top: calc(-.5em + 2px); margin-top: calc(-.5em + 2px);
} }
@ -197,6 +198,10 @@
&::-webkit-slider-runnable-track { &::-webkit-slider-runnable-track {
@include track; @include track;
} }
&::-moz-range-track {
@include track;
}
} }
} }