Skip to content

Ensure setChecked() and setSelected() only trigger DOM events when state is changed (fix #1339) #1380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 3, 2020
8 changes: 8 additions & 0 deletions packages/test-utils/src/wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,10 @@ export default class Wrapper implements BaseWrapper {
)
}

if (this.element.checked === checked) {
return
}

if (event !== 'click' || isPhantomJS) {
// $FlowIgnore
this.element.selected = true
Expand All @@ -400,6 +404,10 @@ export default class Wrapper implements BaseWrapper {
}

if (tagName === 'OPTION') {
if (this.element.selected) {
return
}

// $FlowIgnore
this.element.selected = true
// $FlowIgnore
Expand Down
74 changes: 73 additions & 1 deletion test/specs/wrapper/setChecked.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,43 @@ describeWithShallowAndMount('setChecked', mountingMethod => {
expect(wrapper.find('.counter').text()).to.equal('4')
})

it('triggers a change event when called on a checkbox', () => {
const listener = sinon.spy()

mountingMethod({
// For compatibility with earlier versions of Vue that use the `click`
// event for updating `v-model`.
template: `
<input
type="checkbox"
@change="listener"
@click="listener"
>
`,
methods: { listener }
}).setChecked()

expect(listener).to.have.been.called
})

it('does not trigger a change event if the checkbox is already checked', () => {
const listener = sinon.spy()

mountingMethod({
template: `
<input
type="checkbox"
checked
@change="listener"
@click="listener"
>
`,
methods: { listener }
}).setChecked()

expect(listener).not.to.have.been.called
})

it('updates dom with radio v-model', async () => {
const wrapper = mountingMethod(ComponentWithInput)

Expand All @@ -67,7 +104,7 @@ describeWithShallowAndMount('setChecked', mountingMethod => {
expect(wrapper.text()).to.contain('radioFooResult')
})

it('changes state the right amount of times with checkbox v-model', async () => {
it('changes state the right amount of times with radio v-model', async () => {
const wrapper = mountingMethod(ComponentWithInput)
const radioBar = wrapper.find('#radioBar')
const radioFoo = wrapper.find('#radioFoo')
Expand All @@ -89,6 +126,41 @@ describeWithShallowAndMount('setChecked', mountingMethod => {
expect(wrapper.find('.counter').text()).to.equal('4')
})

it('triggers a change event when called on a radio button', () => {
const listener = sinon.spy()

mountingMethod({
template: `
<input
type="radio"
@change="listener"
@click="listener"
>
`,
methods: { listener }
}).setChecked()

expect(listener).to.have.been.called
})

it('does not trigger a change event if the radio button is already checked', () => {
const listener = sinon.spy()

mountingMethod({
template: `
<input
type="radio"
checked
@change="listener"
@click="listener"
>
`,
methods: { listener }
}).setChecked()

expect(listener).not.to.have.been.called
})

it('throws error if checked param is not boolean', () => {
const message = 'wrapper.setChecked() must be passed a boolean'
const wrapper = mountingMethod(ComponentWithInput)
Expand Down
38 changes: 38 additions & 0 deletions test/specs/wrapper/setSelected.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,44 @@ describeWithShallowAndMount('setSelected', mountingMethod => {
expect(wrapper.text()).to.contain('selectA')
})

it('triggers a change event on the parent select', () => {
const change = sinon.spy()

mountingMethod({
template: `
<select @change="change">
<option />
<option value="foo" />
</select>
`,
methods: { change }
})
.findAll('option')
.at(1)
.setSelected()

expect(change).to.have.been.called
})

it('does not trigger an event if called on already selected option', () => {
const change = sinon.spy()

mountingMethod({
template: `
<select @change="change">
<option />
<option selected value="foo" />
</select>
`,
methods: { change }
})
.findAll('option')
.at(1)
.setSelected()

expect(change).not.to.have.been.called
})

it('throws error if element is not valid', () => {
const message = 'wrapper.setSelected() cannot be called on this element'

Expand Down