test_css.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import pytest
  2. from pandas.util import testing as tm
  3. from pandas.io.formats.css import CSSResolver, CSSWarning
  4. def assert_resolves(css, props, inherited=None):
  5. resolve = CSSResolver()
  6. actual = resolve(css, inherited=inherited)
  7. assert props == actual
  8. def assert_same_resolution(css1, css2, inherited=None):
  9. resolve = CSSResolver()
  10. resolved1 = resolve(css1, inherited=inherited)
  11. resolved2 = resolve(css2, inherited=inherited)
  12. assert resolved1 == resolved2
  13. @pytest.mark.parametrize('name,norm,abnorm', [
  14. ('whitespace', 'hello: world; foo: bar',
  15. ' \t hello \t :\n world \n ; \n foo: \tbar\n\n'),
  16. ('case', 'hello: world; foo: bar', 'Hello: WORLD; foO: bar'),
  17. ('empty-decl', 'hello: world; foo: bar',
  18. '; hello: world;; foo: bar;\n; ;'),
  19. ('empty-list', '', ';'),
  20. ])
  21. def test_css_parse_normalisation(name, norm, abnorm):
  22. assert_same_resolution(norm, abnorm)
  23. @pytest.mark.parametrize(
  24. 'invalid_css,remainder', [
  25. # No colon
  26. ('hello-world', ''),
  27. ('border-style: solid; hello-world', 'border-style: solid'),
  28. ('border-style: solid; hello-world; font-weight: bold',
  29. 'border-style: solid; font-weight: bold'),
  30. # Unclosed string fail
  31. # Invalid size
  32. ('font-size: blah', 'font-size: 1em'),
  33. ('font-size: 1a2b', 'font-size: 1em'),
  34. ('font-size: 1e5pt', 'font-size: 1em'),
  35. ('font-size: 1+6pt', 'font-size: 1em'),
  36. ('font-size: 1unknownunit', 'font-size: 1em'),
  37. ('font-size: 10', 'font-size: 1em'),
  38. ('font-size: 10 pt', 'font-size: 1em'),
  39. ])
  40. def test_css_parse_invalid(invalid_css, remainder):
  41. with tm.assert_produces_warning(CSSWarning):
  42. assert_same_resolution(invalid_css, remainder)
  43. # TODO: we should be checking that in other cases no warnings are raised
  44. @pytest.mark.parametrize(
  45. 'shorthand,expansions',
  46. [('margin', ['margin-top', 'margin-right',
  47. 'margin-bottom', 'margin-left']),
  48. ('padding', ['padding-top', 'padding-right',
  49. 'padding-bottom', 'padding-left']),
  50. ('border-width', ['border-top-width', 'border-right-width',
  51. 'border-bottom-width', 'border-left-width']),
  52. ('border-color', ['border-top-color', 'border-right-color',
  53. 'border-bottom-color', 'border-left-color']),
  54. ('border-style', ['border-top-style', 'border-right-style',
  55. 'border-bottom-style', 'border-left-style']),
  56. ])
  57. def test_css_side_shorthands(shorthand, expansions):
  58. top, right, bottom, left = expansions
  59. assert_resolves('{shorthand}: 1pt'.format(shorthand=shorthand),
  60. {top: '1pt', right: '1pt',
  61. bottom: '1pt', left: '1pt'})
  62. assert_resolves('{shorthand}: 1pt 4pt'.format(shorthand=shorthand),
  63. {top: '1pt', right: '4pt',
  64. bottom: '1pt', left: '4pt'})
  65. assert_resolves('{shorthand}: 1pt 4pt 2pt'.format(shorthand=shorthand),
  66. {top: '1pt', right: '4pt',
  67. bottom: '2pt', left: '4pt'})
  68. assert_resolves('{shorthand}: 1pt 4pt 2pt 0pt'.format(shorthand=shorthand),
  69. {top: '1pt', right: '4pt',
  70. bottom: '2pt', left: '0pt'})
  71. with tm.assert_produces_warning(CSSWarning):
  72. assert_resolves(
  73. '{shorthand}: 1pt 1pt 1pt 1pt 1pt'.format(shorthand=shorthand), {})
  74. @pytest.mark.parametrize('style,inherited,equiv', [
  75. ('margin: 1px; margin: 2px', '',
  76. 'margin: 2px'),
  77. ('margin: 1px', 'margin: 2px',
  78. 'margin: 1px'),
  79. ('margin: 1px; margin: inherit', 'margin: 2px',
  80. 'margin: 2px'),
  81. ('margin: 1px; margin-top: 2px', '',
  82. 'margin-left: 1px; margin-right: 1px; ' +
  83. 'margin-bottom: 1px; margin-top: 2px'),
  84. ('margin-top: 2px', 'margin: 1px',
  85. 'margin: 1px; margin-top: 2px'),
  86. ('margin: 1px', 'margin-top: 2px',
  87. 'margin: 1px'),
  88. ('margin: 1px; margin-top: inherit', 'margin: 2px',
  89. 'margin: 1px; margin-top: 2px'),
  90. ])
  91. def test_css_precedence(style, inherited, equiv):
  92. resolve = CSSResolver()
  93. inherited_props = resolve(inherited)
  94. style_props = resolve(style, inherited=inherited_props)
  95. equiv_props = resolve(equiv)
  96. assert style_props == equiv_props
  97. @pytest.mark.parametrize('style,equiv', [
  98. ('margin: 1px; margin-top: inherit',
  99. 'margin-bottom: 1px; margin-right: 1px; margin-left: 1px'),
  100. ('margin-top: inherit', ''),
  101. ('margin-top: initial', ''),
  102. ])
  103. def test_css_none_absent(style, equiv):
  104. assert_same_resolution(style, equiv)
  105. @pytest.mark.parametrize('size,resolved', [
  106. ('xx-small', '6pt'),
  107. ('x-small', '{pt:f}pt'.format(pt=7.5)),
  108. ('small', '{pt:f}pt'.format(pt=9.6)),
  109. ('medium', '12pt'),
  110. ('large', '{pt:f}pt'.format(pt=13.5)),
  111. ('x-large', '18pt'),
  112. ('xx-large', '24pt'),
  113. ('8px', '6pt'),
  114. ('1.25pc', '15pt'),
  115. ('.25in', '18pt'),
  116. ('02.54cm', '72pt'),
  117. ('25.4mm', '72pt'),
  118. ('101.6q', '72pt'),
  119. ('101.6q', '72pt'),
  120. ])
  121. @pytest.mark.parametrize('relative_to', # invariant to inherited size
  122. [None, '16pt'])
  123. def test_css_absolute_font_size(size, relative_to, resolved):
  124. if relative_to is None:
  125. inherited = None
  126. else:
  127. inherited = {'font-size': relative_to}
  128. assert_resolves('font-size: {size}'.format(size=size),
  129. {'font-size': resolved}, inherited=inherited)
  130. @pytest.mark.parametrize('size,relative_to,resolved', [
  131. ('1em', None, '12pt'),
  132. ('1.0em', None, '12pt'),
  133. ('1.25em', None, '15pt'),
  134. ('1em', '16pt', '16pt'),
  135. ('1.0em', '16pt', '16pt'),
  136. ('1.25em', '16pt', '20pt'),
  137. ('1rem', '16pt', '12pt'),
  138. ('1.0rem', '16pt', '12pt'),
  139. ('1.25rem', '16pt', '15pt'),
  140. ('100%', None, '12pt'),
  141. ('125%', None, '15pt'),
  142. ('100%', '16pt', '16pt'),
  143. ('125%', '16pt', '20pt'),
  144. ('2ex', None, '12pt'),
  145. ('2.0ex', None, '12pt'),
  146. ('2.50ex', None, '15pt'),
  147. ('inherit', '16pt', '16pt'),
  148. ('smaller', None, '10pt'),
  149. ('smaller', '18pt', '15pt'),
  150. ('larger', None, '{pt:f}pt'.format(pt=14.4)),
  151. ('larger', '15pt', '18pt'),
  152. ])
  153. def test_css_relative_font_size(size, relative_to, resolved):
  154. if relative_to is None:
  155. inherited = None
  156. else:
  157. inherited = {'font-size': relative_to}
  158. assert_resolves('font-size: {size}'.format(size=size),
  159. {'font-size': resolved}, inherited=inherited)