@@ -1,16 +1,25 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DataKinds #-}
23{-# LANGUAGE FlexibleInstances #-}
34{-# LANGUAGE QuasiQuotes #-}
45{-# LANGUAGE ScopedTypeVariables #-}
56{-# LANGUAGE TypeFamilies #-}
67{-# LANGUAGE TypeOperators #-}
8+{-# LANGUAGE OverloadedStrings #-}
79{-# OPTIONS_GHC -fno-warn-orphans #-}
810module Servant.JSSpec where
911
1012import Data.Either (isRight)
13+#if !MIN_VERSION_base(4,8,0)
14+import Data.Monoid ((<>),mconcat)
15+#else
16+import Data.Monoid ((<>))
17+#endif
1118import Data.Proxy
12−import Language.ECMAScript3.Parser (parseFromString)
13−import Test.Hspec
19+import Data.Text (Text)
20+import qualified Data.Text as T
21+import Language.ECMAScript3.Parser (program, parse)
22+import Test.Hspec hiding (shouldContain, shouldNotContain)
1423
1524import Servant.JS
1625import Servant.JS.Internal
@@ -20,22 +29,22 @@ import qualified Servant.JS.JQuery as JQ
2029import qualified Servant.JS.Vanilla as JS
2130import Servant.JSSpec.CustomHeaders
2231
23−type TestAPI = "simple" :> ReqBody '[JSON,FormUrlEncoded] String :> Post '[JSON] Bool
32+type TestAPI = "simple" :> ReqBody '[JSON,FormUrlEncoded] Text :> Post '[JSON] Bool
2433 :<|> "has.extension" :> Get '[FormUrlEncoded,JSON] Bool
2534
2635type TopLevelRawAPI = "something" :> Get '[JSON] Int
2736 :<|> Raw
2837
29−type HeaderHandlingAPI = "something" :> Header "Foo" String
38+type HeaderHandlingAPI = "something" :> Header "Foo" Text
3039 :> Get '[JSON] Int
3140
32−type CustomAuthAPI = "something" :> Authorization "Basic" String
41+type CustomAuthAPI = "something" :> Authorization "Basic" Text
3342 :> Get '[JSON] Int
3443
35−type CustomHeaderAPI = "something" :> MyLovelyHorse String
44+type CustomHeaderAPI = "something" :> MyLovelyHorse Text
3645 :> Get '[JSON] Int
3746
38−type CustomHeaderAPI2 = "something" :> WhatsForDinner String
47+type CustomHeaderAPI2 = "something" :> WhatsForDinner Text
3948 :> Get '[JSON] Int
4049
4150headerHandlingProxy :: Proxy HeaderHandlingAPI
@@ -81,12 +90,18 @@ spec = describe "Servant.JQuery" $ do
8190 axiosSpec
8291 --angularSpec AngularCustom
8392
93+shouldContain :: Text -> Text -> Expectation
94+a `shouldContain` b = shouldSatisfy a (T.isInfixOf b)
95+
96+shouldNotContain :: Text -> Text -> Expectation
97+a `shouldNotContain` b = shouldNotSatisfy a (T.isInfixOf b)
98+
8499axiosSpec :: Spec
85100axiosSpec = describe specLabel $ do
86101 it "should add withCredentials when needed" $ do
87102 let jsText = genJS withCredOpts $ listFromAPI (Proxy :: Proxy TestAPI)
88103 output jsText
89− jsText `shouldContain` ("withCredentials: true")
104+ jsText `shouldContain` "withCredentials: true"
90105 it "should add xsrfCookieName when needed" $ do
91106 let jsText = genJS cookieOpts $ listFromAPI (Proxy :: Proxy TestAPI)
92107 output jsText
@@ -101,79 +116,80 @@ axiosSpec = describe specLabel $ do
101116 withCredOpts = AX.defAxiosOptions { AX.withCredentials = True }
102117 cookieOpts = AX.defAxiosOptions { AX.xsrfCookieName = Just "MyXSRFcookie" }
103118 headerOpts = AX.defAxiosOptions { AX.xsrfHeaderName = Just "MyXSRFheader" }
104− genJS :: AxiosOptions -> [AjaxReq] -> String
105− genJS opts req = concatMap (AX.generateAxiosJS opts) req
119+ genJS :: AxiosOptions -> [AjaxReq] -> Text
120+ genJS opts req = mconcat . map (AX.generateAxiosJS opts) $ req
106121
107122angularSpec :: TestNames -> Spec
108123angularSpec test = describe specLabel $ do
109124 it "should implement a service globally" $ do
110125 let jsText = genJS $ listFromAPI (Proxy :: Proxy TestAPI)
111126 output jsText
112− jsText `shouldContain` (".service('" ++ testName ++ "'")
127+ jsText `shouldContain` (".service('" <> testName <> "'")
113128
114129 it "should depend on $http service globally" $ do
115130 let jsText = genJS $ listFromAPI (Proxy :: Proxy TestAPI)
116131 output jsText
117− jsText `shouldContain` ("('" ++ testName ++ "', function($http) {")
132+ jsText `shouldContain` ("('" <> testName <> "', function($http) {")
118133
119134 it "should not depend on $http service in handlers" $ do
120135 let jsText = genJS $ listFromAPI (Proxy :: Proxy TestAPI)
121136 output jsText
122137 jsText `shouldNotContain` "getsomething($http, "
123138 where
124− specLabel = "AngularJS(" ++ (show test) ++ ")"
139+ specLabel = "AngularJS(" <> (show test) <> ")"
125140 output _ = return ()
126141 testName = "MyService"
127142 ngOpts = NG.defAngularOptions { NG.serviceName = testName }
128143 genJS req = NG.angularService ngOpts req
129144
130−generateJSSpec :: TestNames -> (AjaxReq -> String) -> Spec
145+generateJSSpec :: TestNames -> (AjaxReq -> Text) -> Spec
131146generateJSSpec n gen = describe specLabel $ do
147+ let parseFromText = parse program ""
132148 it "should generate valid javascript" $ do
133− let s = jsForAPI (Proxy :: Proxy TestAPI) (concatMap gen)
134− parseFromString s `shouldSatisfy` isRight
149+ let s = jsForAPI (Proxy :: Proxy TestAPI) (mconcat . map gen)
150+ parseFromText s `shouldSatisfy` isRight
135151
136152 it "should use non-empty function names" $ do
137153 let (_ :<|> topLevel) = javascript (Proxy :: Proxy TopLevelRawAPI)
138154 output $ genJS (topLevel "GET")
139− parseFromString (genJS $ topLevel "GET") `shouldSatisfy` isRight
155+ parseFromText (genJS $ topLevel "GET") `shouldSatisfy` isRight
140156
141157 it "should handle simple HTTP headers" $ do
142158 let jsText = genJS $ javascript headerHandlingProxy
143159 output jsText
144− parseFromString jsText `shouldSatisfy` isRight
160+ parseFromText jsText `shouldSatisfy` isRight
145161 jsText `shouldContain` "headerFoo"
146162 jsText `shouldContain` (header n "Foo" $ "headerFoo")
147163
148164 it "should handle complex HTTP headers" $ do
149165 let jsText = genJS $ javascript customAuthProxy
150166 output jsText
151− parseFromString jsText `shouldSatisfy` isRight
167+ parseFromText jsText `shouldSatisfy` isRight
152168 jsText `shouldContain` "headerAuthorization"
153169 jsText `shouldContain` (header n "Authorization" $ "\"Basic \" + headerAuthorization")
154170
155171 it "should handle complex, custom HTTP headers" $ do
156172 let jsText = genJS $ javascript customHeaderProxy
157173 output jsText
158− parseFromString jsText `shouldSatisfy` isRight
174+ parseFromText jsText `shouldSatisfy` isRight
159175 jsText `shouldContain` "headerXMyLovelyHorse"
160176 jsText `shouldContain` (header n "X-MyLovelyHorse" $ "\"I am good friends with \" + headerXMyLovelyHorse")
161177
162178 it "should handle complex, custom HTTP headers (template replacement)" $ do
163179 let jsText = genJS $ javascript customHeaderProxy2
164180 output jsText
165− parseFromString jsText `shouldSatisfy` isRight
181+ parseFromText jsText `shouldSatisfy` isRight
166182 jsText `shouldContain` "headerXWhatsForDinner"
167183 jsText `shouldContain` (header n "X-WhatsForDinner" $ "\"I would like \" + headerXWhatsForDinner + \" with a cherry on top.\"")
168184
169185 it "can generate the whole javascript code string at once with jsForAPI" $ do
170− let jsStr = jsForAPI (Proxy :: Proxy TestAPI) (concatMap gen)
171− parseFromString jsStr `shouldSatisfy` isRight
186+ let jsStr = jsForAPI (Proxy :: Proxy TestAPI) (mconcat . map gen)
187+ parseFromText jsStr `shouldSatisfy` isRight
172188 where
173− specLabel = "generateJS(" ++ (show n) ++ ")"
189+ specLabel = "generateJS(" <> (show n) <> ")"
174190 output _ = return ()
175191 genJS req = gen req
176− header :: TestNames -> String -> String -> String
192+ header :: TestNames -> Text -> Text -> Text
177193 header v headerName headerValue
178− | v `elem` [Vanilla, VanillaCustom] = "xhr.setRequestHeader(\"" ++ headerName ++ "\", " ++ headerValue ++ ");\n"
179− | otherwise = "headers: { \"" ++ headerName ++ "\": " ++ headerValue ++ " }\n"
194+ | v `elem` [Vanilla, VanillaCustom] = "xhr.setRequestHeader(\"" <> headerName <> "\", " <> headerValue <> ");\n"
195+ | otherwise = "headers: { \"" <> headerName <> "\": " <> headerValue <> " }\n"