@@ -2,22 +2,25 @@ module Lunapark.Types where
3import Prelude
3import Prelude
5−import Control.Alt ((<|>))
6−import CSS.Selector as CSS
7import CSS.Render as CSSR
5import CSS.Render as CSSR
6+import CSS.Selector as CSS
7+import Control.Alt ((<|>))
8import Data.Argonaut (Json, (.?))
8import Data.Argonaut (Json, (.?))
9import Data.Argonaut as J
9import Data.Argonaut as J
10import Data.Array as A
10import Data.Array as A
11−import Data.Foldable as F
12−import Data.Time.Duration (Milliseconds(..))
13import Data.Either (Either(..))
11import Data.Either (Either(..))
12+import Data.Traversable as F
13+import Data.Map as Map
14import Data.Maybe (Maybe(..))
14import Data.Maybe (Maybe(..))
15import Data.Newtype (class Newtype, un)
15import Data.Newtype (class Newtype, un)
16import Data.StrMap as SM
16import Data.StrMap as SM
17+import Data.String as Str
18+import Data.Time.Duration (Milliseconds(..))
17import Data.Tuple (Tuple(..))
19import Data.Tuple (Tuple(..))
20+import Data.Variant as V
18import Data.XPath.Types as X
21import Data.XPath.Types as X
19import Node.Encoding as NE
22import Node.Encoding as NE
20−import Unsafe.Coerce (unsafeCoerce)
23+import Node.Path (FilePath)
22newtype SessionId = SessionId String
25newtype SessionId = SessionId String
23derive instance newtypeSessionId ∷ Newtype SessionId _
26derive instance newtypeSessionId ∷ Newtype SessionId _
@@ -40,20 +43,12 @@ encodeElement (Element eid) = J.encodeJson $ SM.fromFoldable
40 , Tuple "ELEMENT" eid
43 , Tuple "ELEMENT" eid
45−encodeCapabilities ∷ Capabilities → Json
46−encodeCapabilities = unsafeCoerce
48−decodeCapabilities ∷ Json → Either String Capabilities
49−decodeCapabilities js = Left "unimplemented"
51decodeSessionId ∷ Json → Either String SessionId
46decodeSessionId ∷ Json → Either String SessionId
52decodeSessionId = map SessionId <<< J.decodeJson
47decodeSessionId = map SessionId <<< J.decodeJson
54type CreateSessionResponse =
49type CreateSessionResponse =
55 { session ∷ SessionId
50 { session ∷ SessionId
56− , capabilities ∷ Capabilities
51+ , capabilities ∷ Array Capability
59decodeCreateSessionResponse ∷ Json → Either String CreateSessionResponse
54decodeCreateSessionResponse ∷ Json → Either String CreateSessionResponse
@@ -90,6 +85,13 @@ encodeTimeouts r = J.encodeJson $ SM.fromFoldable
90 , Tuple "implicit" (un Milliseconds r.implicit)
85 , Tuple "implicit" (un Milliseconds r.implicit)
88+encodeLegacyTimeouts ∷ Timeouts → Array Json
89+encodeLegacyTimeouts r =
90+ [ J.encodeJson $ SM.singleton "script" $ un Milliseconds r.script
91+ , J.encodeJson $ SM.singleton "implicit" $ un Milliseconds r.implicit
92+ , J.encodeJson $ SM.singleton "page load" $ un Milliseconds r.pageLoad
93encodeGoRequest ∷ String → Json
95encodeGoRequest ∷ String → Json
94encodeGoRequest url = J.encodeJson $ SM.fromFoldable [ Tuple "url" url ]
96encodeGoRequest url = J.encodeJson $ SM.fromFoldable [ Tuple "url" url ]
@@ -136,6 +138,12 @@ data Locator
136 | ByTagName String
138 | ByTagName String
137 | ByLinkText String
139 | ByLinkText String
138 | ByPartialLinkText String
140 | ByPartialLinkText String
140encodeLocator ∷ Locator → Json
148encodeLocator ∷ Locator → Json
141encodeLocator l = J.encodeJson $ SM.fromFoldable case l of
149encodeLocator l = J.encodeJson $ SM.fromFoldable case l of
@@ -159,6 +167,10 @@ encodeLocator l = J.encodeJson $ SM.fromFoldable case l of
159 [ Tuple "using" "tag name"
167 [ Tuple "using" "tag name"
160 , Tuple "value" sel
168 , Tuple "value" sel
171+ [ Tuple "using" r.using
172+ , Tuple "value" r.value
163encodeSendKeysRequest ∷ String → Json
175encodeSendKeysRequest ∷ String → Json
164encodeSendKeysRequest txt = J.encodeJson $ SM.fromFoldable [ Tuple "text" txt ]
176encodeSendKeysRequest txt = J.encodeJson $ SM.fromFoldable [ Tuple "text" txt ]
@@ -231,3 +243,221 @@ type Screenshot =
231decodeScreenshot ∷ Json → Either String Screenshot
243decodeScreenshot ∷ Json → Either String Screenshot
232decodeScreenshot j =
244decodeScreenshot j =
233 { content: _, encoding: NE.Base64 } <$> J.decodeJson j
245 { content: _, encoding: NE.Base64 } <$> J.decodeJson j
247+data Button = LeftBtn | MiddleBtn | RightBtn
249+encodeButton ∷ Button → Json
250+encodeButton = J.encodeJson <<< case _ of
255+data PointerMoveOrigin
258+ | FromElement Element
260+encodeOrigin ∷ PointerMoveOrigin → Json
261+encodeOrigin = case _ of
262+ FromViewport → J.encodeJson "viewport"
263+ FromPointer → J.encodeJson "pointer"
264+ FromElement el → encodeElement el
267+ { duration ∷ Milliseconds
268+ , origin ∷ PointerMoveOrigin
273+encodePointerMove ∷ PointerMove → Json
274+encodePointerMove r = J.encodeJson $ SM.fromFoldable
275+ [ Tuple "x" $ J.encodeJson r.x
276+ , Tuple "y" $ J.encodeJson r.y
277+ , Tuple "duration" $ J.encodeJson $ un Milliseconds r.duration
278+ , Tuple "origin" $ encodeOrigin r.origin
282+type Action = V.Variant
283+ ( pause ∷ Milliseconds
286+ , pointerUp ∷ Button
287+ , pointerDown ∷ Button
288+ , pointerMove ∷ PointerMove
291+encodeAction ∷ Action → Json
292+encodeAction = V.match
293+ { pause: \ms → J.encodeJson $ SM.singleton "duration" $ J.encodeJson $ un Milliseconds ms
294+ , keyDown: \ch → J.encodeJson $ SM.singleton "value" $ J.encodeJson ch
295+ , keyUp: \ch → J.encodeJson $ SM.singleton "value" $ J.encodeJson ch
296+ , pointerUp: \btn → J.encodeJson $ SM.singleton "button" $ encodeButton btn
297+ , pointerDown: \btn → J.encodeJson $ SM.singleton "button" $ encodeButton btn
298+ , pointerMove: encodePointerMove
302+data PointerType = Mouse | Pen | Touch
304+printPointerType ∷ PointerType → String
305+printPointerType = case _ of
312+ (Array (V.Variant (pause ∷ Milliseconds)))
314+ (Array (V.Variant (keyDown ∷ Char, keyUp ∷ Char, pause ∷ Milliseconds)))
315+ | Pointer PointerType
316+ (Array (V.Variant (pause ∷ Milliseconds, pointerUp ∷ Button, pointerDown ∷ Button, pointerMove ∷ PointerMove)))
318+-- Right, this is not an `Array` but `StrMap` because all `ActionSequence`s are tagged with unique id's
319+type ActionRequest = SM.StrMap ActionSequence
321+encodeActionRequest ∷ ActionRequest → Json
322+encodeActionRequest sm = J.encodeJson $ SM.singleton "actions" $ map encodePair arrayOfPairs
324+ arrayOfPairs ∷ Array (Tuple String ActionSequence)
325+ arrayOfPairs = SM.toUnfoldable sm
327+ encodePair ∷ Tuple String ActionSequence → Json
328+ encodePair (Tuple identifier sequence) =
329+ J.encodeJson $ SM.insert "id" (J.encodeJson identifier) $ encodeSequence sequence
331+ encodeSequence ∷ ActionSequence → J.JObject
332+ encodeSequence = case _ of
333+ NoSource as → SM.fromFoldable
334+ [ Tuple "type" $ J.encodeJson "none"
335+ , Tuple "actions" $ J.encodeJson $ map (encodeAction <<< V.expand) as
337+ Key as → SM.fromFoldable
338+ [ Tuple "type" $ J.encodeJson "key"
339+ , Tuple "actions" $ J.encodeJson $ map (encodeAction <<< V.expand) as
341+ Pointer ptype as → SM.fromFoldable
342+ [ Tuple "type" $ J.encodeJson "pointer"
343+ , Tuple "parameters" $ J.encodeJson $ SM.singleton "pointerType" $ printPointerType ptype
344+ , Tuple "actions" $ J.encodeJson $ map (encodeAction <<< V.expand) as
352+derive instance eqBrowserType ∷ Eq BrowserType
353+derive instance ordBrowserType ∷ Ord BrowserType
355+type DriverPaths = Map.Map BrowserType FilePath
357+renderDriverPaths ∷ DriverPaths → Array String
358+renderDriverPaths ps = map renderDriverPath pairs
360+ pairs ∷ Array (Tuple BrowserType String)
361+ pairs = Map.toUnfoldable ps
363+ renderDriverPath ∷ Tuple BrowserType String → String
364+ renderDriverPath (Tuple br path) = renderBrowserProp br <> "=\"" <> path <> "\""
366+ renderBrowserProp ∷ BrowserType → String
367+ renderBrowserProp = case _ of
368+ MSEdge → "-Dwebdriver.edge.driver"
369+ Chrome → "-Dwebdriver.chrome.driver"
370+ Firefox → "-DWebdriver.gecko.driver"
378+data UnhandledPrompt = Accept | Dismiss
380+data Platform = Windows | Mac | Linux | Any
383+ = BrowserName BrowserType
384+ | BrowserVersion String
385+ | PlatformName Platform
386+ | AcceptInsecureCerts Boolean
387+ | PageLoadStrategy PageLoad
388+ | DesiredTimeouts Timeouts
389+ | UnhandledPromptBehavior UnhandledPrompt
390+ | CustomCapability String Json
393+encodeCapability ∷ Capability → Tuple String Json
394+encodeCapability = case _ of
395+ BrowserName bn → Tuple "browserName" $ J.encodeJson case bn of
396+ MSEdge → "MicrosoftEdge"
399+ BrowserVersion bv → Tuple "browserVersion" $ J.encodeJson bv
400+ PlatformName pn → Tuple "platform" $ J.encodeJson case pn of
405+ AcceptInsecureCerts b → Tuple "acceptInsecureCerts" $ J.encodeJson b
406+ PageLoadStrategy s → Tuple "pageLoadStrategy" $ J.encodeJson case s of
410+ DesiredTimeouts t → Tuple "timeouts" $ encodeTimeouts t
411+ UnhandledPromptBehavior p → Tuple "unhandledPromptBehavior" $ J.encodeJson case p of
414+ CustomCapability k v → Tuple k v
416+encodeCapabilities ∷ ∀ f. F.Foldable f ⇒ f Capability → Json
417+encodeCapabilities = F.foldl (\b a → J.extend (encodeCapability a) b) J.jsonEmptyObject
419+decodeCapabilities ∷ Json → Either String (Array Capability)
420+decodeCapabilities = J.decodeJson >=> \obj →
421+ F.for (SM.toUnfoldable obj) \l@(Tuple key val) →
422+ decodeCapability l <|> Right (CustomCapability key val)
424+ decodeCapability ∷ Tuple String Json → Either String Capability
425+ decodeCapability (Tuple key val) = case key of
426+ "browserName" → BrowserName <$> decodeBrowserName val
427+ "browserVersion" → BrowserVersion <$> J.decodeJson val
428+ "acceptInsecureCerts" → AcceptInsecureCerts <$> J.decodeJson val
429+ "pageLoadStrategy" → PageLoadStrategy <$> decodePageLoadStrategy val
430+ "timeouts" → DesiredTimeouts <$> decodeTimeouts val
431+ "unhandledPromptBehaviour" → UnhandledPromptBehavior <$> decodePrompt val
432+ _ → Left "unhandled"
434+ decodeBrowserName ∷ Json → Either String BrowserType
435+ decodeBrowserName = J.decodeJson >=> \str → case Str.toLower str of
436+ "microsoftedge" → Right MSEdge
437+ "chrome" → Right Chrome
438+ "firefox" → Right Firefox
439+ _ → Left "unhandled"
441+ decodePageLoadStrategy ∷ Json → Either String PageLoad
442+ decodePageLoadStrategy = J.decodeJson >=> \str → case Str.toLower str of
443+ "none" → Right Immediate
444+ "normal" → Right Normal
445+ "eager" → Right Eager
446+ _ → Left "unhandled"
448+ decodePrompt ∷ Json → Either String UnhandledPrompt
449+ decodePrompt = J.decodeJson >=> \str → case Str.toLower str of
450+ "accept" → Right Accept
451+ "dismiss" → Right Dismiss
452+ _ → Left "unhandled"
454+type CapabilitiesRequest =
455+ { alwaysMatch ∷ Array Capability
456+ , firstMatch ∷ Array (Array Capability)
459+encodeCapabilitiesRequest ∷ CapabilitiesRequest → Json
460+encodeCapabilitiesRequest r = J.encodeJson $ SM.singleton "capabilities" $ SM.fromFoldable
461+ [ Tuple "alwaysMatch" $ encodeCapabilities r.alwaysMatch
462+ , Tuple "firstMatch" $ J.encodeJson $ map encodeCapabilities r.firstMatch