mirror of
				https://github.com/vuejs/babel-plugin-jsx.git
				synced 2025-10-31 17:32:32 +08:00 
			
		
		
		
	feat: support v-models (#140)
* feat: support multiple bindings * refactor: optimize code * test: add underscore modifier case * refactor: modify value to values * refactor: remove underscore modifier support * refactor: optimize v-models * refactor: optimize code
This commit is contained in:
		| @@ -203,7 +203,7 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | |||||||
|         } |         } | ||||||
|         if (isDirective(name)) { |         if (isDirective(name)) { | ||||||
|           const { |           const { | ||||||
|             directive, modifiers, value, arg, directiveName, |             directive, modifiers, values, args, directiveName, | ||||||
|           } = parseDirectives({ |           } = parseDirectives({ | ||||||
|             tag, |             tag, | ||||||
|             isComponent, |             isComponent, | ||||||
| @@ -212,8 +212,6 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | |||||||
|             state, |             state, | ||||||
|             value: attributeValue, |             value: attributeValue, | ||||||
|           }); |           }); | ||||||
|           const argVal = (arg as t.StringLiteral)?.value; |  | ||||||
|           const propName = argVal || 'modelValue'; |  | ||||||
|  |  | ||||||
|           if (directiveName === 'slots') { |           if (directiveName === 'slots') { | ||||||
|             slots = attributeValue as Slots; |             slots = attributeValue as Slots; | ||||||
| @@ -221,52 +219,59 @@ const buildProps = (path: NodePath<t.JSXElement>, state: State) => { | |||||||
|           } |           } | ||||||
|           if (directive) { |           if (directive) { | ||||||
|             directives.push(t.arrayExpression(directive)); |             directives.push(t.arrayExpression(directive)); | ||||||
|           } else if (directiveName === 'model') { |  | ||||||
|             // must be v-model and is a component |  | ||||||
|             properties.push(t.objectProperty( |  | ||||||
|               arg || t.stringLiteral('modelValue'), |  | ||||||
|               value as any, |  | ||||||
|             )); |  | ||||||
|  |  | ||||||
|             dynamicPropNames.add(propName); |  | ||||||
|  |  | ||||||
|             if (modifiers.size) { |  | ||||||
|               properties.push(t.objectProperty( |  | ||||||
|                 t.stringLiteral(`${argVal || 'model'}Modifiers`), |  | ||||||
|                 t.objectExpression( |  | ||||||
|                   [...modifiers].map((modifier) => ( |  | ||||||
|                     t.objectProperty( |  | ||||||
|                       t.stringLiteral(modifier), |  | ||||||
|                       t.booleanLiteral(true), |  | ||||||
|                     ) |  | ||||||
|                   )), |  | ||||||
|                 ), |  | ||||||
|               )); |  | ||||||
|             } |  | ||||||
|           } else if (directiveName === 'html') { |           } else if (directiveName === 'html') { | ||||||
|             properties.push(t.objectProperty( |             properties.push(t.objectProperty( | ||||||
|               t.stringLiteral('innerHTML'), |               t.stringLiteral('innerHTML'), | ||||||
|               value as any, |               values[0] as any, | ||||||
|             )); |             )); | ||||||
|             dynamicPropNames.add('innerHTML'); |             dynamicPropNames.add('innerHTML'); | ||||||
|           } else if (directiveName === 'text') { |           } else if (directiveName === 'text') { | ||||||
|             properties.push(t.objectProperty( |             properties.push(t.objectProperty( | ||||||
|               t.stringLiteral('textContent'), |               t.stringLiteral('textContent'), | ||||||
|               value as any, |               values[0] as any, | ||||||
|             )); |             )); | ||||||
|             dynamicPropNames.add('textContent'); |             dynamicPropNames.add('textContent'); | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           if (directiveName === 'model' && value) { |           if (['models', 'model'].includes(directiveName)) { | ||||||
|             properties.push(t.objectProperty( |             values.forEach((value, index) => { | ||||||
|               t.stringLiteral(`onUpdate:${propName}`), |               const argVal = args[index].value; | ||||||
|               t.arrowFunctionExpression( |               const propName = argVal === 'model' ? 'modelValue' : argVal; | ||||||
|                 [t.identifier('$event')], |  | ||||||
|                 t.assignmentExpression('=', value as any, t.identifier('$event')), |  | ||||||
|               ), |  | ||||||
|             )); |  | ||||||
|  |  | ||||||
|             dynamicPropNames.add(`onUpdate:${propName}`); |               // must be v-model or v-models and is a component | ||||||
|  |               if (!directive) { | ||||||
|  |                 properties.push( | ||||||
|  |                   t.objectProperty(t.stringLiteral(propName), value as any), | ||||||
|  |                 ); | ||||||
|  |                 dynamicPropNames.add(propName); | ||||||
|  |  | ||||||
|  |                 if (modifiers[index]?.size) { | ||||||
|  |                   properties.push( | ||||||
|  |                     t.objectProperty( | ||||||
|  |                       t.stringLiteral(`${argVal}Modifiers`), | ||||||
|  |                       t.objectExpression( | ||||||
|  |                         [...modifiers[index]].map((modifier) => t.objectProperty( | ||||||
|  |                           t.stringLiteral(modifier), | ||||||
|  |                           t.booleanLiteral(true), | ||||||
|  |                         )), | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                   ); | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |  | ||||||
|  |               properties.push( | ||||||
|  |                 t.objectProperty( | ||||||
|  |                   t.stringLiteral(`onUpdate:${propName}`), | ||||||
|  |                   t.arrowFunctionExpression( | ||||||
|  |                     [t.identifier('$event')], | ||||||
|  |                     t.assignmentExpression('=', value as any, t.identifier('$event')), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ); | ||||||
|  |  | ||||||
|  |               dynamicPropNames.add(`onUpdate:${propName}`); | ||||||
|  |             }); | ||||||
|           } |           } | ||||||
|         } else { |         } else { | ||||||
|           if (name.match(xlinkRE)) { |           if (name.match(xlinkRE)) { | ||||||
|   | |||||||
| @@ -24,16 +24,14 @@ const getType = (path: NodePath<t.JSXOpeningElement>) => { | |||||||
|   return typePath ? typePath.get('value').node : null; |   return typePath ? typePath.get('value').node : null; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const parseModifiers = (value: t.Expression) => { | const parseModifiers = (value: t.ArrayExpression): string[] => ( | ||||||
|   let modifiers: string[] = []; |   t.isArrayExpression(value) | ||||||
|   if (t.isArrayExpression(value)) { |     ? value.elements | ||||||
|     modifiers = value.elements |       .map((el) => (t.isStringLiteral(el) ? el.value : '')) | ||||||
|       .map((el) => (t.isStringLiteral(el) ? el.value : '')).filter(Boolean); |       .filter(Boolean) | ||||||
|   } |     : []); | ||||||
|   return modifiers; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const parseDirectives = (args: { | const parseDirectives = (params: { | ||||||
|   name: string, |   name: string, | ||||||
|   path: NodePath<t.JSXAttribute>, |   path: NodePath<t.JSXAttribute>, | ||||||
|   value: t.StringLiteral | t.Expression | null, |   value: t.StringLiteral | t.Expression | null, | ||||||
| @@ -43,48 +41,75 @@ const parseDirectives = (args: { | |||||||
| }) => { | }) => { | ||||||
|   const { |   const { | ||||||
|     name, path, value, state, tag, isComponent, |     name, path, value, state, tag, isComponent, | ||||||
|   } = args; |   } = params; | ||||||
|   let modifiers: string[] = name.split('_'); |   const args: t.StringLiteral[] = []; | ||||||
|   let arg; |   const vals: t.Expression[] = []; | ||||||
|   let val; |   const modifiersSet: Set<string>[] = []; | ||||||
|  |   const underscoreModifiers = name.split('_'); | ||||||
|   const directiveName: string = modifiers.shift() |   const directiveName: string = underscoreModifiers.shift() | ||||||
|     ?.replace(/^v/, '') |     ?.replace(/^v/, '') | ||||||
|     .replace(/^-/, '') |     .replace(/^-/, '') | ||||||
|     .replace(/^\S/, (s: string) => s.toLowerCase()) || ''; |     .replace(/^\S/, (s: string) => s.toLowerCase()) || ''; | ||||||
|  |  | ||||||
|   if (directiveName === 'model' && !t.isJSXExpressionContainer(path.get('value'))) { |   const isVModels = directiveName === 'models'; | ||||||
|  |   const isVModel = directiveName === 'model'; | ||||||
|  |   if (isVModel && !t.isJSXExpressionContainer(path.get('value'))) { | ||||||
|     throw new Error('You have to use JSX Expression inside your v-model'); |     throw new Error('You have to use JSX Expression inside your v-model'); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const shouldResolve = !['html', 'text', 'model'].includes(directiveName) |   if (isVModels && !isComponent) { | ||||||
|     || (directiveName === 'model' && !isComponent); |     throw new Error('v-models can only use in custom components'); | ||||||
|  |  | ||||||
|   if (t.isArrayExpression(value)) { |  | ||||||
|     const { elements } = value; |  | ||||||
|     const [first, second, third] = elements; |  | ||||||
|     if (t.isStringLiteral(second)) { |  | ||||||
|       arg = second; |  | ||||||
|       modifiers = parseModifiers(third as t.Expression); |  | ||||||
|     } else if (second) { |  | ||||||
|       modifiers = parseModifiers(second as t.Expression); |  | ||||||
|     } |  | ||||||
|     val = first; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const modifiersSet = new Set(modifiers); |   const shouldResolve = !['html', 'text', 'model', 'models'].includes(directiveName) | ||||||
|  |     || (isVModel && !isComponent); | ||||||
|  |  | ||||||
|  |   if (['models', 'model'].includes(directiveName)) { | ||||||
|  |     if (t.isArrayExpression(value)) { | ||||||
|  |       const elementsList = isVModels ? value.elements! : [value]; | ||||||
|  |  | ||||||
|  |       elementsList.forEach((element) => { | ||||||
|  |         if (isVModels && !t.isArrayExpression(element)) { | ||||||
|  |           throw new Error('You should pass a Two-dimensional Arrays to v-models'); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const { elements } = element as t.ArrayExpression; | ||||||
|  |         const [first, second, third] = elements; | ||||||
|  |         let modifiers = underscoreModifiers; | ||||||
|  |  | ||||||
|  |         if (t.isStringLiteral(second)) { | ||||||
|  |           args.push(second); | ||||||
|  |           modifiers = parseModifiers(third as t.ArrayExpression); | ||||||
|  |         } else if (t.isArrayExpression(second)) { | ||||||
|  |           args.push(t.stringLiteral('model')); | ||||||
|  |           modifiers = parseModifiers(second); | ||||||
|  |         } else { | ||||||
|  |           // work as v-model={[value]} or v-models={[[value]]} | ||||||
|  |           args.push(t.stringLiteral('model')); | ||||||
|  |         } | ||||||
|  |         modifiersSet.push(new Set(modifiers)); | ||||||
|  |         vals.push(first as t.Expression); | ||||||
|  |       }); | ||||||
|  |     } else if (isVModel) { | ||||||
|  |       // work as v-model={value} | ||||||
|  |       args.push(t.stringLiteral('model')); | ||||||
|  |       modifiersSet.push(new Set(underscoreModifiers)); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     modifiersSet.push(new Set(underscoreModifiers)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
|     directiveName, |     directiveName, | ||||||
|     modifiers: modifiersSet, |     modifiers: modifiersSet, | ||||||
|     value: val || value, |     values: vals.length ? vals : [value], | ||||||
|     arg, |     args, | ||||||
|     directive: shouldResolve ? [ |     directive: shouldResolve ? [ | ||||||
|       resolveDirective(path, state, tag, directiveName), |       resolveDirective(path, state, tag, directiveName), | ||||||
|       val || value, |       vals[0] || value, | ||||||
|       !!modifiersSet.size && t.unaryExpression('void', t.numericLiteral(0), true), |       !!modifiersSet[0]?.size && t.unaryExpression('void', t.numericLiteral(0), true), | ||||||
|       !!modifiersSet.size && t.objectExpression( |       !!modifiersSet[0]?.size && t.objectExpression( | ||||||
|         [...modifiersSet].map( |         [...modifiersSet[0]].map( | ||||||
|           (modifier) => t.objectProperty( |           (modifier) => t.objectProperty( | ||||||
|             t.identifier(modifier), |             t.identifier(modifier), | ||||||
|             t.booleanLiteral(true), |             t.booleanLiteral(true), | ||||||
|   | |||||||
| @@ -171,6 +171,23 @@ createVNode(\\"h1\\", { | |||||||
| }, null, 8, [\\"innerHTML\\"]);" | }, null, 8, [\\"innerHTML\\"]);" | ||||||
| `; | `; | ||||||
|  |  | ||||||
|  | exports[`vModels: vModels 1`] = ` | ||||||
|  | "import { resolveComponent, createVNode } from \\"vue\\"; | ||||||
|  | createVNode(resolveComponent(\\"C\\"), { | ||||||
|  |   \\"modelValue\\": foo, | ||||||
|  |   \\"modelModifiers\\": { | ||||||
|  |     \\"modifier\\": true | ||||||
|  |   }, | ||||||
|  |   \\"onUpdate:modelValue\\": $event => foo = $event, | ||||||
|  |   \\"bar\\": bar, | ||||||
|  |   \\"barModifiers\\": { | ||||||
|  |     \\"modifier1\\": true, | ||||||
|  |     \\"modifier2\\": true | ||||||
|  |   }, | ||||||
|  |   \\"onUpdate:bar\\": $event => bar = $event | ||||||
|  | }, null, 8, [\\"modelValue\\", \\"onUpdate:modelValue\\", \\"bar\\", \\"onUpdate:bar\\"]);" | ||||||
|  | `; | ||||||
|  |  | ||||||
| exports[`vText: vText 1`] = ` | exports[`vText: vText 1`] = ` | ||||||
| "import { createVNode } from \\"vue\\"; | "import { createVNode } from \\"vue\\"; | ||||||
| createVNode(\\"div\\", { | createVNode(\\"div\\", { | ||||||
|   | |||||||
| @@ -134,6 +134,10 @@ const tests = [ | |||||||
|       a = <A>{a}</A>; |       a = <A>{a}</A>; | ||||||
|     `, |     `, | ||||||
|   }, |   }, | ||||||
|  |   { | ||||||
|  |     name: 'vModels', | ||||||
|  |     from: '<C v-models={[[foo, ["modifier"]], [bar, "bar", ["modifier1", "modifier2"]]]} />', | ||||||
|  |   }, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| tests.forEach(( | tests.forEach(( | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import { shallowMount } from '@vue/test-utils'; | import { shallowMount, mount } from '@vue/test-utils'; | ||||||
| import { VNode } from '@vue/runtime-dom'; | import { defineComponent, VNode } from '@vue/runtime-dom'; | ||||||
|  |  | ||||||
| test('input[type="checkbox"] should work', async () => { | test('input[type="checkbox"] should work', async () => { | ||||||
|   const wrapper = shallowMount({ |   const wrapper = shallowMount({ | ||||||
| @@ -164,3 +164,68 @@ test('dynamic type should work', async () => { | |||||||
|   await wrapper.vm.$nextTick(); |   await wrapper.vm.$nextTick(); | ||||||
|   expect(wrapper.vm.$el.checked).toBe(false); |   expect(wrapper.vm.$el.checked).toBe(false); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | test('underscore modifier should work', async () => { | ||||||
|  |   const wrapper = shallowMount({ | ||||||
|  |     data: () => ({ | ||||||
|  |       test: 'b', | ||||||
|  |     }), | ||||||
|  |     render() { | ||||||
|  |       return <input v-model_lazy={this.test} />; | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |   const el = wrapper.vm.$el; | ||||||
|  |  | ||||||
|  |   expect(el.value).toBe('b'); | ||||||
|  |   expect(wrapper.vm.test).toBe('b'); | ||||||
|  |   el.value = 'c'; | ||||||
|  |   await wrapper.trigger('input'); | ||||||
|  |   expect(wrapper.vm.test).toBe('b'); | ||||||
|  |   el.value = 'c'; | ||||||
|  |   await wrapper.trigger('change'); | ||||||
|  |   expect(wrapper.vm.test).toBe('c'); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | test('underscore modifier should work in custom component', async () => { | ||||||
|  |   const Child = defineComponent({ | ||||||
|  |     props: { | ||||||
|  |       modelValue: { | ||||||
|  |         type: Number, | ||||||
|  |         default: 0, | ||||||
|  |       }, | ||||||
|  |       modelModifiers: { | ||||||
|  |         default: () => ({ double: false }), | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     setup(props, { emit }) { | ||||||
|  |       const handleClick = () => { | ||||||
|  |         emit('update:modelValue', 3); | ||||||
|  |       }; | ||||||
|  |       return () => ( | ||||||
|  |         <div onClick={handleClick}> | ||||||
|  |           {props.modelModifiers.double | ||||||
|  |             ? props.modelValue * 2 | ||||||
|  |             : props.modelValue} | ||||||
|  |         </div> | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const wrapper = mount({ | ||||||
|  |     data() { | ||||||
|  |       return { | ||||||
|  |         foo: 1, | ||||||
|  |       }; | ||||||
|  |     }, | ||||||
|  |     render() { | ||||||
|  |       return <Child v-model_double={this.foo} />; | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   expect(wrapper.html()).toBe('<div>2</div>'); | ||||||
|  |   wrapper.vm.$data.foo += 1; | ||||||
|  |   await wrapper.vm.$nextTick(); | ||||||
|  |   expect(wrapper.html()).toBe('<div>4</div>'); | ||||||
|  |   await wrapper.trigger('click'); | ||||||
|  |   expect(wrapper.html()).toBe('<div>6</div>'); | ||||||
|  | }); | ||||||
|   | |||||||
							
								
								
									
										123
									
								
								packages/babel-plugin-jsx/test/v-models.test.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								packages/babel-plugin-jsx/test/v-models.test.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | import { mount } from '@vue/test-utils'; | ||||||
|  | import { defineComponent } from 'vue'; | ||||||
|  |  | ||||||
|  | test('single value binding should work', async () => { | ||||||
|  |   const Child = defineComponent({ | ||||||
|  |     props: { | ||||||
|  |       foo: Number, | ||||||
|  |     }, | ||||||
|  |     setup(props, { emit }) { | ||||||
|  |       const handleClick = () => { | ||||||
|  |         emit('update:foo', 3); | ||||||
|  |       }; | ||||||
|  |       return () => <div onClick={handleClick}>{props.foo}</div>; | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const wrapper = mount({ | ||||||
|  |     data() { | ||||||
|  |       return { | ||||||
|  |         foo: 1, | ||||||
|  |       }; | ||||||
|  |     }, | ||||||
|  |     render() { | ||||||
|  |       return <Child v-models={[[this.foo, 'foo']]} />; | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   expect(wrapper.html()).toBe('<div>1</div>'); | ||||||
|  |   wrapper.vm.$data.foo += 1; | ||||||
|  |   await wrapper.vm.$nextTick(); | ||||||
|  |   expect(wrapper.html()).toBe('<div>2</div>'); | ||||||
|  |   await wrapper.trigger('click'); | ||||||
|  |   expect(wrapper.html()).toBe('<div>3</div>'); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | test('multiple values binding should work', async () => { | ||||||
|  |   const Child = defineComponent({ | ||||||
|  |     props: { | ||||||
|  |       foo: Number, | ||||||
|  |       bar: Number, | ||||||
|  |     }, | ||||||
|  |     setup(props, { emit }) { | ||||||
|  |       const handleClick = () => { | ||||||
|  |         emit('update:foo', 3); | ||||||
|  |         emit('update:bar', 2); | ||||||
|  |       }; | ||||||
|  |       return () => ( | ||||||
|  |         <div onClick={handleClick}> | ||||||
|  |           {props.foo},{props.bar} | ||||||
|  |         </div> | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const wrapper = mount({ | ||||||
|  |     data() { | ||||||
|  |       return { | ||||||
|  |         foo: 1, | ||||||
|  |         bar: 0, | ||||||
|  |       }; | ||||||
|  |     }, | ||||||
|  |     render() { | ||||||
|  |       return ( | ||||||
|  |         <Child | ||||||
|  |           v-models={[ | ||||||
|  |             [this.foo, 'foo'], | ||||||
|  |             [this.bar, 'bar'], | ||||||
|  |           ]} | ||||||
|  |         /> | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   expect(wrapper.html()).toBe('<div>1,0</div>'); | ||||||
|  |   wrapper.vm.$data.foo += 1; | ||||||
|  |   wrapper.vm.$data.bar += 1; | ||||||
|  |   await wrapper.vm.$nextTick(); | ||||||
|  |   expect(wrapper.html()).toBe('<div>2,1</div>'); | ||||||
|  |   await wrapper.trigger('click'); | ||||||
|  |   expect(wrapper.html()).toBe('<div>3,2</div>'); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | test('modifier should work', async () => { | ||||||
|  |   const Child = defineComponent({ | ||||||
|  |     props: { | ||||||
|  |       foo: { | ||||||
|  |         type: Number, | ||||||
|  |         default: 0, | ||||||
|  |       }, | ||||||
|  |       fooModifiers: { | ||||||
|  |         default: () => ({ double: false }), | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|  |     setup(props, { emit }) { | ||||||
|  |       const handleClick = () => { | ||||||
|  |         emit('update:foo', 3); | ||||||
|  |       }; | ||||||
|  |       return () => ( | ||||||
|  |         <div onClick={handleClick}> | ||||||
|  |           {props.fooModifiers.double ? props.foo * 2 : props.foo} | ||||||
|  |         </div> | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const wrapper = mount({ | ||||||
|  |     data() { | ||||||
|  |       return { | ||||||
|  |         foo: 1, | ||||||
|  |       }; | ||||||
|  |     }, | ||||||
|  |     render() { | ||||||
|  |       return <Child v-models={[[this.foo, 'foo', ['double']]]} />; | ||||||
|  |     }, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   expect(wrapper.html()).toBe('<div>2</div>'); | ||||||
|  |   wrapper.vm.$data.foo += 1; | ||||||
|  |   await wrapper.vm.$nextTick(); | ||||||
|  |   expect(wrapper.html()).toBe('<div>4</div>'); | ||||||
|  |   await wrapper.trigger('click'); | ||||||
|  |   expect(wrapper.html()).toBe('<div>6</div>'); | ||||||
|  | }); | ||||||
		Reference in New Issue
	
	Block a user