1import { parse } from 'compiler/parser/index'2import { extend } from 'shared/util'3import { baseOptions } from 'web/compiler/options'4import { isIE, isEdge } from 'core/util/env'56describe('parser', () => {7 it('simple element', () => {8 const ast = parse('<h1>hello world</h1>', baseOptions)9 expect(ast.tag).toBe('h1')10 expect(ast.plain).toBe(true)11 expect(ast.children[0].text).toBe('hello world')12 })1314 it('interpolation in element', () => {15 const ast = parse('<h1>{{msg}}</h1>', baseOptions)16 expect(ast.tag).toBe('h1')17 expect(ast.plain).toBe(true)18 expect(ast.children[0].expression).toBe('_s(msg)')19 })2021 it('child elements', () => {22 const ast = parse('<ul><li>hello world</li></ul>', baseOptions)23 expect(ast.tag).toBe('ul')24 expect(ast.plain).toBe(true)25 expect(ast.children[0].tag).toBe('li')26 expect(ast.children[0].plain).toBe(true)27 expect(ast.children[0].children[0].text).toBe('hello world')28 expect(ast.children[0].parent).toBe(ast)29 })3031 it('unary element', () => {32 const ast = parse('<hr>', baseOptions)33 expect(ast.tag).toBe('hr')34 expect(ast.plain).toBe(true)35 expect(ast.children.length).toBe(0)36 })3738 it('svg element', () => {39 const ast = parse('<svg><text>hello world</text></svg>', baseOptions)40 expect(ast.tag).toBe('svg')41 expect(ast.ns).toBe('svg')42 expect(ast.plain).toBe(true)43 expect(ast.children[0].tag).toBe('text')44 expect(ast.children[0].children[0].text).toBe('hello world')45 expect(ast.children[0].parent).toBe(ast)46 })4748 it('camelCase element', () => {49 const ast = parse(50 '<MyComponent><p>hello world</p></MyComponent>',51 baseOptions52 )53 expect(ast.tag).toBe('MyComponent')54 expect(ast.plain).toBe(true)55 expect(ast.children[0].tag).toBe('p')56 expect(ast.children[0].plain).toBe(true)57 expect(ast.children[0].children[0].text).toBe('hello world')58 expect(ast.children[0].parent).toBe(ast)59 })6061 it('forbidden element', () => {62 // style63 const styleAst = parse('<style>error { color: red; }</style>', baseOptions)64 expect(styleAst.tag).toBe('style')65 expect(styleAst.plain).toBe(true)66 expect(styleAst.forbidden).toBe(true)67 expect(styleAst.children[0].text).toBe('error { color: red; }')68 expect(69 'Templates should only be responsible for mapping the state'70 ).toHaveBeenWarned()71 // script72 const scriptAst = parse(73 '<script type="text/javascript">alert("hello world!")</script>',74 baseOptions75 )76 expect(scriptAst.tag).toBe('script')77 expect(scriptAst.plain).toBe(false)78 expect(scriptAst.forbidden).toBe(true)79 expect(scriptAst.children[0].text).toBe('alert("hello world!")')80 expect(81 'Templates should only be responsible for mapping the state'82 ).toHaveBeenWarned()83 })8485 it('not contain root element', () => {86 parse('hello world', baseOptions)87 expect(88 'Component template requires a root element, rather than just text'89 ).toHaveBeenWarned()90 })9192 it('warn text before root element', () => {93 parse('before root {{ interpolation }}<div></div>', baseOptions)94 expect(95 'text "before root {{ interpolation }}" outside root element will be ignored.'96 ).toHaveBeenWarned()97 })9899 it('warn text after root element', () => {100 parse('<div></div>after root {{ interpolation }}', baseOptions)101 expect(102 'text "after root {{ interpolation }}" outside root element will be ignored.'103 ).toHaveBeenWarned()104 })105106 it('warn multiple root elements', () => {107 parse('<div></div><div></div>', baseOptions)108 expect(109 'Component template should contain exactly one root element'110 ).toHaveBeenWarned()111 })112113 it('remove duplicate whitespace text nodes caused by comments', () => {114 const ast = parse(`<div><a></a> <!----> <a></a></div>`, baseOptions)115 expect(ast.children.length).toBe(3)116 expect(ast.children[0].tag).toBe('a')117 expect(ast.children[1].text).toBe(' ')118 expect(ast.children[2].tag).toBe('a')119 })120121 it('remove text nodes between v-if conditions', () => {122 const ast = parse(123 `<div><div v-if="1"></div> <div v-else-if="2"></div> <div v-else></div> <span></span></div>`,124 baseOptions125 )126 expect(ast.children.length).toBe(3)127 expect(ast.children[0].tag).toBe('div')128 expect(ast.children[0].ifConditions.length).toBe(3)129 expect(ast.children[1].text).toBe(' ') // text130 expect(ast.children[2].tag).toBe('span')131 })132133 it('warn non whitespace text between v-if conditions', () => {134 parse(`<div><div v-if="1"></div> foo <div v-else></div></div>`, baseOptions)135 expect(136 `text "foo" between v-if and v-else(-if) will be ignored`137 ).toHaveBeenWarned()138 })139140 it('not warn 2 root elements with v-if and v-else', () => {141 parse('<div v-if="1"></div><div v-else></div>', baseOptions)142 expect(143 'Component template should contain exactly one root element'144 ).not.toHaveBeenWarned()145 })146147 it('not warn 3 root elements with v-if, v-else-if and v-else', () => {148 parse(149 '<div v-if="1"></div><div v-else-if="2"></div><div v-else></div>',150 baseOptions151 )152 expect(153 'Component template should contain exactly one root element'154 ).not.toHaveBeenWarned()155 })156157 it('not warn 2 root elements with v-if and v-else on separate lines', () => {158 parse(159 `160 <div v-if="1"></div>161 <div v-else></div>162 `,163 baseOptions164 )165 expect(166 'Component template should contain exactly one root element'167 ).not.toHaveBeenWarned()168 })169170 it('not warn 3 or more root elements with v-if, v-else-if and v-else on separate lines', () => {171 parse(172 `173 <div v-if="1"></div>174 <div v-else-if="2"></div>175 <div v-else></div>176 `,177 baseOptions178 )179 expect(180 'Component template should contain exactly one root element'181 ).not.toHaveBeenWarned()182183 parse(184 `185 <div v-if="1"></div>186 <div v-else-if="2"></div>187 <div v-else-if="3"></div>188 <div v-else-if="4"></div>189 <div v-else></div>190 `,191 baseOptions192 )193 expect(194 'Component template should contain exactly one root element'195 ).not.toHaveBeenWarned()196 })197198 it('generate correct ast for 2 root elements with v-if and v-else on separate lines', () => {199 const ast = parse(200 `201 <div v-if="1"></div>202 <p v-else></p>203 `,204 baseOptions205 )206 expect(ast.tag).toBe('div')207 expect(ast.ifConditions[1].block.tag).toBe('p')208 })209210 it('generate correct ast for 3 or more root elements with v-if and v-else on separate lines', () => {211 const ast = parse(212 `213 <div v-if="1"></div>214 <span v-else-if="2"></span>215 <p v-else></p>216 `,217 baseOptions218 )219 expect(ast.tag).toBe('div')220 expect(ast.ifConditions[0].block.tag).toBe('div')221 expect(ast.ifConditions[1].block.tag).toBe('span')222 expect(ast.ifConditions[2].block.tag).toBe('p')223224 const astMore = parse(225 `226 <div v-if="1"></div>227 <span v-else-if="2"></span>228 <div v-else-if="3"></div>229 <span v-else-if="4"></span>230 <p v-else></p>231 `,232 baseOptions233 )234 expect(astMore.tag).toBe('div')235 expect(astMore.ifConditions[0].block.tag).toBe('div')236 expect(astMore.ifConditions[1].block.tag).toBe('span')237 expect(astMore.ifConditions[2].block.tag).toBe('div')238 expect(astMore.ifConditions[3].block.tag).toBe('span')239 expect(astMore.ifConditions[4].block.tag).toBe('p')240 })241242 it('warn 2 root elements with v-if', () => {243 parse('<div v-if="1"></div><div v-if="2"></div>', baseOptions)244 expect(245 'Component template should contain exactly one root element'246 ).toHaveBeenWarned()247 })248249 it('warn 3 root elements with v-if and v-else on first 2', () => {250 parse('<div v-if="1"></div><div v-else></div><div></div>', baseOptions)251 expect(252 'Component template should contain exactly one root element'253 ).toHaveBeenWarned()254 })255256 it('warn 3 root elements with v-if and v-else-if on first 2', () => {257 parse('<div v-if="1"></div><div v-else-if></div><div></div>', baseOptions)258 expect(259 'Component template should contain exactly one root element'260 ).toHaveBeenWarned()261 })262263 it('warn 4 root elements with v-if, v-else-if and v-else on first 2', () => {264 parse(265 '<div v-if="1"></div><div v-else-if></div><div v-else></div><div></div>',266 baseOptions267 )268 expect(269 'Component template should contain exactly one root element'270 ).toHaveBeenWarned()271 })272273 it('warn 2 root elements with v-if and v-else with v-for on 2nd', () => {274 parse(275 '<div v-if="1"></div><div v-else v-for="i in [1]"></div>',276 baseOptions277 )278 expect(279 'Cannot use v-for on stateful component root element because it renders multiple elements'280 ).toHaveBeenWarned()281 })282283 it('warn 2 root elements with v-if and v-else-if with v-for on 2nd', () => {284 parse(285 '<div v-if="1"></div><div v-else-if="2" v-for="i in [1]"></div>',286 baseOptions287 )288 expect(289 'Cannot use v-for on stateful component root element because it renders multiple elements'290 ).toHaveBeenWarned()291 })292293 it('warn <template> as root element', () => {294 parse('<template></template>', baseOptions)295 expect('Cannot use <template> as component root element').toHaveBeenWarned()296 })297298 it('warn <slot> as root element', () => {299 parse('<slot></slot>', baseOptions)300 expect('Cannot use <slot> as component root element').toHaveBeenWarned()301 })302303 it('warn v-for on root element', () => {304 parse('<div v-for="item in items"></div>', baseOptions)305 expect(306 'Cannot use v-for on stateful component root element'307 ).toHaveBeenWarned()308 })309310 it('warn <template> key', () => {311 parse(312 '<div><template v-for="i in 10" :key="i"></template></div>',313 baseOptions314 )315 expect('<template> cannot be keyed').toHaveBeenWarned()316 })317318 it('warn the child of the <transition-group> component has sequential index', () => {319 parse(320 `321 <div>322 <transition-group>323 <i v-for="(o, i) of arr" :key="i"></i>324 </transition-group>325 </div>326 `,327 baseOptions328 )329 expect(330 'Do not use v-for index as key on <transition-group> children'331 ).toHaveBeenWarned()332 })333334 it('v-pre directive', () => {335 const ast = parse(336 '<div v-pre id="message1"><p>{{msg}}</p></div>',337 baseOptions338 )339 expect(ast.pre).toBe(true)340 expect(ast.attrs[0].name).toBe('id')341 expect(ast.attrs[0].value).toBe('"message1"')342 expect(ast.children[0].children[0].text).toBe('{{msg}}')343 })344345 it('v-pre directive should leave template in DOM', () => {346 const ast = parse(347 '<div v-pre id="message1"><template id="template1"><p>{{msg}}</p></template></div>',348 baseOptions349 )350 expect(ast.pre).toBe(true)351 expect(ast.attrs[0].name).toBe('id')352 expect(ast.attrs[0].value).toBe('"message1"')353 expect(ast.children[0].attrs[0].name).toBe('id')354 expect(ast.children[0].attrs[0].value).toBe('"template1"')355 })356357 it('v-for directive basic syntax', () => {358 const ast = parse('<ul><li v-for="item in items"></li></ul>', baseOptions)359 const liAst = ast.children[0]360 expect(liAst.for).toBe('items')361 expect(liAst.alias).toBe('item')362 })363364 it('v-for directive iteration syntax', () => {365 const ast = parse(366 '<ul><li v-for="(item, index) in items"></li></ul>',367 baseOptions368 )369 const liAst = ast.children[0]370 expect(liAst.for).toBe('items')371 expect(liAst.alias).toBe('item')372 expect(liAst.iterator1).toBe('index')373 expect(liAst.iterator2).toBeUndefined()374 })375376 it('v-for directive iteration syntax (multiple)', () => {377 const ast = parse(378 '<ul><li v-for="(item, key, index) in items"></li></ul>',379 baseOptions380 )381 const liAst = ast.children[0]382 expect(liAst.for).toBe('items')383 expect(liAst.alias).toBe('item')384 expect(liAst.iterator1).toBe('key')385 expect(liAst.iterator2).toBe('index')386 })387388 it('v-for directive key', () => {389 const ast = parse(390 '<ul><li v-for="item in items" :key="item.uid"></li></ul>',391 baseOptions392 )393 const liAst = ast.children[0]394 expect(liAst.for).toBe('items')395 expect(liAst.alias).toBe('item')396 expect(liAst.key).toBe('item.uid')397 })398399 it('v-for directive destructuring', () => {400 let ast = parse('<ul><li v-for="{ foo } in items"></li></ul>', baseOptions)401 let liAst = ast.children[0]402 expect(liAst.for).toBe('items')403 expect(liAst.alias).toBe('{ foo }')404405 // with paren406 ast = parse('<ul><li v-for="({ foo }) in items"></li></ul>', baseOptions)407 liAst = ast.children[0]408 expect(liAst.for).toBe('items')409 expect(liAst.alias).toBe('{ foo }')410411 // multi-var destructuring412 ast = parse(413 '<ul><li v-for="{ foo, bar, baz } in items"></li></ul>',414 baseOptions415 )416 liAst = ast.children[0]417 expect(liAst.for).toBe('items')418 expect(liAst.alias).toBe('{ foo, bar, baz }')419420 // multi-var destructuring with paren421 ast = parse(422 '<ul><li v-for="({ foo, bar, baz }) in items"></li></ul>',423 baseOptions424 )425 liAst = ast.children[0]426 expect(liAst.for).toBe('items')427 expect(liAst.alias).toBe('{ foo, bar, baz }')428429 // with index430 ast = parse('<ul><li v-for="({ foo }, i) in items"></li></ul>', baseOptions)431 liAst = ast.children[0]432 expect(liAst.for).toBe('items')433 expect(liAst.alias).toBe('{ foo }')434 expect(liAst.iterator1).toBe('i')435436 // with key + index437 ast = parse(438 '<ul><li v-for="({ foo }, i, j) in items"></li></ul>',439 baseOptions440 )441 liAst = ast.children[0]442 expect(liAst.for).toBe('items')443 expect(liAst.alias).toBe('{ foo }')444 expect(liAst.iterator1).toBe('i')445 expect(liAst.iterator2).toBe('j')446447 // multi-var destructuring with index448 ast = parse(449 '<ul><li v-for="({ foo, bar, baz }, i) in items"></li></ul>',450 baseOptions451 )452 liAst = ast.children[0]453 expect(liAst.for).toBe('items')454 expect(liAst.alias).toBe('{ foo, bar, baz }')455 expect(liAst.iterator1).toBe('i')456457 // array458 ast = parse('<ul><li v-for="[ foo ] in items"></li></ul>', baseOptions)459 liAst = ast.children[0]460 expect(liAst.for).toBe('items')461 expect(liAst.alias).toBe('[ foo ]')462463 // multi-array464 ast = parse(465 '<ul><li v-for="[ foo, bar, baz ] in items"></li></ul>',466 baseOptions467 )468 liAst = ast.children[0]469 expect(liAst.for).toBe('items')470 expect(liAst.alias).toBe('[ foo, bar, baz ]')471472 // array with paren473 ast = parse('<ul><li v-for="([ foo ]) in items"></li></ul>', baseOptions)474 liAst = ast.children[0]475 expect(liAst.for).toBe('items')476 expect(liAst.alias).toBe('[ foo ]')477478 // multi-array with paren479 ast = parse(480 '<ul><li v-for="([ foo, bar, baz ]) in items"></li></ul>',481 baseOptions482 )483 liAst = ast.children[0]484 expect(liAst.for).toBe('items')485 expect(liAst.alias).toBe('[ foo, bar, baz ]')486487 // array with index488 ast = parse('<ul><li v-for="([ foo ], i) in items"></li></ul>', baseOptions)489 liAst = ast.children[0]490 expect(liAst.for).toBe('items')491 expect(liAst.alias).toBe('[ foo ]')492 expect(liAst.iterator1).toBe('i')493494 // array with key + index495 ast = parse(496 '<ul><li v-for="([ foo ], i, j) in items"></li></ul>',497 baseOptions498 )499 liAst = ast.children[0]500 expect(liAst.for).toBe('items')501 expect(liAst.alias).toBe('[ foo ]')502 expect(liAst.iterator1).toBe('i')503 expect(liAst.iterator2).toBe('j')504505 // multi-array with paren506 ast = parse(507 '<ul><li v-for="([ foo, bar, baz ]) in items"></li></ul>',508 baseOptions509 )510 liAst = ast.children[0]511 expect(liAst.for).toBe('items')512 expect(liAst.alias).toBe('[ foo, bar, baz ]')513514 // multi-array with index515 ast = parse(516 '<ul><li v-for="([ foo, bar, baz ], i) in items"></li></ul>',517 baseOptions518 )519 liAst = ast.children[0]520 expect(liAst.for).toBe('items')521 expect(liAst.alias).toBe('[ foo, bar, baz ]')522 expect(liAst.iterator1).toBe('i')523524 // nested525 ast = parse(526 '<ul><li v-for="({ foo, bar: { baz }, qux: [ n ] }, i, j) in items"></li></ul>',527 baseOptions528 )529 liAst = ast.children[0]530 expect(liAst.for).toBe('items')531 expect(liAst.alias).toBe('{ foo, bar: { baz }, qux: [ n ] }')532 expect(liAst.iterator1).toBe('i')533 expect(liAst.iterator2).toBe('j')534535 // array nested536 ast = parse(537 '<ul><li v-for="([ foo, { bar }, baz ], i, j) in items"></li></ul>',538 baseOptions539 )540 liAst = ast.children[0]541 expect(liAst.for).toBe('items')542 expect(liAst.alias).toBe('[ foo, { bar }, baz ]')543 expect(liAst.iterator1).toBe('i')544 expect(liAst.iterator2).toBe('j')545 })546547 it('v-for directive invalid syntax', () => {548 parse('<ul><li v-for="item into items"></li></ul>', baseOptions)549 expect('Invalid v-for expression').toHaveBeenWarned()550 })551552 it('v-if directive syntax', () => {553 const ast = parse('<p v-if="show">hello world</p>', baseOptions)554 expect(ast.if).toBe('show')555 expect(ast.ifConditions[0].exp).toBe('show')556 })557558 it('v-else-if directive syntax', () => {559 const ast = parse(560 '<div><p v-if="show">hello</p><span v-else-if="2">elseif</span><p v-else>world</p></div>',561 baseOptions562 )563 const ifAst = ast.children[0]564 const conditionsAst = ifAst.ifConditions565 expect(conditionsAst.length).toBe(3)566 expect(conditionsAst[1].block.children[0].text).toBe('elseif')567 expect(conditionsAst[1].block.parent).toBe(ast)568 expect(conditionsAst[2].block.children[0].text).toBe('world')569 expect(conditionsAst[2].block.parent).toBe(ast)570 })571572 it('v-else directive syntax', () => {573 const ast = parse(574 '<div><p v-if="show">hello</p><p v-else>world</p></div>',575 baseOptions576 )577 const ifAst = ast.children[0]578 const conditionsAst = ifAst.ifConditions579 expect(conditionsAst.length).toBe(2)580 expect(conditionsAst[1].block.children[0].text).toBe('world')581 expect(conditionsAst[1].block.parent).toBe(ast)582 })583584 it('v-else-if directive invalid syntax', () => {585 parse('<div><p v-else-if="1">world</p></div>', baseOptions)586 expect('v-else-if="1" used on element').toHaveBeenWarned()587 })588589 it('v-else directive invalid syntax', () => {590 parse('<div><p v-else>world</p></div>', baseOptions)591 expect('v-else used on element').toHaveBeenWarned()592 })593594 it('v-once directive syntax', () => {595 const ast = parse('<p v-once>world</p>', baseOptions)596 expect(ast.once).toBe(true)597 })598599 it('slot tag single syntax', () => {600 const ast = parse('<div><slot></slot></div>', baseOptions)601 expect(ast.children[0].tag).toBe('slot')602 expect(ast.children[0].slotName).toBeUndefined()603 })604605 it('slot tag named syntax', () => {606 const ast = parse(607 '<div><slot name="one">hello world</slot></div>',608 baseOptions609 )610 expect(ast.children[0].tag).toBe('slot')611 expect(ast.children[0].slotName).toBe('"one"')612 })613614 it('slot target', () => {615 const ast = parse('<p slot="one">hello world</p>', baseOptions)616 expect(ast.slotTarget).toBe('"one"')617 })618619 it('component properties', () => {620 const ast = parse('<my-component :msg="hello"></my-component>', baseOptions)621 expect(ast.attrs[0].name).toBe('msg')622 expect(ast.attrs[0].value).toBe('hello')623 })624625 it('component "is" attribute', () => {626 const ast = parse(627 '<my-component is="component1"></my-component>',628 baseOptions629 )630 expect(ast.component).toBe('"component1"')631 })632633 it('component "inline-template" attribute', () => {634 const ast = parse(635 '<my-component inline-template>hello world</my-component>',636 baseOptions637 )638 expect(ast.inlineTemplate).toBe(true)639 })640641 it('class binding', () => {642 // static643 const ast1 = parse('<p class="class1">hello world</p>', baseOptions)644 expect(ast1.staticClass).toBe('"class1"')645 // dynamic646 const ast2 = parse('<p :class="class1">hello world</p>', baseOptions)647 expect(ast2.classBinding).toBe('class1')648 // interpolation warning649 parse('<p class="{{error}}">hello world</p>', baseOptions)650 expect(651 'Interpolation inside attributes has been removed'652 ).toHaveBeenWarned()653 })654655 it('style binding', () => {656 const ast = parse('<p :style="error">hello world</p>', baseOptions)657 expect(ast.styleBinding).toBe('error')658 })659660 it('attribute with v-bind', () => {661 const ast = parse(662 '<input type="text" name="field1" :value="msg">',663 baseOptions664 )665 expect(ast.attrsList[0].name).toBe('type')666 expect(ast.attrsList[0].value).toBe('text')667 expect(ast.attrsList[1].name).toBe('name')668 expect(ast.attrsList[1].value).toBe('field1')669 expect(ast.attrsMap['type']).toBe('text')670 expect(ast.attrsMap['name']).toBe('field1')671 expect(ast.attrs[0].name).toBe('type')672 expect(ast.attrs[0].value).toBe('"text"')673 expect(ast.attrs[1].name).toBe('name')674 expect(ast.attrs[1].value).toBe('"field1"')675 expect(ast.props[0].name).toBe('value')676 expect(ast.props[0].value).toBe('msg')677 })678679 it('empty v-bind expression', () => {680 parse('<div :empty-msg=""></div>', baseOptions)681 expect(682 'The value for a v-bind expression cannot be empty. Found in "v-bind:empty-msg"'683 ).toHaveBeenWarned()684 })685686 if (process.env.VBIND_PROP_SHORTHAND) {687 it('v-bind.prop shorthand syntax', () => {688 const ast = parse('<div .id="foo"></div>', baseOptions)689 expect(ast.props).toEqual([{ name: 'id', value: 'foo', dynamic: false }])690 })691692 it('v-bind.prop shorthand syntax w/ modifiers', () => {693 const ast = parse('<div .id.mod="foo"></div>', baseOptions)694 expect(ast.props).toEqual([{ name: 'id', value: 'foo', dynamic: false }])695 })696697 it('v-bind.prop shorthand dynamic argument', () => {698 const ast = parse('<div .[id]="foo"></div>', baseOptions)699 expect(ast.props).toEqual([{ name: 'id', value: 'foo', dynamic: true }])700 })701 }702703 // This only works for string templates.704 // In-DOM templates will be malformed before Vue can parse it.705 describe('parse and warn invalid dynamic arguments', () => {706 ;[707 `<div v-bind:['foo' + bar]="baz"/>`,708 `<div :['foo' + bar]="baz"/>`,709 `<div @['foo' + bar]="baz"/>`,710 `<foo #['foo' + bar]="baz"/>`,711 `<div :['foo' + bar].some.mod="baz"/>`712 ].forEach(template => {713 it(template, () => {714 const ast = parse(template, baseOptions)715 expect(`Invalid dynamic argument expression`).toHaveBeenWarned()716 })717 })718 })719720 // #9781721 it('multiple dynamic slot names without warning', () => {722 const ast = parse(723 `<my-component>724 <template #[foo]>foo</template>725 <template #[data]="scope">scope</template>726 <template #[bar]>bar</template>727 </my-component>`,728 baseOptions729 )730731 expect(`Invalid dynamic argument expression`).not.toHaveBeenWarned()732 expect(ast.scopedSlots.foo).not.toBeUndefined()733 expect(ast.scopedSlots.data).not.toBeUndefined()734 expect(ast.scopedSlots.bar).not.toBeUndefined()735 expect(ast.scopedSlots.foo.type).toBe(1)736 expect(ast.scopedSlots.data.type).toBe(1)737 expect(ast.scopedSlots.bar.type).toBe(1)738 expect(ast.scopedSlots.foo.attrsMap['#[foo]']).toBe('')739 expect(ast.scopedSlots.bar.attrsMap['#[bar]']).toBe('')740 expect(ast.scopedSlots.data.attrsMap['#[data]']).toBe('scope')741 })742743 // #6887744 it('special case static attribute that must be props', () => {745 const ast = parse('<video muted></video>', baseOptions)746 expect(ast.attrs[0].name).toBe('muted')747 expect(ast.attrs[0].value).toBe('""')748 expect(ast.props[0].name).toBe('muted')749 expect(ast.props[0].value).toBe('true')750 })751752 it('attribute with v-on', () => {753 const ast = parse(754 '<input type="text" name="field1" :value="msg" @input="onInput">',755 baseOptions756 )757 expect(ast.events.input.value).toBe('onInput')758 })759760 it('attribute with directive', () => {761 const ast = parse(762 '<input type="text" name="field1" :value="msg" v-validate:field1="required">',763 baseOptions764 )765 expect(ast.directives[0].name).toBe('validate')766 expect(ast.directives[0].value).toBe('required')767 expect(ast.directives[0].arg).toBe('field1')768 })769770 it('attribute with modified directive', () => {771 const ast = parse(772 '<input type="text" name="field1" :value="msg" v-validate.on.off>',773 baseOptions774 )775 expect(ast.directives[0].modifiers.on).toBe(true)776 expect(ast.directives[0].modifiers.off).toBe(true)777 })778779 it('literal attribute', () => {780 // basic781 const ast1 = parse(782 '<input type="text" name="field1" value="hello world">',783 baseOptions784 )785 expect(ast1.attrsList[0].name).toBe('type')786 expect(ast1.attrsList[0].value).toBe('text')787 expect(ast1.attrsList[1].name).toBe('name')788 expect(ast1.attrsList[1].value).toBe('field1')789 expect(ast1.attrsList[2].name).toBe('value')790 expect(ast1.attrsList[2].value).toBe('hello world')791 expect(ast1.attrsMap['type']).toBe('text')792 expect(ast1.attrsMap['name']).toBe('field1')793 expect(ast1.attrsMap['value']).toBe('hello world')794 expect(ast1.attrs[0].name).toBe('type')795 expect(ast1.attrs[0].value).toBe('"text"')796 expect(ast1.attrs[1].name).toBe('name')797 expect(ast1.attrs[1].value).toBe('"field1"')798 expect(ast1.attrs[2].name).toBe('value')799 expect(ast1.attrs[2].value).toBe('"hello world"')800 // interpolation warning801 parse('<input type="text" name="field1" value="{{msg}}">', baseOptions)802 expect(803 'Interpolation inside attributes has been removed'804 ).toHaveBeenWarned()805 })806807 if (!isIE && !isEdge) {808 it('duplicate attribute', () => {809 parse('<p class="class1" class="class1">hello world</p>', baseOptions)810 expect('duplicate attribute').toHaveBeenWarned()811 })812 }813814 it('custom delimiter', () => {815 const ast = parse(816 '<p>{msg}</p>',817 extend({ delimiters: ['{', '}'] }, baseOptions)818 )819 expect(ast.children[0].expression).toBe('_s(msg)')820 })821822 it('not specified getTagNamespace option', () => {823 const options = extend({}, baseOptions)824 delete options.getTagNamespace825 const ast = parse('<svg><text>hello world</text></svg>', options)826 expect(ast.tag).toBe('svg')827 expect(ast.ns).toBeUndefined()828 })829830 it('not specified mustUseProp', () => {831 const options = extend({}, baseOptions)832 delete options.mustUseProp833 const ast = parse('<input type="text" name="field1" :value="msg">', options)834 expect(ast.props).toBeUndefined()835 })836837 it('use prop when prop modifier was explicitly declared', () => {838 const ast = parse(839 '<component is="textarea" :value.prop="val" />',840 baseOptions841 )842 expect(ast.attrs).toBeUndefined()843 expect(ast.props.length).toBe(1)844 expect(ast.props[0].name).toBe('value')845 expect(ast.props[0].value).toBe('val')846 })847848 it('pre/post transforms', () => {849 const options = extend({}, baseOptions)850 const spy1 = vi.fn()851 const spy2 = vi.fn()852 options.modules = options.modules.concat([853 {854 preTransformNode(el) {855 spy1(el.tag)856 },857 postTransformNode(el) {858 expect(el.attrs.length).toBe(1)859 spy2(el.tag)860 }861 }862 ])863 parse('<img v-pre src="hi">', options)864 expect(spy1).toHaveBeenCalledWith('img')865 expect(spy2).toHaveBeenCalledWith('img')866 })867868 it('preserve whitespace in <pre> tag', function () {869 const options = extend({}, baseOptions)870 const ast = parse(871 '<pre><code> \n<span>hi</span>\n </code><span> </span></pre>',872 options873 )874 const code = ast.children[0]875 expect(code.children[0].type).toBe(3)876 expect(code.children[0].text).toBe(' \n')877 expect(code.children[2].type).toBe(3)878 expect(code.children[2].text).toBe('\n ')879880 const span = ast.children[1]881 expect(span.children[0].type).toBe(3)882 expect(span.children[0].text).toBe(' ')883 })884885 // #5992886 it('ignore the first newline in <pre> tag', function () {887 const options = extend({}, baseOptions)888 const ast = parse(889 '<div><pre>\nabc</pre>\ndef<pre>\n\nabc</pre></div>',890 options891 )892 const pre = ast.children[0]893 expect(pre.children[0].type).toBe(3)894 expect(pre.children[0].text).toBe('abc')895 const text = ast.children[1]896 expect(text.type).toBe(3)897 expect(text.text).toBe('\ndef')898 const pre2 = ast.children[2]899 expect(pre2.children[0].type).toBe(3)900 expect(pre2.children[0].text).toBe('\nabc')901 })902903 it('keep first newline after unary tag in <pre>', () => {904 const options = extend({}, baseOptions)905 const ast = parse('<pre>abc<input>\ndef</pre>', options)906 expect(ast.children[1].type).toBe(1)907 expect(ast.children[1].tag).toBe('input')908 expect(ast.children[2].type).toBe(3)909 expect(ast.children[2].text).toBe('\ndef')910 })911912 it('forgivingly handle < in plain text', () => {913 const options = extend({}, baseOptions)914 const ast = parse('<p>1 < 2 < 3</p>', options)915 expect(ast.tag).toBe('p')916 expect(ast.children.length).toBe(1)917 expect(ast.children[0].type).toBe(3)918 expect(ast.children[0].text).toBe('1 < 2 < 3')919 })920921 it('IE conditional comments', () => {922 const options = extend({}, baseOptions)923 const ast = parse(924 `925 <div>926 <!--[if lte IE 8]>927 <p>Test 1</p>928 <![endif]-->929 </div>930 `,931 options932 )933 expect(ast.tag).toBe('div')934 expect(ast.children.length).toBe(0)935 })936937 it('parse content in textarea as text', () => {938 const options = extend({}, baseOptions)939940 const whitespace = parse(941 `942 <textarea>943 <p>Test 1</p>944 test2945 </textarea>946 `,947 options948 )949 expect(whitespace.tag).toBe('textarea')950 expect(whitespace.children.length).toBe(1)951 expect(whitespace.children[0].type).toBe(3)952 // textarea is whitespace sensitive953 expect(whitespace.children[0].text).toBe(` <p>Test 1</p>954 test2955 `)956957 const comment = parse('<textarea><!--comment--></textarea>', options)958 expect(comment.tag).toBe('textarea')959 expect(comment.children.length).toBe(1)960 expect(comment.children[0].type).toBe(3)961 expect(comment.children[0].text).toBe('<!--comment-->')962 })963964 // #5526965 it('should not decode text in script tags', () => {966 const options = extend({}, baseOptions)967 const ast = parse(968 `<script type="x/template">><foo><</script>`,969 options970 )971 expect(ast.children[0].text).toBe(`><foo><`)972 })973974 it('should ignore comments', () => {975 const options = extend({}, baseOptions)976 const ast = parse(`<div>123<!--comment here--></div>`, options)977 expect(ast.tag).toBe('div')978 expect(ast.children.length).toBe(1)979 expect(ast.children[0].type).toBe(3)980 expect(ast.children[0].text).toBe('123')981 })982983 it('should kept comments', () => {984 const options = extend(985 {986 comments: true987 },988 baseOptions989 )990 const ast = parse(`<div>123<!--comment here--></div>`, options)991 expect(ast.tag).toBe('div')992 expect(ast.children.length).toBe(2)993 expect(ast.children[0].type).toBe(3)994 expect(ast.children[0].text).toBe('123')995 expect(ast.children[1].type).toBe(3) // parse comment with ASTText996 expect(ast.children[1].isComment).toBe(true) // parse comment with ASTText997 expect(ast.children[1].text).toBe('comment here')998 })9991000 // #94071001 it('should parse templates with comments anywhere', () => {1002 const options = extend(1003 {1004 comments: true1005 },1006 baseOptions1007 )1008 const ast = parse(`<!--comment here--><div>123</div>`, options)1009 expect(ast.tag).toBe('div')1010 expect(ast.children.length).toBe(1)1011 })10121013 // #81031014 it('should allow CRLFs in string interpolations', () => {1015 const ast = parse(`<p>{{\r\nmsg\r\n}}</p>`, baseOptions)1016 expect(ast.children[0].expression).toBe('_s(msg)')1017 })10181019 it('preserveWhitespace: false', () => {1020 const options = extend(1021 {1022 preserveWhitespace: false1023 },1024 baseOptions1025 )10261027 const ast = parse(1028 '<p>\n Welcome to <b>Vue.js</b> <i>world</i> \n <span>.\n Have fun!\n</span></p>',1029 options1030 )1031 expect(ast.tag).toBe('p')1032 expect(ast.children.length).toBe(4)1033 expect(ast.children[0].type).toBe(3)1034 expect(ast.children[0].text).toBe('\n Welcome to ')1035 expect(ast.children[1].tag).toBe('b')1036 expect(ast.children[1].children[0].text).toBe('Vue.js')1037 expect(ast.children[2].tag).toBe('i')1038 expect(ast.children[2].children[0].text).toBe('world')1039 expect(ast.children[3].tag).toBe('span')1040 expect(ast.children[3].children[0].text).toBe('.\n Have fun!\n')1041 })10421043 const condenseOptions = extend(1044 {1045 whitespace: 'condense',1046 // should be ignored when whitespace is specified1047 preserveWhitespace: false1048 },1049 baseOptions1050 )10511052 it(`whitespace: 'condense'`, () => {1053 const options = extend({}, condenseOptions)1054 const ast = parse(1055 '<p>\n Welcome to <b>Vue.js</b> <i>world</i> \n <span>.\n Have fun!\n</span></p>',1056 options1057 )1058 expect(ast.tag).toBe('p')1059 expect(ast.children.length).toBe(5)1060 expect(ast.children[0].type).toBe(3)1061 expect(ast.children[0].text).toBe(' Welcome to ')1062 expect(ast.children[1].tag).toBe('b')1063 expect(ast.children[1].children[0].text).toBe('Vue.js')1064 expect(ast.children[2].type).toBe(3)1065 // should condense inline whitespace into single space1066 expect(ast.children[2].text).toBe(' ')1067 expect(ast.children[3].tag).toBe('i')1068 expect(ast.children[3].children[0].text).toBe('world')1069 // should have removed the whitespace node between tags that contains newlines1070 expect(ast.children[4].tag).toBe('span')1071 expect(ast.children[4].children[0].text).toBe('. Have fun! ')1072 })10731074 it(`maintains with whitespace: 'condense'`, () => {1075 const options = extend({}, condenseOptions)1076 const ast = parse('<span> </span>', options)1077 const code = ast.children[0]1078 expect(code.type).toBe(3)1079 expect(code.text).toBe('\xA0')1080 })10811082 it(`preserve whitespace in <pre> tag with whitespace: 'condense'`, function () {1083 const options = extend({}, condenseOptions)1084 const ast = parse(1085 '<pre><code> \n<span>hi</span>\n </code><span> </span></pre>',1086 options1087 )1088 const code = ast.children[0]1089 expect(code.children[0].type).toBe(3)1090 expect(code.children[0].text).toBe(' \n')1091 expect(code.children[2].type).toBe(3)1092 expect(code.children[2].text).toBe('\n ')10931094 const span = ast.children[1]1095 expect(span.children[0].type).toBe(3)1096 expect(span.children[0].text).toBe(' ')1097 })10981099 it(`ignore the first newline in <pre> tag with whitespace: 'condense'`, function () {1100 const options = extend({}, condenseOptions)1101 const ast = parse(1102 '<div><pre>\nabc</pre>\ndef<pre>\n\nabc</pre></div>',1103 options1104 )1105 const pre = ast.children[0]1106 expect(pre.children[0].type).toBe(3)1107 expect(pre.children[0].text).toBe('abc')1108 const text = ast.children[1]1109 expect(text.type).toBe(3)1110 expect(text.text).toBe(' def')1111 const pre2 = ast.children[2]1112 expect(pre2.children[0].type).toBe(3)1113 expect(pre2.children[0].text).toBe('\nabc')1114 })11151116 it(`keep first newline after unary tag in <pre> with whitespace: 'condense'`, () => {1117 const options = extend({}, condenseOptions)1118 const ast = parse('<pre>abc<input>\ndef</pre>', options)1119 expect(ast.children[1].type).toBe(1)1120 expect(ast.children[1].tag).toBe('input')1121 expect(ast.children[2].type).toBe(3)1122 expect(ast.children[2].text).toBe('\ndef')1123 })11241125 // #101521126 it('not warn when scoped slot used inside of dynamic component on regular element', () => {1127 parse(1128 `1129 <div>1130 <div is="customComp" v-slot="slotProps"></div>1131 <div :is="'customComp'" v-slot="slotProps"></div>1132 <div v-bind:is="'customComp'" v-slot="slotProps"></div>1133 </div>1134 `,1135 baseOptions1136 )1137 expect(1138 'v-slot can only be used on components or <template>'1139 ).not.toHaveBeenWarned()11401141 parse(1142 `<div is="customComp"><template v-slot="slotProps"></template></div>`,1143 baseOptions1144 )1145 expect(1146 `<template v-slot> can only appear at the root level inside the receiving the component`1147 ).not.toHaveBeenWarned()1148 })1149})
Findings
✓ No findings reported for this file.