parseComponent.spec.ts 7.4 KB

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