safelist.test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. import { run, html, css } from './util/run'
  2. it('should not safelist anything', () => {
  3. let config = {
  4. content: [{ raw: html`<div class="uppercase"></div>` }],
  5. }
  6. return run('@tailwind utilities', config).then((result) => {
  7. return expect(result.css).toMatchFormattedCss(css`
  8. .uppercase {
  9. text-transform: uppercase;
  10. }
  11. `)
  12. })
  13. })
  14. it('should safelist strings', () => {
  15. let config = {
  16. content: [{ raw: html`<div class="uppercase"></div>` }],
  17. safelist: ['mt-[20px]', 'font-bold', 'text-gray-200', 'hover:underline'],
  18. }
  19. return run('@tailwind utilities', config).then((result) => {
  20. expect(result.css).toMatchFormattedCss(css`
  21. .mt-\[20px\] {
  22. margin-top: 20px;
  23. }
  24. .font-bold {
  25. font-weight: 700;
  26. }
  27. .uppercase {
  28. text-transform: uppercase;
  29. }
  30. .text-gray-200 {
  31. --tw-text-opacity: 1;
  32. color: rgb(229 231 235 / var(--tw-text-opacity));
  33. }
  34. .hover\:underline:hover {
  35. text-decoration-line: underline;
  36. }
  37. `)
  38. })
  39. })
  40. it('should safelist based on a pattern regex', () => {
  41. let config = {
  42. content: [{ raw: html`<div class="uppercase"></div>` }],
  43. safelist: [
  44. {
  45. pattern: /^bg-(red)-(100|200)$/,
  46. variants: ['hover'],
  47. },
  48. ],
  49. }
  50. return run('@tailwind utilities', config).then((result) => {
  51. expect(result.css).toMatchFormattedCss(css`
  52. .bg-red-100 {
  53. --tw-bg-opacity: 1;
  54. background-color: rgb(254 226 226 / var(--tw-bg-opacity));
  55. }
  56. .bg-red-200 {
  57. --tw-bg-opacity: 1;
  58. background-color: rgb(254 202 202 / var(--tw-bg-opacity));
  59. }
  60. .uppercase {
  61. text-transform: uppercase;
  62. }
  63. .hover\:bg-red-100:hover {
  64. --tw-bg-opacity: 1;
  65. background-color: rgb(254 226 226 / var(--tw-bg-opacity));
  66. }
  67. .hover\:bg-red-200:hover {
  68. --tw-bg-opacity: 1;
  69. background-color: rgb(254 202 202 / var(--tw-bg-opacity));
  70. }
  71. `)
  72. })
  73. })
  74. it('should not generate duplicates', () => {
  75. let config = {
  76. content: [{ raw: html`<div class="uppercase"></div>` }],
  77. safelist: [
  78. 'uppercase',
  79. {
  80. pattern: /^bg-(red)-(100|200)$/,
  81. variants: ['hover'],
  82. },
  83. {
  84. pattern: /^bg-(red)-(100|200)$/,
  85. variants: ['hover'],
  86. },
  87. {
  88. pattern: /^bg-(red)-(100|200)$/,
  89. variants: ['hover'],
  90. },
  91. ],
  92. }
  93. return run('@tailwind utilities', config).then((result) => {
  94. expect(result.css).toMatchFormattedCss(css`
  95. .bg-red-100 {
  96. --tw-bg-opacity: 1;
  97. background-color: rgb(254 226 226 / var(--tw-bg-opacity));
  98. }
  99. .bg-red-200 {
  100. --tw-bg-opacity: 1;
  101. background-color: rgb(254 202 202 / var(--tw-bg-opacity));
  102. }
  103. .uppercase {
  104. text-transform: uppercase;
  105. }
  106. .hover\:bg-red-100:hover {
  107. --tw-bg-opacity: 1;
  108. background-color: rgb(254 226 226 / var(--tw-bg-opacity));
  109. }
  110. .hover\:bg-red-200:hover {
  111. --tw-bg-opacity: 1;
  112. background-color: rgb(254 202 202 / var(--tw-bg-opacity));
  113. }
  114. `)
  115. })
  116. })
  117. it('should safelist when using a custom prefix', () => {
  118. let config = {
  119. prefix: 'tw-',
  120. content: [{ raw: html`<div class="tw-uppercase"></div>` }],
  121. safelist: [
  122. {
  123. pattern: /^tw-bg-red-(100|200)$/g,
  124. },
  125. ],
  126. }
  127. return run('@tailwind utilities', config).then((result) => {
  128. expect(result.css).toMatchFormattedCss(css`
  129. .tw-bg-red-100 {
  130. --tw-bg-opacity: 1;
  131. background-color: rgb(254 226 226 / var(--tw-bg-opacity));
  132. }
  133. .tw-bg-red-200 {
  134. --tw-bg-opacity: 1;
  135. background-color: rgb(254 202 202 / var(--tw-bg-opacity));
  136. }
  137. .tw-uppercase {
  138. text-transform: uppercase;
  139. }
  140. `)
  141. })
  142. })
  143. it('should not safelist when an empty list is provided', () => {
  144. let config = {
  145. content: [{ raw: html`<div class="uppercase"></div>` }],
  146. safelist: [],
  147. }
  148. return run('@tailwind utilities', config).then((result) => {
  149. return expect(result.css).toMatchFormattedCss(css`
  150. .uppercase {
  151. text-transform: uppercase;
  152. }
  153. `)
  154. })
  155. })
  156. it('should not safelist when an sparse/holey list is provided', () => {
  157. let config = {
  158. content: [{ raw: html`<div class="uppercase"></div>` }],
  159. safelist: [, , ,],
  160. }
  161. return run('@tailwind utilities', config).then((result) => {
  162. return expect(result.css).toMatchFormattedCss(css`
  163. .uppercase {
  164. text-transform: uppercase;
  165. }
  166. `)
  167. })
  168. })
  169. it('should not safelist any invalid variants if provided', () => {
  170. let config = {
  171. content: [{ raw: html`<div class="uppercase"></div>` }],
  172. safelist: [
  173. {
  174. pattern: /^bg-(red)-(100|200)$/,
  175. variants: ['foo', 'bar'],
  176. },
  177. ],
  178. }
  179. return run('@tailwind utilities', config).then((result) => {
  180. expect(result.css).toMatchFormattedCss(css`
  181. .bg-red-100 {
  182. --tw-bg-opacity: 1;
  183. background-color: rgb(254 226 226 / var(--tw-bg-opacity));
  184. }
  185. .bg-red-200 {
  186. --tw-bg-opacity: 1;
  187. background-color: rgb(254 202 202 / var(--tw-bg-opacity));
  188. }
  189. .uppercase {
  190. text-transform: uppercase;
  191. }
  192. `)
  193. })
  194. })
  195. it('should safelist negatives based on a pattern regex', () => {
  196. let config = {
  197. content: [{ raw: html`<div class="uppercase"></div>` }],
  198. safelist: [
  199. {
  200. pattern: /^-top-1$/,
  201. variants: ['hover'],
  202. },
  203. ],
  204. }
  205. return run('@tailwind utilities', config).then((result) => {
  206. return expect(result.css).toMatchFormattedCss(css`
  207. .-top-1 {
  208. top: -0.25rem;
  209. }
  210. .uppercase {
  211. text-transform: uppercase;
  212. }
  213. .hover\:-top-1:hover {
  214. top: -0.25rem;
  215. }
  216. `)
  217. })
  218. })
  219. it('should safelist negatives based on a pattern regex', () => {
  220. let config = {
  221. content: [{ raw: html`<div class="uppercase"></div>` }],
  222. safelist: [
  223. {
  224. pattern: /^bg-red-(400|500)(\/(40|50))?$/,
  225. variants: ['hover'],
  226. },
  227. {
  228. pattern: /^(fill|ring|text)-red-200\/50$/,
  229. variants: ['hover'],
  230. },
  231. ],
  232. }
  233. return run('@tailwind utilities', config).then((result) => {
  234. expect(result.css).toMatchFormattedCss(css`
  235. .bg-red-400 {
  236. --tw-bg-opacity: 1;
  237. background-color: rgb(248 113 113 / var(--tw-bg-opacity));
  238. }
  239. .bg-red-400\/40 {
  240. background-color: #f8717166;
  241. }
  242. .bg-red-400\/50 {
  243. background-color: #f8717180;
  244. }
  245. .bg-red-500 {
  246. --tw-bg-opacity: 1;
  247. background-color: rgb(239 68 68 / var(--tw-bg-opacity));
  248. }
  249. .bg-red-500\/40 {
  250. background-color: #ef444466;
  251. }
  252. .bg-red-500\/50 {
  253. background-color: #ef444480;
  254. }
  255. .fill-red-200\/50 {
  256. fill: #fecaca80;
  257. }
  258. .uppercase {
  259. text-transform: uppercase;
  260. }
  261. .text-red-200\/50 {
  262. color: #fecaca80;
  263. }
  264. .ring-red-200\/50 {
  265. --tw-ring-color: #fecaca80;
  266. }
  267. .hover\:bg-red-400:hover {
  268. --tw-bg-opacity: 1;
  269. background-color: rgb(248 113 113 / var(--tw-bg-opacity));
  270. }
  271. .hover\:bg-red-400\/40:hover {
  272. background-color: #f8717166;
  273. }
  274. .hover\:bg-red-400\/50:hover {
  275. background-color: #f8717180;
  276. }
  277. .hover\:bg-red-500:hover {
  278. --tw-bg-opacity: 1;
  279. background-color: rgb(239 68 68 / var(--tw-bg-opacity));
  280. }
  281. .hover\:bg-red-500\/40:hover {
  282. background-color: #ef444466;
  283. }
  284. .hover\:bg-red-500\/50:hover {
  285. background-color: #ef444480;
  286. }
  287. .hover\:fill-red-200\/50:hover {
  288. fill: #fecaca80;
  289. }
  290. .hover\:text-red-200\/50:hover {
  291. color: #fecaca80;
  292. }
  293. .hover\:ring-red-200\/50:hover {
  294. --tw-ring-color: #fecaca80;
  295. }
  296. `)
  297. })
  298. })
  299. it('should safelist pattern regex with !important selector', () => {
  300. let config = {
  301. content: [{ raw: html`<div class="uppercase"></div>` }],
  302. safelist: [{ pattern: /^!grid-cols-(4|5|6)$/ }],
  303. }
  304. return run('@tailwind utilities', config).then((result) => {
  305. return expect(result.css).toMatchFormattedCss(css`
  306. .\!grid-cols-4 {
  307. grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
  308. }
  309. .\!grid-cols-5 {
  310. grid-template-columns: repeat(5, minmax(0, 1fr)) !important;
  311. }
  312. .\!grid-cols-6 {
  313. grid-template-columns: repeat(6, minmax(0, 1fr)) !important;
  314. }
  315. .uppercase {
  316. text-transform: uppercase;
  317. }
  318. `)
  319. })
  320. })
  321. it('should safelist pattern regex with custom prefix along with !important selector', () => {
  322. let config = {
  323. prefix: 'tw-',
  324. content: [{ raw: html`<div class="tw-uppercase"></div>` }],
  325. safelist: [{ pattern: /^!tw-grid-cols-(4|5|6)$/ }],
  326. }
  327. return run('@tailwind utilities', config).then((result) => {
  328. return expect(result.css).toMatchFormattedCss(css`
  329. .\!tw-grid-cols-4 {
  330. grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
  331. }
  332. .\!tw-grid-cols-5 {
  333. grid-template-columns: repeat(5, minmax(0, 1fr)) !important;
  334. }
  335. .\!tw-grid-cols-6 {
  336. grid-template-columns: repeat(6, minmax(0, 1fr)) !important;
  337. }
  338. .tw-uppercase {
  339. text-transform: uppercase;
  340. }
  341. `)
  342. })
  343. })
  344. it('should safelist pattern regex having !important selector with variants', () => {
  345. let config = {
  346. content: [{ raw: html`<div class="uppercase"></div>` }],
  347. safelist: [
  348. {
  349. pattern: /^!bg-gray-(500|600|700|800)$/,
  350. variants: ['hover'],
  351. },
  352. ],
  353. }
  354. return run('@tailwind utilities', config).then((result) => {
  355. expect(result.css).toMatchFormattedCss(css`
  356. .\!bg-gray-500 {
  357. --tw-bg-opacity: 1 !important;
  358. background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important;
  359. }
  360. .\!bg-gray-600 {
  361. --tw-bg-opacity: 1 !important;
  362. background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important;
  363. }
  364. .\!bg-gray-700 {
  365. --tw-bg-opacity: 1 !important;
  366. background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important;
  367. }
  368. .\!bg-gray-800 {
  369. --tw-bg-opacity: 1 !important;
  370. background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important;
  371. }
  372. .uppercase {
  373. text-transform: uppercase;
  374. }
  375. .hover\:\!bg-gray-500:hover {
  376. --tw-bg-opacity: 1 !important;
  377. background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important;
  378. }
  379. .hover\:\!bg-gray-600:hover {
  380. --tw-bg-opacity: 1 !important;
  381. background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important;
  382. }
  383. .hover\:\!bg-gray-700:hover {
  384. --tw-bg-opacity: 1 !important;
  385. background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important;
  386. }
  387. .hover\:\!bg-gray-800:hover {
  388. --tw-bg-opacity: 1 !important;
  389. background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important;
  390. }
  391. `)
  392. })
  393. })
  394. it('should safelist multiple patterns with !important selector', () => {
  395. let config = {
  396. content: [{ raw: html`<div class="uppercase"></div>` }],
  397. safelist: [
  398. {
  399. pattern: /^!text-gray-(700|800|900)$/,
  400. variants: ['hover'],
  401. },
  402. {
  403. pattern: /^!bg-gray-(200|300|400)$/,
  404. variants: ['hover'],
  405. },
  406. ],
  407. }
  408. return run('@tailwind utilities', config).then((result) => {
  409. expect(result.css).toMatchFormattedCss(css`
  410. .\!bg-gray-200 {
  411. --tw-bg-opacity: 1 !important;
  412. background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important;
  413. }
  414. .\!bg-gray-300 {
  415. --tw-bg-opacity: 1 !important;
  416. background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important;
  417. }
  418. .\!bg-gray-400 {
  419. --tw-bg-opacity: 1 !important;
  420. background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important;
  421. }
  422. .uppercase {
  423. text-transform: uppercase;
  424. }
  425. .\!text-gray-700 {
  426. --tw-text-opacity: 1 !important;
  427. color: rgb(55 65 81 / var(--tw-text-opacity)) !important;
  428. }
  429. .\!text-gray-800 {
  430. --tw-text-opacity: 1 !important;
  431. color: rgb(31 41 55 / var(--tw-text-opacity)) !important;
  432. }
  433. .\!text-gray-900 {
  434. --tw-text-opacity: 1 !important;
  435. color: rgb(17 24 39 / var(--tw-text-opacity)) !important;
  436. }
  437. .hover\:\!bg-gray-200:hover {
  438. --tw-bg-opacity: 1 !important;
  439. background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important;
  440. }
  441. .hover\:\!bg-gray-300:hover {
  442. --tw-bg-opacity: 1 !important;
  443. background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important;
  444. }
  445. .hover\:\!bg-gray-400:hover {
  446. --tw-bg-opacity: 1 !important;
  447. background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important;
  448. }
  449. .hover\:\!text-gray-700:hover {
  450. --tw-text-opacity: 1 !important;
  451. color: rgb(55 65 81 / var(--tw-text-opacity)) !important;
  452. }
  453. .hover\:\!text-gray-800:hover {
  454. --tw-text-opacity: 1 !important;
  455. color: rgb(31 41 55 / var(--tw-text-opacity)) !important;
  456. }
  457. .hover\:\!text-gray-900:hover {
  458. --tw-text-opacity: 1 !important;
  459. color: rgb(17 24 39 / var(--tw-text-opacity)) !important;
  460. }
  461. `)
  462. })
  463. })