Skip to content

Commit

Permalink
Merge pull request #230 from vuejs/add-set-data
Browse files Browse the repository at this point in the history
Add setData
  • Loading branch information
afontcu committed Oct 28, 2020
2 parents c3bee18 + fc49c02 commit 6dc4c64
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ text | ✅ |
trigger | ✅ | returns `nextTick`. You can do `await wrapper.find('button').trigger('click')`
setProps | ✅ |
props | ✅
setData | ❌ | has PR
setData | ✅ |
destroy | ✅ | renamed to `unmount` to match Vue 3 lifecycle hook name.
props | ✅
contains | ⚰️| use `find`
Expand Down
28 changes: 28 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,31 @@ export function mergeGlobalProperties(
export function isFunctionalComponent(component: any) {
return typeof component === 'function'
}

// https://stackoverflow.com/a/48218209
export const mergeDeep = (
target: Record<string, any>,
source: Record<string, any>
) => {
const isObject = (obj: unknown): obj is Object =>
obj && typeof obj === 'object'

if (!isObject(target) || !isObject(source)) {
return source
}

Object.keys(source).forEach((key) => {
const targetValue = target[key]
const sourceValue = source[key]

if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
target[key] = targetValue.concat(sourceValue)
} else if (isObject(targetValue) && isObject(sourceValue)) {
target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue)
} else {
target[key] = sourceValue
}
})

return target
}
7 changes: 6 additions & 1 deletion src/vueWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { createWrapperError } from './errorWrapper'
import { TriggerOptions } from './createDomEvent'
import { find, matches } from './utils/find'
import { isFunctionalComponent } from './utils'
import { isFunctionalComponent, mergeDeep } from './utils'

export class VueWrapper<T extends ComponentPublicInstance> {
private componentVM: T
Expand Down Expand Up @@ -226,6 +226,11 @@ export class VueWrapper<T extends ComponentPublicInstance> {
return Array.from(results).map((element) => new DOMWrapper(element))
}

setData(data: Record<string, any>): Promise<void> {
mergeDeep(this.componentVM.$data, data)
return nextTick()
}

setProps(props: Record<string, any>): Promise<void> {
// if this VM's parent is not the root or if setProps does not exist, error out
if (this.vm.$parent !== this.rootVM || !this.__setProps) {
Expand Down
91 changes: 91 additions & 0 deletions tests/setData.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { defineComponent, ref } from 'vue'

import { mount } from '../src'

describe('setData', () => {
it('sets component data', async () => {
const Component = {
template: '<div>{{ foo }}</div>',
data: () => ({ foo: 'bar' })
}

const wrapper = mount(Component)
expect(wrapper.html()).toContain('bar')

await wrapper.setData({ foo: 'qux' })
expect(wrapper.html()).toContain('qux')
})

it('causes nested nodes to re-render', async () => {
const Component = {
template: `<div><div v-if="show" id="show">Show</div></div>`,
data: () => ({ show: false })
}

const wrapper = mount(Component)

expect(wrapper.find('#show').exists()).toBe(false)

await wrapper.setData({ show: true })

expect(wrapper.find('#show').exists()).toBe(true)
})

it('updates a single property of a complex object', async () => {
const Component = {
template: `<div>{{ complexObject.string }}. bar: {{ complexObject.foo.bar }}</div>`,
data: () => ({
complexObject: {
string: 'will not change',
foo: {
bar: 'old val'
}
}
})
}

const wrapper = mount(Component)

expect(wrapper.html()).toContain('will not change. bar: old val')

await wrapper.setData({
complexObject: {
foo: {
bar: 'new val'
}
}
})

expect(wrapper.html()).toContain('will not change. bar: new val')
})

it('does not set new properties', async () => {
jest.spyOn(console, 'warn').mockImplementationOnce(() => {})

const Component = {
template: `<div>{{ foo || 'fallback' }}</div>`
}

const wrapper = mount(Component)

expect(wrapper.html()).toContain('fallback')

expect(() => wrapper.setData({ foo: 'bar' })).toThrowError(
'Cannot add property foo'
)
})

it('does not modify composition API setup data', async () => {
const Component = defineComponent({
template: `<div>Count is: {{ count }}</div>`,
setup: () => ({ count: ref(1) })
})
const wrapper = mount(Component)

expect(wrapper.html()).toContain('Count is: 1')

expect(() => wrapper.setData({ count: 2 })).toThrowError(
'Cannot add property count'
)
})
})

0 comments on commit 6dc4c64

Please sign in to comment.