@@ -23,32 +23,50 @@ import createWrapper from './create-wrapper'
2323import { orderWatchers } from './order-watchers'
2424
2525export default class Wrapper implements BaseWrapper {
26- vnode : VNode | null ;
27- vm: Component | null ;
26+ + vnode : VNode | null ;
27+ + vm : Component | null ;
2828 _emitted : { [ name : string ] : Array < Array < any > > } ;
2929 _emittedByOrder: Array < { name : string , args : Array < any > } > ;
3030 isVm: boolean ;
31- element: Element ;
31+ + element : Element ;
3232 update: Function ;
33- options: WrapperOptions ;
33+ + options : WrapperOptions ;
3434 version: number ;
3535 isFunctionalComponent: boolean ;
3636
3737 constructor ( node : VNode | Element , options : WrapperOptions ) {
38- if ( node instanceof Element ) {
39- this . element = node
40- this . vnode = null
41- } else {
42- this . vnode = node
43- this . element = node . elm
38+ const vnode = node instanceof Element ? null : node
39+ const element = node instanceof Element ? node : node . elm
40+ // Prevent redefine by VueWrapper
41+ if ( this . constructor . name === 'Wrapper' ) {
42+ // $FlowIgnore
43+ Object . defineProperty ( this , 'vnode' , {
44+ get : ( ) => vnode ,
45+ set : ( ) => { }
46+ } )
47+ // $FlowIgnore
48+ Object . defineProperty ( this , 'element' , {
49+ get : ( ) => element ,
50+ set : ( ) => { }
51+ } )
52+ // $FlowIgnore
53+ Object . defineProperty ( this , 'vm' , {
54+ get : ( ) => undefined ,
55+ set : ( ) => { }
56+ } )
4457 }
58+ const frozenOptions = Object . freeze ( options )
59+ // $FlowIgnore
60+ Object . defineProperty ( this , 'options' , {
61+ get : ( ) => frozenOptions ,
62+ set : ( ) => { }
63+ } )
4564 if (
4665 this . vnode &&
4766 ( this . vnode [ FUNCTIONAL_OPTIONS ] || this . vnode . functionalContext )
4867 ) {
4968 this . isFunctionalComponent = true
5069 }
51- this . options = options
5270 this . version = Number (
5371 `${ Vue . version . split ( '.' ) [ 0 ] } .${ Vue . version . split ( '.' ) [ 1 ] } `
5472 )
@@ -112,7 +130,7 @@ export default class Wrapper implements BaseWrapper {
112130 */
113131 emitted ( event ?: string ) {
114132 if ( ! this . _emitted && ! this . vm ) {
115- throwError ( `wrapper . emitted ( ) can only be called on a Vue ` + ` instance `)
133+ throwError ( `wrapper . emitted ( ) can only be called on a Vue instance `)
116134 }
117135 if ( event ) {
118136 return this . _emitted [ event ]
@@ -126,7 +144,7 @@ export default class Wrapper implements BaseWrapper {
126144 emittedByOrder ( ) {
127145 if ( ! this . _emittedByOrder && ! this . vm ) {
128146 throwError (
129- `wrapper . emittedByOrder ( ) can only be called on a ` + ` Vue instance `
147+ `wrapper . emittedByOrder ( ) can only be called on a Vue instance `
130148 )
131149 }
132150 return this . _emittedByOrder
@@ -155,13 +173,7 @@ export default class Wrapper implements BaseWrapper {
155173 `visible has been deprecated and will be removed in ` +
156174 `version 1, use isVisible instead`
157175 )
158-
159176 let element = this . element
160-
161- if ( ! element ) {
162- return false
163- }
164-
165177 while ( element ) {
166178 if (
167179 element . style &&
@@ -188,17 +200,17 @@ export default class Wrapper implements BaseWrapper {
188200
189201 if ( typeof attribute !== 'string' ) {
190202 throwError (
191- `wrapper.hasAttribute() must be passed attribute as ` + ` a string`
203+ `wrapper.hasAttribute() must be passed attribute as a string`
192204 )
193205 }
194206
195207 if ( typeof value !== 'string' ) {
196208 throwError (
197- `wrapper.hasAttribute() must be passed value as a ` + ` string`
209+ `wrapper.hasAttribute() must be passed value as a string`
198210 )
199211 }
200212
201- return ! ! ( this . element && this . element . getAttribute ( attribute ) === value )
213+ return ! ! ( this . element . getAttribute ( attribute ) === value )
202214 }
203215
204216 /**
@@ -270,7 +282,7 @@ export default class Wrapper implements BaseWrapper {
270282 )
271283
272284 if ( typeof style !== 'string' ) {
273- throwError ( `wrapper.hasStyle() must be passed style as a ` + ` string`)
285+ throwError ( `wrapper.hasStyle() must be passed style as a string` )
274286 }
275287
276288 if ( typeof value !== 'string' ) {
@@ -413,11 +425,6 @@ export default class Wrapper implements BaseWrapper {
413425 */
414426 isVisible ( ) : boolean {
415427 let element = this . element
416-
417- if ( ! element ) {
418- return false
419- }
420-
421428 while ( element ) {
422429 if (
423430 element . style &&
@@ -669,41 +676,32 @@ export default class Wrapper implements BaseWrapper {
669676 * Sets element value and triggers input event
670677 */
671678 setValue ( value : any ) {
672- const el = this . element
673-
674- if ( ! el ) {
675- throwError (
676- `cannot call wrapper.setValue() on a wrapper ` + `without an element`
677- )
678- }
679-
680- const tag = el . tagName
679+ const tagName = this . element . tagName
681680 const type = this . attributes ( ) . type
682- const event = 'input'
683681
684- if ( tag === 'SELECT' ) {
682+ if ( tagName === 'SELECT' ) {
685683 throwError (
686684 `wrapper.setValue() cannot be called on a <select> ` +
687685 `element. Use wrapper.setSelected() instead`
688686 )
689- } else if ( tag === 'INPUT' && type === 'checkbox' ) {
687+ } else if ( tagName === 'INPUT' && type === 'checkbox' ) {
690688 throwError (
691689 `wrapper.setValue() cannot be called on a <input ` +
692690 `type="checkbox" /> element. Use ` +
693691 `wrapper.setChecked() instead`
694692 )
695- } else if ( tag === 'INPUT' && type === 'radio' ) {
693+ } else if ( tagName === 'INPUT' && type === 'radio' ) {
696694 throwError (
697695 `wrapper.setValue() cannot be called on a <input ` +
698696 `type="radio" /> element. Use wrapper.setChecked() ` +
699697 `instead`
700698 )
701- } else if ( tag === 'INPUT' || tag === 'textarea' ) {
699+ } else if ( tagName === 'INPUT' || tagName === 'textarea' ) {
702700 // $FlowIgnore
703- el . value = value
704- this . trigger ( event )
701+ this . element . value = value
702+ this . trigger ( 'input' )
705703 } else {
706- throwError ( `wrapper.setValue() cannot be called on this ` + ` element`)
704+ throwError ( `wrapper.setValue() cannot be called on this element` )
707705 }
708706 }
709707
@@ -714,36 +712,26 @@ export default class Wrapper implements BaseWrapper {
714712 if ( typeof checked !== 'boolean' ) {
715713 throwError ( 'wrapper.setChecked() must be passed a boolean' )
716714 }
717-
718- const el = this . element
719-
720- if ( ! el ) {
721- throwError (
722- `cannot call wrapper.setChecked() on a wrapper ` + `without an element`
723- )
724- }
725-
726- const tag = el . tagName
715+ const tagName = this . element . tagName
727716 const type = this . attributes ( ) . type
728- const event = 'change'
729717
730- if ( tag === 'SELECT' ) {
718+ if ( tagName === 'SELECT' ) {
731719 throwError (
732720 `wrapper.setChecked() cannot be called on a ` +
733721 `<select> element. Use wrapper.setSelected() ` +
734722 `instead`
735723 )
736- } else if ( tag === 'INPUT' && type === 'checkbox' ) {
724+ } else if ( tagName === 'INPUT' && type === 'checkbox' ) {
737725 // $FlowIgnore
738- if ( el . checked !== checked ) {
726+ if ( this . element . checked !== checked ) {
739727 if ( ! navigator . userAgent . includes ( 'jsdom' ) ) {
740728 // $FlowIgnore
741- el . checked = checked
729+ this . element . checked = checked
742730 }
743731 this . trigger ( 'click' )
744- this . trigger ( event )
732+ this . trigger ( 'change' )
745733 }
746- } else if ( tag === 'INPUT' && type === 'radio' ) {
734+ } else if ( tagName === 'INPUT' && type === 'radio' ) {
747735 if ( ! checked ) {
748736 throwError (
749737 `wrapper.setChecked() cannot be called with ` +
@@ -752,87 +740,72 @@ export default class Wrapper implements BaseWrapper {
752740 )
753741 } else {
754742 // $FlowIgnore
755- if ( ! el . checked ) {
743+ if ( ! this . element . checked ) {
756744 this . trigger ( 'click' )
757- this . trigger ( event )
745+ this . trigger ( 'change' )
758746 }
759747 }
760- } else if ( tag === 'INPUT' || tag === 'textarea' ) {
748+ } else if ( tagName === 'INPUT' || tagName === 'textarea' ) {
761749 throwError (
762750 `wrapper.setChecked() cannot be called on "text" ` +
763751 `inputs. Use wrapper.setValue() instead`
764752 )
765753 } else {
766- throwError ( `wrapper.setChecked() cannot be called on this ` + ` element`)
754+ throwError ( `wrapper.setChecked() cannot be called on this element` )
767755 }
768756 }
769757
770758 /**
771759 * Selects <option></option> element
772760 */
773761 setSelected ( ) {
774- const el = this . element
775-
776- if ( ! el ) {
777- throwError (
778- `cannot call wrapper.setSelected() on a wrapper ` + `without an element`
779- )
780- }
781-
782- const tag = el . tagName
762+ const tagName = this . element . tagName
783763 const type = this . attributes ( ) . type
784- const event = 'change'
785764
786- if ( tag === 'OPTION' ) {
765+ if ( tagName === 'OPTION' ) {
787766 // $FlowIgnore
788- el . selected = true
767+ this . element . selected = true
789768 // $FlowIgnore
790- if ( el . parentElement . tagName === 'OPTGROUP' ) {
769+ if ( this . element . parentElement . tagName === 'OPTGROUP' ) {
791770 // $FlowIgnore
792- createWrapper ( el . parentElement . parentElement , this . options ) . trigger (
793- event
794- )
771+ createWrapper ( this . element . parentElement . parentElement , this . options )
772+ . trigger ( 'change' )
795773 } else {
796774 // $FlowIgnore
797- createWrapper ( el . parentElement , this . options ) . trigger ( event )
775+ createWrapper ( this . element . parentElement , this . options )
776+ . trigger ( 'change' )
798777 }
799- } else if ( tag === 'SELECT' ) {
778+ } else if ( tagName === 'SELECT' ) {
800779 throwError (
801780 `wrapper.setSelected() cannot be called on select. ` +
802781 `Call it on one of its options`
803782 )
804- } else if ( tag === 'INPUT' && type === 'checkbox' ) {
783+ } else if ( tagName === 'INPUT' && type === 'checkbox' ) {
805784 throwError (
806785 `wrapper.setSelected() cannot be called on a <input ` +
807786 `type="checkbox" /> element. Use ` +
808787 `wrapper.setChecked() instead`
809788 )
810- } else if ( tag === 'INPUT' && type === 'radio' ) {
789+ } else if ( tagName === 'INPUT' && type === 'radio' ) {
811790 throwError (
812791 `wrapper.setSelected() cannot be called on a <input ` +
813792 `type="radio" /> element. Use wrapper.setChecked() ` +
814793 `instead`
815794 )
816- } else if ( tag === 'INPUT' || tag === 'textarea' ) {
795+ } else if ( tagName === 'INPUT' || tagName === 'textarea' ) {
817796 throwError (
818797 `wrapper.setSelected() cannot be called on "text" ` +
819798 `inputs. Use wrapper.setValue() instead`
820799 )
821800 } else {
822- throwError ( `wrapper.setSelected() cannot be called on this ` + ` element`)
801+ throwError ( `wrapper.setSelected() cannot be called on this element` )
823802 }
824803 }
825804
826805 /**
827806 * Return text of wrapper element
828807 */
829808 text ( ) : string {
830- if ( ! this . element ) {
831- throwError (
832- `cannot call wrapper.text() on a wrapper without an ` + `element`
833- )
834- }
835-
836809 return this . element . textContent . trim ( )
837810 }
838811
@@ -859,12 +832,6 @@ export default class Wrapper implements BaseWrapper {
859832 throwError ( 'wrapper.trigger() must be passed a string' )
860833 }
861834
862- if ( ! this . element ) {
863- throwError (
864- `cannot call wrapper.trigger() on a wrapper without ` + `an element`
865- )
866- }
867-
868835 if ( options . target ) {
869836 throwError (
870837 `you cannot set the target value of an event. See ` +
0 commit comments