sfc-parser.spec.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import { parseComponent } from 'sfc/parser'
  2. describe('Single File Component parser', () => {
  3. it('should parse', () => {
  4. const res = parseComponent(`
  5. <template>
  6. <div>hi</div>
  7. </template>
  8. <style src="./test.css"></style>
  9. <style lang="stylus" scoped>
  10. h1
  11. color red
  12. h2
  13. color green
  14. </style>
  15. <style module>
  16. h1 { font-weight: bold }
  17. </style>
  18. <style bool-attr val-attr="test"></style>
  19. <script>
  20. export default {}
  21. </script>
  22. <div>
  23. <style>nested should be ignored</style>
  24. </div>
  25. `)
  26. expect(res.template.content.trim()).toBe('<div>hi</div>')
  27. expect(res.styles.length).toBe(4)
  28. expect(res.styles[0].src).toBe('./test.css')
  29. expect(res.styles[1].lang).toBe('stylus')
  30. expect(res.styles[1].scoped).toBe(true)
  31. expect(res.styles[1].content.trim()).toBe('h1\n color red\nh2\n color green')
  32. expect(res.styles[2].module).toBe(true)
  33. expect(res.styles[3].attrs['bool-attr']).toBe(true)
  34. expect(res.styles[3].attrs['val-attr']).toBe('test')
  35. expect(res.script.content.trim()).toBe('export default {}')
  36. })
  37. it('should parse template with closed input', () => {
  38. const res = parseComponent(`
  39. <template>
  40. <input type="text"/>
  41. </template>
  42. `)
  43. expect(res.template.content.trim()).toBe('<input type="text"/>')
  44. })
  45. it('should handle nested template', () => {
  46. const res = parseComponent(`
  47. <template>
  48. <div><template v-if="ok">hi</template></div>
  49. </template>
  50. `)
  51. expect(res.template.content.trim()).toBe('<div><template v-if="ok">hi</template></div>')
  52. })
  53. it('deindent content', () => {
  54. const content = `
  55. <template>
  56. <div></div>
  57. </template>
  58. <script>
  59. export default {}
  60. </script>
  61. <style>
  62. h1 { color: red }
  63. </style>
  64. `
  65. const deindentDefault = parseComponent(content.trim(), { pad: false })
  66. const deindentEnabled = parseComponent(content.trim(), { pad: false, deindent: true })
  67. const deindentDisabled = parseComponent(content.trim(), { pad: false, deindent: false })
  68. expect(deindentDefault.template.content).toBe('\n<div></div>\n')
  69. expect(deindentDefault.script.content).toBe('\nexport default {}\n')
  70. expect(deindentDefault.styles[0].content).toBe('\nh1 { color: red }\n')
  71. expect(deindentEnabled.template.content).toBe('\n<div></div>\n')
  72. expect(deindentEnabled.script.content).toBe('\nexport default {}\n')
  73. expect(deindentEnabled.styles[0].content).toBe('\nh1 { color: red }\n')
  74. expect(deindentDisabled.template.content).toBe('\n <div></div>\n ')
  75. expect(deindentDisabled.script.content).toBe('\n export default {}\n ')
  76. expect(deindentDisabled.styles[0].content).toBe('\n h1 { color: red }\n ')
  77. })
  78. it('pad content', () => {
  79. const content = `
  80. <template>
  81. <div></div>
  82. </template>
  83. <script>
  84. export default {}
  85. </script>
  86. <style>
  87. h1 { color: red }
  88. </style>
  89. `
  90. const padDefault = parseComponent(content.trim(), { pad: true })
  91. const padLine = parseComponent(content.trim(), { pad: 'line' })
  92. const padSpace = parseComponent(content.trim(), { pad: 'space' })
  93. expect(padDefault.script.content).toBe(Array(3 + 1).join('//\n') + '\nexport default {}\n')
  94. expect(padDefault.styles[0].content).toBe(Array(6 + 1).join('\n') + '\nh1 { color: red }\n')
  95. expect(padLine.script.content).toBe(Array(3 + 1).join('//\n') + '\nexport default {}\n')
  96. expect(padLine.styles[0].content).toBe(Array(6 + 1).join('\n') + '\nh1 { color: red }\n')
  97. expect(padSpace.script.content).toBe(`<template>
  98. <div></div>
  99. </template>
  100. <script>`.replace(/./g, ' ') + '\nexport default {}\n')
  101. expect(padSpace.styles[0].content).toBe(`<template>
  102. <div></div>
  103. </template>
  104. <script>
  105. export default {}
  106. </script>
  107. <style>`.replace(/./g, ' ') + '\nh1 { color: red }\n')
  108. })
  109. it('should handle template blocks with lang as special text', () => {
  110. const res = parseComponent(`
  111. <template lang="pug">
  112. div
  113. h1(v-if='1 < 2') hello
  114. </template>
  115. `)
  116. expect(res.template.content.trim()).toBe(`div\n h1(v-if='1 < 2') hello`)
  117. })
  118. it('should handle component contains "<" only', () => {
  119. const res = parseComponent(`
  120. <template>
  121. <span><</span>
  122. </template>
  123. `)
  124. expect(res.template.content.trim()).toBe(`<span><</span>`)
  125. })
  126. it('should handle custom blocks without parsing them', () => {
  127. const res = parseComponent(`
  128. <template>
  129. <div></div>
  130. </template>
  131. <example name="simple">
  132. <my-button ref="button">Hello</my-button>
  133. </example>
  134. <example name="with props">
  135. <my-button color="red">Hello</my-button>
  136. </example>
  137. <test name="simple" foo="bar">
  138. export default function simple (vm) {
  139. describe('Hello', () => {
  140. it('should display Hello', () => {
  141. this.vm.$refs.button.$el.innerText.should.equal('Hello')
  142. }))
  143. }))
  144. }
  145. </test>
  146. `)
  147. expect(res.customBlocks.length).toBe(3)
  148. const simpleExample = res.customBlocks[0]
  149. expect(simpleExample.type).toBe('example')
  150. expect(simpleExample.content.trim()).toBe('<my-button ref="button">Hello</my-button>')
  151. expect(simpleExample.attrs.name).toBe('simple')
  152. const withProps = res.customBlocks[1]
  153. expect(withProps.type).toBe('example')
  154. expect(withProps.content.trim()).toBe('<my-button color="red">Hello</my-button>')
  155. expect(withProps.attrs.name).toBe('with props')
  156. const simpleTest = res.customBlocks[2]
  157. expect(simpleTest.type).toBe('test')
  158. expect(simpleTest.content.trim()).toBe(`export default function simple (vm) {
  159. describe('Hello', () => {
  160. it('should display Hello', () => {
  161. this.vm.$refs.button.$el.innerText.should.equal('Hello')
  162. }))
  163. }))
  164. }`)
  165. expect(simpleTest.attrs.name).toBe('simple')
  166. expect(simpleTest.attrs.foo).toBe('bar')
  167. })
  168. // Regression #4289
  169. it('accepts nested template tag', () => {
  170. const raw = `<div>
  171. <template v-if="true === true">
  172. <section class="section">
  173. <div class="container">
  174. Should be shown
  175. </div>
  176. </section>
  177. </template>
  178. <template v-else>
  179. <p>Should not be shown</p>
  180. </template>
  181. </div>`
  182. const res = parseComponent(`<template>${raw}</template>`)
  183. expect(res.template.content.trim()).toBe(raw)
  184. })
  185. it('should not hang on trailing text', () => {
  186. const res = parseComponent(`<template>hi</`)
  187. expect(res.template.content).toBe('hi')
  188. })
  189. it('should collect errors with source range', () => {
  190. const res = parseComponent(`<template>hi</`, { outputSourceRange: true })
  191. expect(res.errors.length).toBe(1)
  192. expect(res.errors[0].start).toBe(0)
  193. })
  194. })