compileStyle.spec.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import { compileStyle, compileStyleAsync } from '../src/compileStyle'
  2. import { mockWarn } from '@vue/shared'
  3. describe('SFC scoped CSS', () => {
  4. mockWarn()
  5. function compileScoped(source: string): string {
  6. const res = compileStyle({
  7. source,
  8. filename: 'test.css',
  9. id: 'test',
  10. scoped: true
  11. })
  12. if (res.errors.length) {
  13. res.errors.forEach(err => {
  14. console.error(err)
  15. })
  16. expect(res.errors.length).toBe(0)
  17. }
  18. return res.code
  19. }
  20. test('simple selectors', () => {
  21. expect(compileScoped(`h1 { color: red; }`)).toMatch(
  22. `h1[test] { color: red;`
  23. )
  24. expect(compileScoped(`.foo { color: red; }`)).toMatch(
  25. `.foo[test] { color: red;`
  26. )
  27. })
  28. test('descendent selector', () => {
  29. expect(compileScoped(`h1 .foo { color: red; }`)).toMatch(
  30. `h1 .foo[test] { color: red;`
  31. )
  32. })
  33. test('multiple selectors', () => {
  34. expect(compileScoped(`h1 .foo, .bar, .baz { color: red; }`)).toMatch(
  35. `h1 .foo[test], .bar[test], .baz[test] { color: red;`
  36. )
  37. })
  38. test('pseudo class', () => {
  39. expect(compileScoped(`.foo:after { color: red; }`)).toMatch(
  40. `.foo[test]:after { color: red;`
  41. )
  42. })
  43. test('pseudo element', () => {
  44. expect(compileScoped(`::selection { display: none; }`)).toMatch(
  45. '[test]::selection {'
  46. )
  47. })
  48. test('spaces before pseudo element', () => {
  49. const code = compileScoped(`.abc, ::selection { color: red; }`)
  50. expect(code).toMatch('.abc[test],')
  51. expect(code).toMatch('[test]::selection {')
  52. })
  53. test('::v-deep', () => {
  54. expect(compileScoped(`::v-deep(.foo) { color: red; }`))
  55. .toMatchInlineSnapshot(`
  56. "[test] .foo { color: red;
  57. }"
  58. `)
  59. expect(compileScoped(`::v-deep(.foo .bar) { color: red; }`))
  60. .toMatchInlineSnapshot(`
  61. "[test] .foo .bar { color: red;
  62. }"
  63. `)
  64. expect(compileScoped(`.baz .qux ::v-deep(.foo .bar) { color: red; }`))
  65. .toMatchInlineSnapshot(`
  66. ".baz .qux[test] .foo .bar { color: red;
  67. }"
  68. `)
  69. })
  70. test('::v-slotted', () => {
  71. expect(compileScoped(`::v-slotted(.foo) { color: red; }`))
  72. .toMatchInlineSnapshot(`
  73. ".foo[test-s] { color: red;
  74. }"
  75. `)
  76. expect(compileScoped(`::v-slotted(.foo .bar) { color: red; }`))
  77. .toMatchInlineSnapshot(`
  78. ".foo .bar[test-s] { color: red;
  79. }"
  80. `)
  81. expect(compileScoped(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`))
  82. .toMatchInlineSnapshot(`
  83. ".baz .qux .foo .bar[test-s] { color: red;
  84. }"
  85. `)
  86. })
  87. test('::v-global', () => {
  88. expect(compileScoped(`::v-global(.foo) { color: red; }`))
  89. .toMatchInlineSnapshot(`
  90. ".foo { color: red;
  91. }"
  92. `)
  93. expect(compileScoped(`::v-global(.foo .bar) { color: red; }`))
  94. .toMatchInlineSnapshot(`
  95. ".foo .bar { color: red;
  96. }"
  97. `)
  98. // global ignores anything before it
  99. expect(compileScoped(`.baz .qux ::v-global(.foo .bar) { color: red; }`))
  100. .toMatchInlineSnapshot(`
  101. ".foo .bar { color: red;
  102. }"
  103. `)
  104. })
  105. test('media query', () => {
  106. expect(compileScoped(`@media print { .foo { color: red }}`))
  107. .toMatchInlineSnapshot(`
  108. "@media print {
  109. .foo[test] { color: red
  110. }}"
  111. `)
  112. })
  113. test('supports query', () => {
  114. expect(compileScoped(`@supports(display: grid) { .foo { display: grid }}`))
  115. .toMatchInlineSnapshot(`
  116. "@supports(display: grid) {
  117. .foo[test] { display: grid
  118. }}"
  119. `)
  120. })
  121. test('scoped keyframes', () => {
  122. const style = compileScoped(`
  123. .anim {
  124. animation: color 5s infinite, other 5s;
  125. }
  126. .anim-2 {
  127. animation-name: color;
  128. animation-duration: 5s;
  129. }
  130. .anim-3 {
  131. animation: 5s color infinite, 5s other;
  132. }
  133. .anim-multiple {
  134. animation: color 5s infinite, opacity 2s;
  135. }
  136. .anim-multiple-2 {
  137. animation-name: color, opacity;
  138. animation-duration: 5s, 2s;
  139. }
  140. @keyframes color {
  141. from { color: red; }
  142. to { color: green; }
  143. }
  144. @-webkit-keyframes color {
  145. from { color: red; }
  146. to { color: green; }
  147. }
  148. @keyframes opacity {
  149. from { opacity: 0; }
  150. to { opacity: 1; }
  151. }
  152. @-webkit-keyframes opacity {
  153. from { opacity: 0; }
  154. to { opacity: 1; }
  155. }
  156. `)
  157. expect(style).toContain(
  158. `.anim[test] {\n animation: color-test 5s infinite, other 5s;`
  159. )
  160. expect(style).toContain(`.anim-2[test] {\n animation-name: color-test`)
  161. expect(style).toContain(
  162. `.anim-3[test] {\n animation: 5s color-test infinite, 5s other;`
  163. )
  164. expect(style).toContain(`@keyframes color-test {`)
  165. expect(style).toContain(`@-webkit-keyframes color-test {`)
  166. expect(style).toContain(
  167. `.anim-multiple[test] {\n animation: color-test 5s infinite,opacity-test 2s;`
  168. )
  169. expect(style).toContain(
  170. `.anim-multiple-2[test] {\n animation-name: color-test,opacity-test;`
  171. )
  172. expect(style).toContain(`@keyframes opacity-test {`)
  173. expect(style).toContain(`@-webkit-keyframes opacity-test {`)
  174. })
  175. // vue-loader/#1370
  176. test('spaces after selector', () => {
  177. expect(compileScoped(`.foo , .bar { color: red; }`)).toMatchInlineSnapshot(`
  178. ".foo[test], .bar[test] { color: red;
  179. }"
  180. `)
  181. })
  182. describe('deprecated syntax', () => {
  183. test('::v-deep as combinator', () => {
  184. expect(compileScoped(`::v-deep .foo { color: red; }`))
  185. .toMatchInlineSnapshot(`
  186. "[test] .foo { color: red;
  187. }"
  188. `)
  189. expect(compileScoped(`.bar ::v-deep .foo { color: red; }`))
  190. .toMatchInlineSnapshot(`
  191. ".bar[test] .foo { color: red;
  192. }"
  193. `)
  194. expect(
  195. `::v-deep usage as a combinator has been deprecated.`
  196. ).toHaveBeenWarned()
  197. })
  198. test('>>> (deprecated syntax)', () => {
  199. const code = compileScoped(`>>> .foo { color: red; }`)
  200. expect(code).toMatchInlineSnapshot(`
  201. "[test] .foo { color: red;
  202. }"
  203. `)
  204. expect(
  205. `the >>> and /deep/ combinators have been deprecated.`
  206. ).toHaveBeenWarned()
  207. })
  208. test('/deep/ (deprecated syntax)', () => {
  209. const code = compileScoped(`/deep/ .foo { color: red; }`)
  210. expect(code).toMatchInlineSnapshot(`
  211. "[test] .foo { color: red;
  212. }"
  213. `)
  214. expect(
  215. `the >>> and /deep/ combinators have been deprecated.`
  216. ).toHaveBeenWarned()
  217. })
  218. })
  219. })
  220. describe('SFC CSS modules', () => {
  221. test('should include resulting classes object in result', async () => {
  222. const result = await compileStyleAsync({
  223. source: `.red { color: red }\n.green { color: green }\n:global(.blue) { color: blue }`,
  224. filename: `test.css`,
  225. id: 'test',
  226. modules: true
  227. })
  228. expect(result.modules).toBeDefined()
  229. expect(result.modules!.red).toMatch('_red_')
  230. expect(result.modules!.green).toMatch('_green_')
  231. expect(result.modules!.blue).toBeUndefined()
  232. })
  233. test('postcss-modules options', async () => {
  234. const result = await compileStyleAsync({
  235. source: `:local(.foo-bar) { color: red }\n.baz-qux { color: green }`,
  236. filename: `test.css`,
  237. id: 'test',
  238. modules: true,
  239. modulesOptions: {
  240. scopeBehaviour: 'global',
  241. generateScopedName: `[name]__[local]__[hash:base64:5]`,
  242. localsConvention: 'camelCaseOnly'
  243. }
  244. })
  245. expect(result.modules).toBeDefined()
  246. expect(result.modules!.fooBar).toMatch('__foo-bar__')
  247. expect(result.modules!.bazQux).toBeUndefined()
  248. })
  249. })