parseComponent.spec.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. { deindent: true }
  29. )
  30. expect(res.template!.content.trim()).toBe('<div>hi</div>')
  31. expect(res.styles.length).toBe(4)
  32. expect(res.styles[0].src).toBe('./test.css')
  33. expect(res.styles[1].lang).toBe('stylus')
  34. expect(res.styles[1].scoped).toBe(true)
  35. expect(res.styles[1].content.trim()).toBe(
  36. 'h1\n color red\nh2\n color green'
  37. )
  38. expect(res.styles[2].module).toBe(true)
  39. expect(res.styles[3].attrs['bool-attr']).toBe(true)
  40. expect(res.styles[3].attrs['val-attr']).toBe('test')
  41. expect(res.script!.content.trim()).toBe('export default {}')
  42. })
  43. it('should parse template with closed input', () => {
  44. const res = parseComponent(`
  45. <template>
  46. <input type="text"/>
  47. </template>
  48. `)
  49. expect(res.template!.content.trim()).toBe('<input type="text"/>')
  50. })
  51. it('should handle nested template', () => {
  52. const res = parseComponent(`
  53. <template>
  54. <div><template v-if="ok">hi</template></div>
  55. </template>
  56. `)
  57. expect(res.template!.content.trim()).toBe(
  58. '<div><template v-if="ok">hi</template></div>'
  59. )
  60. })
  61. it('deindent content', () => {
  62. const content = `
  63. <template>
  64. <div></div>
  65. </template>
  66. <script>
  67. export default {}
  68. </script>
  69. <style>
  70. h1 { color: red }
  71. </style>
  72. `
  73. const deindentDefault = parseComponent(content.trim(), {
  74. pad: false,
  75. deindent: true
  76. })
  77. const deindentEnabled = parseComponent(content.trim(), {
  78. pad: false,
  79. deindent: true
  80. })
  81. const deindentDisabled = parseComponent(content.trim(), {
  82. pad: false,
  83. deindent: false
  84. })
  85. expect(deindentDefault.template!.content).toBe('\n<div></div>\n')
  86. expect(deindentDefault.script!.content).toBe('\nexport default {}\n')
  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. `,
  195. { deindent: true }
  196. )
  197. expect(res.customBlocks.length).toBe(3)
  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. })
  223. // Regression #4289
  224. it('accepts nested template tag', () => {
  225. const raw = `<div>
  226. <template v-if="true === true">
  227. <section class="section">
  228. <div class="container">
  229. Should be shown
  230. </div>
  231. </section>
  232. </template>
  233. <template v-else>
  234. <p>Should not be shown</p>
  235. </template>
  236. </div>`
  237. const res = parseComponent(`<template>${raw}</template>`)
  238. expect(res.template!.content.trim()).toBe(raw)
  239. })
  240. it('should not hang on trailing text', () => {
  241. const res = parseComponent(`<template>hi</`)
  242. expect(res.template!.content).toBe('hi')
  243. })
  244. it('should collect errors with source range', () => {
  245. const res = parseComponent(`<template>hi</`, { outputSourceRange: true })
  246. expect(res.errors.length).toBe(1)
  247. expect((res.errors[0] as WarningMessage).start).toBe(0)
  248. })
  249. })