@@ -2,22 +2,25 @@ module Lunapark.Types where
22
33import Prelude
44
5−import Control.Alt ((<|>))
6−import CSS.Selector as CSS
75import CSS.Render as CSSR
6+import CSS.Selector as CSS
7+import Control.Alt ((<|>))
88import Data.Argonaut (Json, (.?))
99import Data.Argonaut as J
1010import Data.Array as A
11−import Data.Foldable as F
12−import Data.Time.Duration (Milliseconds(..))
1311import Data.Either (Either(..))
12+import Data.Traversable as F
13+import Data.Map as Map
1414import Data.Maybe (Maybe(..))
1515import Data.Newtype (class Newtype, un)
1616import Data.StrMap as SM
17+import Data.String as Str
18+import Data.Time.Duration (Milliseconds(..))
1719import Data.Tuple (Tuple(..))
20+import Data.Variant as V
1821import Data.XPath.Types as X
1922import Node.Encoding as NE
20−import Unsafe.Coerce (unsafeCoerce)
23+import Node.Path (FilePath)
2124
2225newtype SessionId = SessionId String
2326derive instance newtypeSessionId ∷ Newtype SessionId _
@@ -40,20 +43,12 @@ encodeElement (Element eid) = J.encodeJson $ SM.fromFoldable
4043 , Tuple "ELEMENT" eid
4144 ]
4245
43−data Capabilities
44−
45−encodeCapabilities ∷ Capabilities → Json
46−encodeCapabilities = unsafeCoerce
47−
48−decodeCapabilities ∷ Json → Either String Capabilities
49−decodeCapabilities js = Left "unimplemented"
50−
5146decodeSessionId ∷ Json → Either String SessionId
5247decodeSessionId = map SessionId <<< J.decodeJson
5348
5449type CreateSessionResponse =
5550 { session ∷ SessionId
56− , capabilities ∷ Capabilities
51+ , capabilities ∷ Array Capability
5752 }
5853
5954decodeCreateSessionResponse ∷ Json → Either String CreateSessionResponse
@@ -90,6 +85,13 @@ encodeTimeouts r = J.encodeJson $ SM.fromFoldable
9085 , Tuple "implicit" (un Milliseconds r.implicit)
9186 ]
9287
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
93+ ]
94+
9395encodeGoRequest ∷ String → Json
9496encodeGoRequest url = J.encodeJson $ SM.fromFoldable [ Tuple "url" url ]
9597
@@ -136,6 +138,12 @@ data Locator
136138 | ByTagName String
137139 | ByLinkText String
138140 | ByPartialLinkText String
141+ | Raw RawLocator
142+
143+type RawLocator =
144+ { using ∷ String
145+ , value ∷ String
146+ }
139147
140148encodeLocator ∷ Locator → Json
141149encodeLocator l = J.encodeJson $ SM.fromFoldable case l of
@@ -159,6 +167,10 @@ encodeLocator l = J.encodeJson $ SM.fromFoldable case l of
159167 [ Tuple "using" "tag name"
160168 , Tuple "value" sel
161169 ]
170+ Raw r →
171+ [ Tuple "using" r.using
172+ , Tuple "value" r.value
173+ ]
162174
163175encodeSendKeysRequest ∷ String → Json
164176encodeSendKeysRequest txt = J.encodeJson $ SM.fromFoldable [ Tuple "text" txt ]
@@ -231,3 +243,221 @@ type Screenshot =
231243decodeScreenshot ∷ Json → Either String Screenshot
232244decodeScreenshot j =
233245 { content: _, encoding: NE.Base64 } <$> J.decodeJson j
246+
247+data Button = LeftBtn | MiddleBtn | RightBtn
248+
249+encodeButton ∷ Button → Json
250+encodeButton = J.encodeJson <<< case _ of
251+ LeftBtn → 0
252+ MiddleBtn → 1
253+ RightBtn → 2
254+
255+data PointerMoveOrigin
256+ = FromViewport
257+ | FromPointer
258+ | FromElement Element
259+
260+encodeOrigin ∷ PointerMoveOrigin → Json
261+encodeOrigin = case _ of
262+ FromViewport → J.encodeJson "viewport"
263+ FromPointer → J.encodeJson "pointer"
264+ FromElement el → encodeElement el
265+
266+type PointerMove =
267+ { duration ∷ Milliseconds
268+ , origin ∷ PointerMoveOrigin
269+ , x ∷ Int
270+ , y ∷ Int
271+ }
272+
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
279+ ]
280+
281+
282+type Action = V.Variant
283+ ( pause ∷ Milliseconds
284+ , keyDown ∷ Char
285+ , keyUp ∷ Char
286+ , pointerUp ∷ Button
287+ , pointerDown ∷ Button
288+ , pointerMove ∷ PointerMove
289+ )
290+
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
299+ }
300+
301+
302+data PointerType = Mouse | Pen | Touch
303+
304+printPointerType ∷ PointerType → String
305+printPointerType = case _ of
306+ Mouse → "mouse"
307+ Pen → "pen"
308+ Touch → "touch"
309+
310+data ActionSequence
311+ = NoSource
312+ (Array (V.Variant (pause ∷ Milliseconds)))
313+ | Key
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)))
317+
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
320+
321+encodeActionRequest ∷ ActionRequest → Json
322+encodeActionRequest sm = J.encodeJson $ SM.singleton "actions" $ map encodePair arrayOfPairs
323+ where
324+ arrayOfPairs ∷ Array (Tuple String ActionSequence)
325+ arrayOfPairs = SM.toUnfoldable sm
326+
327+ encodePair ∷ Tuple String ActionSequence → Json
328+ encodePair (Tuple identifier sequence) =
329+ J.encodeJson $ SM.insert "id" (J.encodeJson identifier) $ encodeSequence sequence
330+
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
336+ ]
337+ Key as → SM.fromFoldable
338+ [ Tuple "type" $ J.encodeJson "key"
339+ , Tuple "actions" $ J.encodeJson $ map (encodeAction <<< V.expand) as
340+ ]
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
345+ ]
346+
347+data BrowserType
348+ = MSEdge
349+ | Chrome
350+ | Firefox
351+
352+derive instance eqBrowserType ∷ Eq BrowserType
353+derive instance ordBrowserType ∷ Ord BrowserType
354+
355+type DriverPaths = Map.Map BrowserType FilePath
356+
357+renderDriverPaths ∷ DriverPaths → Array String
358+renderDriverPaths ps = map renderDriverPath pairs
359+ where
360+ pairs ∷ Array (Tuple BrowserType String)
361+ pairs = Map.toUnfoldable ps
362+
363+ renderDriverPath ∷ Tuple BrowserType String → String
364+ renderDriverPath (Tuple br path) = renderBrowserProp br <> "=\"" <> path <> "\""
365+
366+ renderBrowserProp ∷ BrowserType → String
367+ renderBrowserProp = case _ of
368+ MSEdge → "-Dwebdriver.edge.driver"
369+ Chrome → "-Dwebdriver.chrome.driver"
370+ Firefox → "-DWebdriver.gecko.driver"
371+
372+
373+data PageLoad
374+ = Normal
375+ | Eager
376+ | Immediate
377+
378+data UnhandledPrompt = Accept | Dismiss
379+
380+data Platform = Windows | Mac | Linux | Any
381+
382+data Capability
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
391+
392+
393+encodeCapability ∷ Capability → Tuple String Json
394+encodeCapability = case _ of
395+ BrowserName bn → Tuple "browserName" $ J.encodeJson case bn of
396+ MSEdge → "MicrosoftEdge"
397+ Chrome → "chrome"
398+ Firefox → "firefox"
399+ BrowserVersion bv → Tuple "browserVersion" $ J.encodeJson bv
400+ PlatformName pn → Tuple "platform" $ J.encodeJson case pn of
401+ Windows → "WINDOWS"
402+ Mac → "MAC"
403+ Linux → "LINUX"
404+ Any → "ANY"
405+ AcceptInsecureCerts b → Tuple "acceptInsecureCerts" $ J.encodeJson b
406+ PageLoadStrategy s → Tuple "pageLoadStrategy" $ J.encodeJson case s of
407+ Normal → "normal"
408+ Eager → "eager"
409+ Immediate → "none"
410+ DesiredTimeouts t → Tuple "timeouts" $ encodeTimeouts t
411+ UnhandledPromptBehavior p → Tuple "unhandledPromptBehavior" $ J.encodeJson case p of
412+ Accept → "accept"
413+ Dismiss → "dismiss"
414+ CustomCapability k v → Tuple k v
415+
416+encodeCapabilities ∷ ∀ f. F.Foldable f ⇒ f Capability → Json
417+encodeCapabilities = F.foldl (\b a → J.extend (encodeCapability a) b) J.jsonEmptyObject
418+
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)
423+ where
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"
433+
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"
440+
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"
447+
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"
453+
454+type CapabilitiesRequest =
455+ { alwaysMatch ∷ Array Capability
456+ , firstMatch ∷ Array (Array Capability)
457+ }
458+
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
463+ ]