python-css.mustache 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import unittest
  4. import cssbeautifier
  5. class CSSBeautifierTest(unittest.TestCase):
  6. def resetOptions(self):
  7. false = False
  8. true = True
  9. self.options = cssbeautifier.default_options()
  10. self.options.indent_size = 1
  11. self.options.indent_char = '\t'
  12. self.options.selector_separator_newline = true
  13. self.options.end_with_newline = false
  14. self.options.newline_between_rules = false
  15. def testGenerated(self):
  16. self.resetOptions()
  17. test_fragment = self.decodesto
  18. t = self.decodesto
  19. false = False
  20. true = True
  21. {{#default_options}} self.options.{{name}} = {{&value}}
  22. {{/default_options}}
  23. {{#groups}}
  24. {{^matrix}}
  25. # {{&name}}
  26. {{#options}}
  27. self.options.{{name}} = {{&value}}
  28. {{/options}}
  29. {{#tests}}
  30. {{#test_line}}.{{/test_line}}
  31. {{/tests}}
  32. {{/matrix}}
  33. {{#matrix}}
  34. # {{&name}} - ({{#matrix_context_string}}.{{/matrix_context_string}})
  35. {{#options}}
  36. self.options.{{name}} = {{&value}}
  37. {{/options}}
  38. {{#tests}}
  39. {{#test_line}}.{{/test_line}}
  40. {{/tests}}
  41. {{/matrix}}
  42. {{/groups}}
  43. def testNewline(self):
  44. self.resetOptions()
  45. t = self.decodesto
  46. self.options.end_with_newline = True
  47. t("", "\n")
  48. t("\n", "\n")
  49. t(".tabs{}\n", ".tabs {}\n")
  50. t(".tabs{}", ".tabs {}\n")
  51. def testBasics(self):
  52. self.resetOptions()
  53. t = self.decodesto
  54. t("", "")
  55. t("\n", "")
  56. t(".tabs{}\n", ".tabs {}")
  57. t(".tabs{}", ".tabs {}")
  58. t(".tabs{color:red}", ".tabs {\n\tcolor: red\n}")
  59. t(".tabs{color:rgb(255, 255, 0)}", ".tabs {\n\tcolor: rgb(255, 255, 0)\n}")
  60. t(".tabs{background:url('back.jpg')}", ".tabs {\n\tbackground: url('back.jpg')\n}")
  61. t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
  62. t("@media print {.tab{}}", "@media print {\n\t.tab {}\n}")
  63. t("@media print {.tab{background-image:url(foo@2x.png)}}", "@media print {\n\t.tab {\n\t\tbackground-image: url(foo@2x.png)\n\t}\n}")
  64. t("a:before {\n" +
  65. "\tcontent: 'a{color:black;}\"\"\\'\\'\"\\n\\n\\na{color:black}\';\n" +
  66. "}");
  67. # may not eat the space before "["
  68. t('html.js [data-custom="123"] {\n\topacity: 1.00;\n}')
  69. t('html.js *[data-custom="123"] {\n\topacity: 1.00;\n}')
  70. # lead-in whitespace determines base-indent.
  71. # lead-in newlines are stripped.
  72. t("\n\na, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
  73. t(" a, img {padding: 0.2px}", " a,\n img {\n \tpadding: 0.2px\n }")
  74. t(" \t \na, img {padding: 0.2px}", " \t a,\n \t img {\n \t \tpadding: 0.2px\n \t }")
  75. t("\n\n a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
  76. def testSeperateSelectors(self):
  77. self.resetOptions()
  78. t = self.decodesto
  79. t("#bla, #foo{color:red}", "#bla,\n#foo {\n\tcolor: red\n}")
  80. t("a, img {padding: 0.2px}", "a,\nimg {\n\tpadding: 0.2px\n}")
  81. def testBlockNesting(self):
  82. self.resetOptions()
  83. t = self.decodesto
  84. t("#foo {\n\tbackground-image: url(foo@2x.png);\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
  85. t("@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo@2x.png);\n\t}\n\t@font-face {\n\t\tfont-family: 'Bitstream Vera Serif Bold';\n\t\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n\t}\n}")
  86. # @font-face {
  87. # font-family: 'Bitstream Vera Serif Bold';
  88. # src: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');
  89. # }
  90. # @media screen {
  91. # #foo:hover {
  92. # background-image: url(foo.png);
  93. # }
  94. # @media screen and (min-device-pixel-ratio: 2) {
  95. # @font-face {
  96. # font-family: 'Helvetica Neue'
  97. # }
  98. # #foo:hover {
  99. # background-image: url(foo@2x.png);
  100. # }
  101. # }
  102. # }
  103. t("@font-face {\n\tfont-family: 'Bitstream Vera Serif Bold';\n\tsrc: url('http://developer.mozilla.org/@api/deki/files/2934/=VeraSeBd.ttf');\n}\n@media screen {\n\t#foo:hover {\n\t\tbackground-image: url(foo.png);\n\t}\n\t@media screen and (min-device-pixel-ratio: 2) {\n\t\t@font-face {\n\t\t\tfont-family: 'Helvetica Neue'\n\t\t}\n\t\t#foo:hover {\n\t\t\tbackground-image: url(foo@2x.png);\n\t\t}\n\t}\n}")
  104. def testOptions(self):
  105. self.resetOptions()
  106. self.options.indent_size = 2
  107. self.options.indent_char = ' '
  108. self.options.selector_separator_newline = False
  109. t = self.decodesto
  110. # pseudo-classes and pseudo-elements
  111. t("#foo:hover {\n background-image: url(foo@2x.png)\n}")
  112. t("#foo *:hover {\n color: purple\n}")
  113. t("::selection {\n color: #ff0000;\n}")
  114. # TODO: don't break nested pseduo-classes
  115. t("@media screen {.tab,.bat:hover {color:red}}", "@media screen {\n .tab, .bat:hover {\n color: red\n }\n}")
  116. # particular edge case with braces and semicolons inside tags that allows custom text
  117. t( "a:not(\"foobar\\\";{}omg\"){\ncontent: 'example\\';{} text';\ncontent: \"example\\\";{} text\";}",
  118. "a:not(\"foobar\\\";{}omg\") {\n content: 'example\\';{} text';\n content: \"example\\\";{} text\";\n}")
  119. def testLessCss(self):
  120. self.resetOptions()
  121. t = self.decodesto
  122. t('.well{ \n @well-bg:@bg-color;@well-fg:@fg-color;}','.well {\n\t@well-bg: @bg-color;\n\t@well-fg: @fg-color;\n}')
  123. t('.well {&.active {\nbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;}}',
  124. '.well {\n' +
  125. '\t&.active {\n' +
  126. '\t\tbox-shadow: 0 1px 1px @border-color, 1px 0 1px @border-color;\n' +
  127. '\t}\n' +
  128. '}')
  129. t('a {\n' +
  130. '\tcolor: blue;\n' +
  131. '\t&:hover {\n' +
  132. '\t\tcolor: green;\n' +
  133. '\t}\n' +
  134. '\t& & &&&.active {\n' +
  135. '\t\tcolor: green;\n' +
  136. '\t}\n' +
  137. '}')
  138. # Not sure if this is sensible
  139. # but I believe it is correct to not remove the space in "&: hover".
  140. t('a {\n' +
  141. '\t&: hover {\n' +
  142. '\t\tcolor: green;\n' +
  143. '\t}\n' +
  144. '}');
  145. # import
  146. t('@import "test";');
  147. # don't break nested pseudo-classes
  148. t("a:first-child{color:red;div:first-child{color:black;}}",
  149. "a:first-child {\n\tcolor: red;\n\tdiv:first-child {\n\t\tcolor: black;\n\t}\n}");
  150. # handle SASS/LESS parent reference
  151. t("div{&:first-letter {text-transform: uppercase;}}",
  152. "div {\n\t&:first-letter {\n\t\ttext-transform: uppercase;\n\t}\n}");
  153. # nested modifiers (&:hover etc)
  154. t(".tabs{&:hover{width:10px;}}", ".tabs {\n\t&:hover {\n\t\twidth: 10px;\n\t}\n}")
  155. t(".tabs{&.big{width:10px;}}", ".tabs {\n\t&.big {\n\t\twidth: 10px;\n\t}\n}")
  156. t(".tabs{&>big{width:10px;}}", ".tabs {\n\t&>big {\n\t\twidth: 10px;\n\t}\n}")
  157. t(".tabs{&+.big{width:10px;}}", ".tabs {\n\t&+.big {\n\t\twidth: 10px;\n\t}\n}")
  158. # nested rules
  159. t(".tabs{.child{width:10px;}}", ".tabs {\n\t.child {\n\t\twidth: 10px;\n\t}\n}")
  160. # variables
  161. t("@myvar:10px;.tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
  162. t("@myvar:10px; .tabs{width:10px;}", "@myvar: 10px;\n.tabs {\n\twidth: 10px;\n}")
  163. def decodesto(self, input, expectation=None):
  164. if expectation == None:
  165. expectation = input
  166. self.assertMultiLineEqual(
  167. cssbeautifier.beautify(input, self.options), expectation)
  168. # if the expected is different from input, run it again
  169. # expected output should be unchanged when run twice.
  170. if not expectation != input:
  171. self.assertMultiLineEqual(
  172. cssbeautifier.beautify(expectation, self.options), expectation)
  173. # Everywhere we do newlines, they should be replaced with opts.eol
  174. self.options.eol = '\r\\n';
  175. expectation = expectation.replace('\n', '\r\n')
  176. self.assertMultiLineEqual(
  177. cssbeautifier.beautify(input, self.options), expectation)
  178. input = input.replace('\n', '\r\n')
  179. self.assertMultiLineEqual(
  180. cssbeautifier.beautify(input, self.options), expectation)
  181. self.options.eol = '\n'
  182. if __name__ == '__main__':
  183. unittest.main()