summaryrefslogtreecommitdiff
path: root/scss/tests/test_expression.py
blob: cf59125eb015b08a3f53e3c44aff77d60c5661ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
from scss.expression import Calculator
from scss.functions.core import CORE_LIBRARY
from scss.rule import Namespace
from scss.types import Color, List, Null, Number, String

import pytest

@pytest.fixture
def calc():
    return Calculator().evaluate_expression

def test_reference_operations():
    """Test the example expressions in the reference document:

    http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#operations
    """
    # TODO: break this into its own file and add the entire reference guide

    # Need to build the calculator manually to get at its namespace, and need
    # to use calculate() instead of evaluate_expression() so interpolation
    # works
    ns = Namespace(functions=CORE_LIBRARY)
    calc = Calculator(ns).calculate

    # Simple example
    assert calc('1in + 8pt') == Number(1.11111111, "in")

    # Division
    ns.set_variable('$width', Number(1000, "px"))
    ns.set_variable('$font-size', Number(12, "px"))
    ns.set_variable('$line-height', Number(30, "px"))
    assert calc('10px/8px') == String('10px / 8px') # plain CSS; no division
    assert calc('$width/2') == Number(500, "px")      # uses a variable; does division
    assert calc('(500px/2)') == Number(250, "px")     # uses parens; does division
    assert calc('5px + 8px/2px') == Number(9, "px")   # uses +; does division
    # TODO: Ruby doesn't include these spaces
    assert calc('#{$font-size}/#{$line-height}') == String('12px / 30px')
                                            # uses #{}; does no division

    # Color operations
    ns.set_variable('$translucent-red', Color.from_rgb(1, 0, 0, 0.5))
    ns.set_variable('$green', Color.from_name('lime'))
    assert calc('#010203 + #040506') == Color.from_hex('#050709')
    assert calc('#010203 * 2') == Color.from_hex('#020406')
    assert calc('rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75)') == Color.from_rgb(1, 1, 0, 0.75)
    assert calc('opacify($translucent-red, 0.3)') == Color.from_rgb(1, 0, 0, 0.9)
    assert calc('transparentize($translucent-red, 0.25)') == Color.from_rgb(1, 0, 0, 0.25)
    assert calc("progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}')"
                ) == "progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr=#FF00FF00, endColorstr=#80FF0000)"

    # String operations
    ns.set_variable('$value', Null())
    assert calc('e + -resize') == 'e-resize'
    assert calc('"Foo " + Bar') == '"Foo Bar"'
    assert calc('sans- + "serif"') == 'sans-serif'
    assert calc('3px + 4px auto') == '7px auto'
    assert calc('"I ate #{5 + 10} pies!"') == '"I ate 15 pies!"'
    assert calc('"I ate #{$value} pies!"') == '"I ate  pies!"'



# Operators: arithmetic (+ - * / %), unary (+ -), comparison (== != < > <= >=), boolean
# Types: numbers, colors, strings, booleans, lists
# Test them all!

def test_addition(calc):
    assert calc('123 + 456') == Number(579)

    assert calc('1px + 2px') == Number(3, "px")

    assert calc('123 + abc') == String('123abc')
    assert calc('abc + 123') == String('abc123')

    assert calc('abc + def') == String('abcdef')
    assert calc('abc + "def"') == String('abcdef')
    ret = calc('"abc" + def')
    assert ret == String('abcdef')
    assert ret.quotes == '"'
    ret = calc('"abc" + "def"')
    assert ret == String('abcdef')
    assert ret.quotes == '"'

    assert calc('#010305 + #050301') == Color.from_hex('#060606')
    assert calc('#ffffff + #ffffff') == Color.from_name('white')

def test_subtraction(calc):
    assert calc('123 - 456') == Number(-333)
    assert calc('456 - 123') == Number(333)
    # TODO test that subtracting e.g. strings doesn't work

    assert calc('#0f0f0f - #050505') == Color.from_hex('#0a0a0a')

def test_division(calc):
    assert calc('(5px / 5px)') == Number(1)
    assert calc('(1in / 6pt)') == Number(12)

def test_comparison_numeric(calc):
    assert calc('123 < 456')
    assert calc('123 <= 456')
    assert calc('123 <= 123')
    assert calc('456 > 123')
    assert calc('456 >= 123')
    assert calc('456 >= 456')
    assert calc('123 == 123')
    assert calc('123 != 456')

    # Same tests, negated
    assert not calc('123 > 456')
    assert not calc('123 >= 456')
    assert not calc('456 < 123')
    assert not calc('456 <= 123')
    assert not calc('123 != 123')
    assert not calc('123 == 456')

def test_comparison_stringerific(calc):
    assert calc('"abc" == "abc"')
    assert calc('"abc" != "xyz"')

    # Same tests, negated
    assert not calc('"abc" != "abc"')
    assert not calc('"abc" == "xyz"')

    # Interaction with other types
    assert calc('123 != "123"')

    # Sass strings don't support ordering
    for expression in (
            '"abc" < "xyz"',
            '"abc" <= "xyz"',
            '"abc" <= "abc"',
            '"xyz" > "abc"',
            '"xyz" >= "abc"',
            '"xyz" >= "xyz"',
            '123 < "456"'):

        with pytest.raises(TypeError):
            calc(expression)

def test_comparison_null(calc):
    assert calc('null == null')
    assert calc('null != 0')

    with pytest.raises(TypeError):
        calc('null < null')


def test_parse(calc):
    # Tests for some general parsing.

    assert calc('foo !important bar') == List([
        String('foo'), String('!important'), String('bar'),
    ])


# TODO write more!  i'm lazy.