1{-# LANGUAGE QuasiQuotes #-}
2{-# LANGUAGE TemplateHaskell #-}
3{-# LANGUAGE CPP #-}
4module Text.JuliusSpec (spec) where
5
6import Test.HUnit hiding (Test)
7import Test.Hspec
8
9import Prelude hiding (reverse)
10#ifdef TEST_COFFEE
11import Text.Coffee
12#endif
13import Text.Julius
14import Quoter (quote, quoteFile, quoteFileReload)
15import Data.List (intercalate)
16import qualified Data.Text.Lazy as T
17import qualified Data.List
18import qualified Data.List as L
19import Data.Text (Text, pack, unpack)
20import Data.Monoid (mappend)
21import Data.Aeson (toJSON)
22
23join :: [String] -> String
24#ifdef TEST_COFFEE
25join l = (intercalate ";\n" l)
26#else
27join = intercalate "\n"
28#endif
29
30spec :: Spec
31spec = do
32#if !(defined TEST_COFFEE || defined TEST_ROY)
33  it "julius" $ do
34    let var = "x=2"
35    let urlp = (Home, [(pack "p", pack "q")])
36    flip jelper [quote|['שלום', @{Home}, #{rawJS var}, '@?{urlp}', ^{jmixin} ]|]
37      $ intercalate " "
38        [ "['שלום',"
39        , "url, " ++ var ++ ","
40        , "'url?p=q',"
41        , "f(2) ]"
42        ]
43
44
45  it "juliusFile" $ do
46    let var = "x=2"
47    let urlp = (Home, [(pack "p", pack "q")])
48    flip jelper $(quoteFile "test/juliuses/external1.julius") $ join
49        [ "שלום"
50        , var
51        , "url"
52        , "url?p=q"
53        , "f(2)"
54        ] ++ "\n"
55
56
57  it "juliusFileReload" $ do
58    let var = "x=2"
59    let urlp = (Home, [(pack "p", pack "q")])
60    flip jelper $(quoteFileReload "test/juliuses/external1.julius") $ join
61        [ "שלום"
62        , var
63        , "url"
64        , "url?p=q"
65        , "f(2)"
66        ] ++ "\n"
67#endif
68
69{- TODO
70  it "juliusFileDebugChange" $ do
71    let var = "somevar"
72        test result = jelper result $(juliusFileDebug "test/juliuses/external2.julius")
73    writeFile "test/juliuses/external2.julius" "var #{var} = 1;"
74    test "var somevar = 1;"
75    writeFile "test/juliuses/external2.julius" "var #{var} = 2;"
76    test "var somevar = 2;"
77    writeFile "test/juliuses/external2.julius" "var #{var} = 1;"
78    -}
79
80
81  it "julius module names" $
82    let foo = "foo"
83        double = 3.14 :: Double
84        int = -5 :: Int
85#ifdef TEST_COFFEE
86    in jelper "var _this = this;\n\n(function(shakespeare_var_rawJSDataListreversefoo, shakespeare_var_rawJSLreversefoo, shakespeare_var_rawJSshowdouble, shakespeare_var_rawJSshowint) {\n  return [shakespeare_var_rawJSDataListreversefoo, shakespeare_var_rawJSLreversefoo, shakespeare_var_rawJSshowdouble, shakespeare_var_rawJSshowint];\n})(oof, oof, 3.14, -5);\n"
87#else
88#  ifdef TEST_ROY
89    in jelper "(function(shakespeare_var_rawJSDataListreversefoo, shakespeare_var_rawJSLreversefoo, shakespeare_var_rawJSshowdouble, shakespeare_var_rawJSshowint) {\n    return [shakespeare_var_rawJSDataListreversefoo, shakespeare_var_rawJSLreversefoo, shakespeare_var_rawJSshowdouble, shakespeare_var_rawJSshowint];\n})(oof, oof, 3.14, -5);\n"
90#  else
91    in jelper "[oof, oof, 3.14, -5]"
92#  endif
93#endif
94         [quote|[#{rawJS $ Data.List.reverse foo}, #{rawJS $ L.reverse foo}, #{rawJS $ show double}, #{rawJS $ show int}]|]
95
96
97-- not valid coffeescript
98#if !(defined TEST_COFFEE || defined TEST_ROY)
99  it "single dollar at and caret" $ do
100    jelper "$@^" [quote|$@^|]
101    jelper "#{@{^{" [quote|#\{@\{^\{|]
102#endif
103
104  it "dollar operator" $ do
105    let val = (1 :: Int, (2 :: Int, 3 :: Int))
106#if (defined TEST_COFFEE)
107    jelper "var _this = this;\n\n(function(shakespeare_var_rawJSshowfstsndval) {\n  return shakespeare_var_rawJSshowfstsndval;\n})(2);\n" [quote|#{ rawJS $ show $ fst $ snd val }|]
108    jelper "var _this = this;\n\n(function(shakespeare_var_rawJSshowfstsndval) {\n  return shakespeare_var_rawJSshowfstsndval;\n})(2);\n" [quote|#{ rawJS $ show $ fst $ snd val }|]
109#else
110
111#  if (defined TEST_ROY)
112    jelper "(function(shakespeare_var_rawJSshowfstsndval) {\n    return shakespeare_var_rawJSshowfstsndval;\n})(2);\n" [quote|#{ rawJS $ show $ fst $ snd val }|]
113    jelper "(function(shakespeare_var_rawJSshowfstsndval) {\n    return shakespeare_var_rawJSshowfstsndval;\n})(2);\n" [quote|#{ rawJS $ show $ fst $ snd val }|]
114
115#  else
116    jelper "2" [quote|#{ rawJS $ show $ fst $ snd val }|]
117    jelper "2" [quote|#{ rawJS $ show $ fst $ snd $ val}|]
118#  endif
119#endif
120
121#if (defined TEST_ROY)
122  it "roy function wrapper" $ do
123    let royInsert = rawJS "\"royInsert\""
124    jelper "(function(shakespeare_var_royInsert) {\n    var roy = {\n        \"royInsert\": shakespeare_var_royInsert\n    };\n    return console.log(roy);\n})(\"royInsert\");\n" [quote|
125let roy = { royInsert: #{royInsert} }
126console.log roy
127|]
128#endif
129  it "empty file" $ jelper "" [quote||]
130
131  it "JSON data" $ jelper "\"Hello \\\"World!\\\"\"" [julius|#{toJSON "Hello \"World!\""}|]
132
133  it "< escaping" $ jelper "\"\\u003c\"" [julius|#{toJSON "<"}|]
134
135  it "> escaping" $ jelper "\"\\u003e\"" [julius|#{toJSON ">"}|]
136
137  it "& escaping" $ jelper "\"\\u0026\"" [julius|#{toJSON "&"}|]
138
139  it "boolean interpolation" $ jelper
140    "true false true false true false"
141    [julius|#{True} #{False} #{toJSON True} #{toJSON False} #{rawJS True} #{rawJS False}|]
142
143  it "^\\ should not be escaped" $ jelper
144     "var re = /[^\\r]/;"
145     [julius|var re = /[^\r]/;|]
146
147  it "^\\{ should be escaped" $ jelper
148     "var re = /[^{]/;"
149     [julius|var re = /[^\{]/;|]
150
151  it "produces escaped JavaScript" $ jelper
152     "\"yay\""
153     [julius|#{"yay"}|]
154
155data Url = Home | Sub SubUrl
156data SubUrl = SubUrl
157render :: Url -> [(Text, Text)] -> Text
158render Home qs = pack "url" `mappend` showParams qs
159render (Sub SubUrl) qs = pack "suburl" `mappend` showParams qs
160
161showParams :: [(Text, Text)] -> Text
162showParams [] = pack ""
163showParams z =
164    pack $ '?' : intercalate "&" (map go z)
165  where
166    go (x, y) = go' x ++ '=' : go' y
167    go' = concatMap encodeUrlChar . unpack
168
169-- | Taken straight from web-encodings; reimplemented here to avoid extra
170-- dependencies.
171encodeUrlChar :: Char -> String
172encodeUrlChar c
173    -- List of unreserved characters per RFC 3986
174    -- Gleaned from http://en.wikipedia.org/wiki/Percent-encoding
175    | 'A' <= c && c <= 'Z' = [c]
176    | 'a' <= c && c <= 'z' = [c]
177    | '0' <= c && c <= '9' = [c]
178encodeUrlChar c@'-' = [c]
179encodeUrlChar c@'_' = [c]
180encodeUrlChar c@'.' = [c]
181encodeUrlChar c@'~' = [c]
182encodeUrlChar ' ' = "+"
183encodeUrlChar y =
184    let (a, c) = fromEnum y `divMod` 16
185        b = a `mod` 16
186        showHex' x
187            | x < 10 = toEnum $ x + (fromEnum '0')
188            | x < 16 = toEnum $ x - 10 + (fromEnum 'A')
189            | otherwise = error $ "Invalid argument to showHex: " ++ show x
190     in ['%', showHex' b, showHex' c]
191
192
193
194
195#ifndef TEST_ROY
196jmixin :: JavascriptUrl u
197jmixin = [quote|f(2)|]
198#endif
199
200jelper :: String -> JavascriptUrl Url -> Assertion
201jelper res h = do
202  T.pack res @=? renderJavascriptUrl render h
203
204instance Show Url where
205    show _ = "FIXME remove this instance show Url"
206