Skip to content

Commit fb46268

Browse files
38elementseddyerburgh
authored andcommitted
feat: throw error if the read-only property is tried to change (#749)
1 parent b801c25 commit fb46268

File tree

10 files changed

+49
-48
lines changed

10 files changed

+49
-48
lines changed

docs/api/wrapper-array/README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ A `WrapperArray` is an object that contains an array of [`Wrappers`](../wrapper/
44

55
## Properties
66

7-
### `wrappers`
7+
### `wrappers`
88

9-
`array`: the `Wrappers` contained in the `WrapperArray`
9+
`array` (read-only): the `Wrappers` contained in the `WrapperArray`
1010

11-
### `length`
11+
### `length`
1212

13-
`number`: the number of `Wrappers` contained in the `WrapperArray`
13+
`number` (read-only): the number of `Wrappers` contained in the `WrapperArray`
1414

1515
## Methods
1616

docs/api/wrapper/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ A `Wrapper` is an object that contains a mounted component or vnode and methods
1414

1515
`HTMLElement` (read-only): the root DOM node of the wrapper
1616

17-
### `options`
17+
### `options`
1818

1919
#### `options.attachedToDocument`
2020

2121
`Boolean` (read-only): True if `attachedToDocument` in mounting options was `true`
2222

23-
#### `options.sync`
23+
#### `options.sync`
2424

2525
`Boolean` (read-only): True if `sync` in mounting options was not `false`
2626

packages/test-utils/src/vue-wrapper.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @flow
22

33
import Wrapper from './wrapper'
4+
import { throwError } from 'shared/util'
45
import { setWatchersToSync } from './set-watchers-to-sync'
56
import { orderWatchers } from './order-watchers'
67

@@ -11,17 +12,17 @@ export default class VueWrapper extends Wrapper implements BaseWrapper {
1112
// $FlowIgnore : issue with defineProperty
1213
Object.defineProperty(this, 'vnode', {
1314
get: () => vm._vnode,
14-
set: () => {}
15+
set: () => throwError('wrapper.vnode is read-only')
1516
})
1617
// $FlowIgnore
1718
Object.defineProperty(this, 'element', {
1819
get: () => vm.$el,
19-
set: () => {}
20+
set: () => throwError('wrapper.element is read-only')
2021
})
2122
// $FlowIgnore
2223
Object.defineProperty(this, 'vm', {
2324
get: () => vm,
24-
set: () => {}
25+
set: () => throwError('wrapper.vm is read-only')
2526
})
2627
if (options.sync) {
2728
setWatchersToSync(vm)

packages/test-utils/src/wrapper-array.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@ import type VueWrapper from './vue-wrapper'
55
import { throwError, warn } from 'shared/util'
66

77
export default class WrapperArray implements BaseWrapper {
8-
wrappers: Array<Wrapper | VueWrapper>;
9-
length: number;
8+
+wrappers: Array<Wrapper | VueWrapper>;
9+
+length: number;
1010

1111
constructor (wrappers: Array<Wrapper | VueWrapper>) {
12-
this.wrappers = wrappers || []
13-
this.length = this.wrappers.length
12+
const length = wrappers.length
13+
// $FlowIgnore
14+
Object.defineProperty(this, 'wrappers', {
15+
get: () => wrappers,
16+
set: () => throwError('wrapperArray.wrappers is read-only')
17+
})
18+
// $FlowIgnore
19+
Object.defineProperty(this, 'length', {
20+
get: () => length,
21+
set: () => throwError('wrapperArray.length is read-only')
22+
})
1423
}
1524

1625
at (index: number): Wrapper | VueWrapper {

packages/test-utils/src/wrapper.js

+5-7
Original file line numberDiff line numberDiff line change
@@ -42,24 +42,24 @@ export default class Wrapper implements BaseWrapper {
4242
// $FlowIgnore
4343
Object.defineProperty(this, 'vnode', {
4444
get: () => vnode,
45-
set: () => {}
45+
set: () => throwError('wrapper.vnode is read-only')
4646
})
4747
// $FlowIgnore
4848
Object.defineProperty(this, 'element', {
4949
get: () => element,
50-
set: () => {}
50+
set: () => throwError('wrapper.element is read-only')
5151
})
5252
// $FlowIgnore
5353
Object.defineProperty(this, 'vm', {
5454
get: () => undefined,
55-
set: () => {}
55+
set: () => throwError('wrapper.vm is read-only')
5656
})
5757
}
5858
const frozenOptions = Object.freeze(options)
5959
// $FlowIgnore
6060
Object.defineProperty(this, 'options', {
6161
get: () => frozenOptions,
62-
set: () => {}
62+
set: () => throwError('wrapper.options is read-only')
6363
})
6464
if (
6565
this.vnode &&
@@ -399,7 +399,6 @@ export default class Wrapper implements BaseWrapper {
399399
}
400400

401401
return !!(
402-
this.element &&
403402
this.element.getAttribute &&
404403
this.element.matches(selector)
405404
)
@@ -667,7 +666,6 @@ export default class Wrapper implements BaseWrapper {
667666
})
668667

669668
// $FlowIgnore : Problem with possibly null this.vm
670-
this.vnode = this.vm._vnode
671669
orderWatchers(this.vm || this.vnode.context.$root)
672670
Vue.config.silent = originalConfig
673671
}
@@ -814,7 +812,7 @@ export default class Wrapper implements BaseWrapper {
814812
*/
815813
destroy () {
816814
if (!this.isVueInstance()) {
817-
throwError(`wrapper.destroy() can only be called on a Vue ` + `instance`)
815+
throwError(`wrapper.destroy() can only be called on a Vue instance`)
818816
}
819817

820818
if (this.element.parentNode) {

test/specs/vuewrapper.js renamed to test/specs/vue-wrapper.spec.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ describeWithShallowAndMount('VueWrapper', mountingMethod => {
55
it(`has the ${property} property which is read-only`, () => {
66
const wrapper = mountingMethod({ template: '<div><p></p></div>' })
77
expect(wrapper.constructor.name).to.equal('VueWrapper')
8-
const originalProperty = wrapper[property]
9-
wrapper[property] = 'foo'
10-
expect(wrapper[property]).to.equal(originalProperty)
8+
const message = `[vue-test-utils]: wrapper.${property} is read-only`
9+
expect(() => { wrapper[property] = 'foo' })
10+
.to.throw()
11+
.with.property('message', message)
1112
})
1213
})
1314
})

test/specs/wrapper-array.spec.js

+13-9
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ describeWithShallowAndMount('WrapperArray', mountingMethod => {
77
const wrapper = mountingMethod(compiled)
88
const wrapperArray = wrapper.findAll('p')
99
expect(wrapperArray.constructor.name).to.equal('WrapperArray')
10-
if (wrappers) {
11-
wrapperArray.wrappers = wrappers
12-
wrapperArray.length = wrappers.length
13-
}
14-
return wrapperArray
10+
return wrappers ? new wrapperArray.constructor(wrappers) : wrapperArray
1511
}
1612

13+
['wrappers', 'length'].forEach(property => {
14+
it(`has the ${property} property which is read-only`, () => {
15+
const wrapperArray = getWrapperArray()
16+
const message = `[vue-test-utils]: wrapperArray.${property} is read-only`
17+
expect(() => { wrapperArray[property] = 'foo' })
18+
.to.throw()
19+
.with.property('message', message)
20+
})
21+
})
22+
1723
it('returns class with length equal to length of wrappers passed in constructor', () => {
1824
const wrapperArray = getWrapperArray()
1925
expect(wrapperArray.length).to.equal(3)
@@ -67,8 +73,7 @@ describeWithShallowAndMount('WrapperArray', mountingMethod => {
6773
if (method === 'at') {
6874
return
6975
}
70-
const wrapperArray = getWrapperArray()
71-
wrapperArray.wrappers = []
76+
const wrapperArray = getWrapperArray([])
7277
const message = `[vue-test-utils]: ${method} cannot be called on 0 items`
7378
expect(() => wrapperArray[method]())
7479
.to.throw()
@@ -99,8 +104,7 @@ describeWithShallowAndMount('WrapperArray', mountingMethod => {
99104
) {
100105
return
101106
}
102-
const wrapperArray = getWrapperArray()
103-
wrapperArray.wrappers = [1, 2, 3]
107+
const wrapperArray = getWrapperArray([1, 2, 3])
104108
const message = `[vue-test-utils]: ${method} must be called on a single wrapper, use at(i) to access a wrapper`
105109
expect(() => wrapperArray[method]())
106110
.to.throw()

test/specs/wrapper.spec.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ describeWithShallowAndMount('Wrapper', mountingMethod => {
66
const wrapper = mountingMethod({ template: '<div><p></p></div>' })
77
.find('p')
88
expect(wrapper.constructor.name).to.equal('Wrapper')
9-
const originalProperty = wrapper[property]
10-
wrapper[property] = 'foo'
11-
expect(wrapper[property]).to.equal(originalProperty)
9+
const message = `[vue-test-utils]: wrapper.${property} is read-only`
10+
expect(() => { wrapper[property] = 'foo' })
11+
.to.throw()
12+
.with.property('message', message)
1213
})
1314
})
1415
})

test/specs/wrapper/attributes.spec.js

-7
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,4 @@ describeWithShallowAndMount('attributes', mountingMethod => {
1515
const wrapper = mountingMethod(compiled)
1616
expect(wrapper.attributes()).to.eql({})
1717
})
18-
19-
it('returns empty object if wrapper element is null', () => {
20-
const compiled = compileToFunctions('<div />')
21-
const wrapper = mountingMethod(compiled)
22-
wrapper.element = null
23-
expect(wrapper.attributes()).to.eql({})
24-
})
2518
})

test/specs/wrapper/is.spec.js

-6
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@ describeWithShallowAndMount('is', mountingMethod => {
2828
expect(wrapper.is('#div')).to.equal(true)
2929
})
3030

31-
it('returns false if wrapper does not contain element', () => {
32-
const wrapper = mountingMethod(ComponentWithChild)
33-
wrapper.element = null
34-
expect(wrapper.is('a')).to.equal(false)
35-
})
36-
3731
it('returns true if root node matches Vue Component selector', () => {
3832
const wrapper = mountingMethod(ComponentWithChild)
3933
const component = wrapper.findAll(Component).at(0)

0 commit comments

Comments
 (0)