packages feed

webdriver-precore-0.2.0.0: test/BiDi/Demos/LogEventDemos.hs

module BiDi.Demos.LogEventDemos where

import BiDi.Actions (BiDiActions (..))
import BiDi.DemoUtils ( demo, rootContext, BiDiDemo )
import Data.Maybe (fromJust)
import IOUtils (DemoActions (..))
import TestData (badJavaScriptUrl, consoleLogUrl)
import WebDriverPreCore.BiDi.Protocol
  ( ContextTarget (..),
    Evaluate (..),
    Event(..),
    LocateNodes (..),
    LocateNodesResult (..),
    Locator (..),
    LogEntry (..), 
    LogEvent(..),
    Navigate (..),
    NodeRemoteValue (..),
    PointerCommonProperties (..),
    PointerSourceActions (..),
    PointerSourceAction (..),
    KnownSubscriptionType (..),
    Pointer (..),
    PointerType (..),
    Origin (..),
    PerformActions (..),
    SourceActions (..),
    SharedId,
    SharedReference (..),
    Target (..)
  )
import Prelude hiding (log, putStrLn)
import Data.Text (Text)
import Control.Monad ((>=>))
import Data.Coerce (coerce)

-- >>> runDemo logEventConsoleEntries
logEventConsoleEntries :: BiDiDemo
logEventConsoleEntries =
  demo "Log Events - Console Log Entries" action
  where
    action :: DemoActions -> BiDiActions -> IO ()
    action utils@MkDemoActions {..} bidi@MkBiDiActions {..} = do
      logTxt "Subscribe to LogEntryAdded event"

      (logEventFired, waitLogEventFired) <- timeLimitLog LogEntryAdded
      subscribeLogEntryAdded $ chkIsConsoleEntry logTxt >=> logEventFired

      (manyLogEventFired, waitManyLogEventFired) <- timeLimitLogMany LogEntryAdded
      subscribeMany [LogEntryAdded] $ chkEventIsConsoleEntry logTxt >=> manyLogEventFired

      logTxt "Navigate to console log test page"
      url <- consoleLogUrl
      bc <- rootContext utils bidi
      browsingContextNavigate $ MkNavigate bc url Nothing

      -- Page automatically logs on load, so events should fire
      logTxt "Waiting for console log events from page load..."

      sequence_
        [ waitLogEventFired,
          waitManyLogEventFired
        ]

-- >>> runDemo logEventConsoleLevelDebug
logEventConsoleLevelDebug :: BiDiDemo
logEventConsoleLevelDebug =
  demo "Log Events - Console Debug Level" action
  where
    action :: DemoActions -> BiDiActions -> IO ()
    action utils@MkDemoActions {..} bidi@MkBiDiActions {..} = do
      bc <- rootContext utils bidi

      logTxt "Subscribe to LogEntryAdded event"
      (logEventFired, waitLogEventFired) <- timeLimitLog LogEntryAdded
      subscribeLogEntryAdded $ chkIsConsoleEntry logTxt >=> logEventFired

      (manyLogEventFired, waitManyLogEventFired) <- timeLimitLogMany LogEntryAdded
      subscribeMany [LogEntryAdded] $ chkEventIsConsoleEntry logTxt >=> manyLogEventFired

      logTxt "Triggering console.debug()"
      scriptEvaluate $
        MkEvaluate
          { expression = "console.debug('Debug level message')",
            target = ContextTarget $ MkContextTarget {context = bc, sandbox = Nothing},
            awaitPromise = False,
            resultOwnership = Nothing,
            serializationOptions = Nothing
          }
      sequence_
        [ waitLogEventFired,
          waitManyLogEventFired
        ]

-- >>> runDemo logEventConsoleLevelInfo
logEventConsoleLevelInfo :: BiDiDemo
logEventConsoleLevelInfo =
  demo "Log Events - Console Info Level" action
  where
    action :: DemoActions -> BiDiActions -> IO ()
    action utils@MkDemoActions {..} bidi@MkBiDiActions {..} = do
      bc <- rootContext utils bidi

      logTxt "Subscribe to LogEntryAdded event"
      (logEventFired, waitLogEventFired) <- timeLimitLog LogEntryAdded
      subscribeLogEntryAdded $ chkIsConsoleEntry logTxt >=> logEventFired

      (manyLogEventFired, waitManyLogEventFired) <- timeLimitLogMany LogEntryAdded
      subscribeMany [LogEntryAdded] $ chkEventIsConsoleEntry logTxt >=> manyLogEventFired

      logTxt "Triggering console.info()"
      scriptEvaluate $
        MkEvaluate
          { expression = "console.info('Info level message')",
            target = ContextTarget $ MkContextTarget {context = bc, sandbox = Nothing},
            awaitPromise = False,
            resultOwnership = Nothing,
            serializationOptions = Nothing
          }
      sequence_
        [ waitLogEventFired,
          waitManyLogEventFired
        ]

-- >>> runDemo logEventConsoleLevelWarn
logEventConsoleLevelWarn :: BiDiDemo
logEventConsoleLevelWarn =
  demo "Log Events - Console Warn Level" action
  where
    action :: DemoActions -> BiDiActions -> IO ()
    action utils@MkDemoActions {..} bidi@MkBiDiActions {..} = do
      bc <- rootContext utils bidi

      logTxt "Subscribe to LogEntryAdded event"
      (logEventFired, waitLogEventFired) <- timeLimitLog LogEntryAdded
      subscribeLogEntryAdded $ chkIsConsoleEntry logTxt >=> logEventFired

      (manyLogEventFired, waitManyLogEventFired) <- timeLimitLogMany LogEntryAdded
      subscribeMany [LogEntryAdded] $ chkEventIsConsoleEntry logTxt >=> manyLogEventFired

      logTxt "Triggering console.warn()"
      scriptEvaluate $
        MkEvaluate
          { expression = "console.warn('Warning level message')",
            target = ContextTarget $ MkContextTarget {context = bc, sandbox = Nothing},
            awaitPromise = False,
            resultOwnership = Nothing,
            serializationOptions = Nothing
          }
      sequence_
        [ waitLogEventFired,
          waitManyLogEventFired
        ]

-- >>> runDemo logEventConsoleLevelError
logEventConsoleLevelError :: BiDiDemo
logEventConsoleLevelError =
  demo "Log Events - Console Error Level" action
  where
    action :: DemoActions -> BiDiActions -> IO ()
    action utils@MkDemoActions {..} bidi@MkBiDiActions {..} = do
      bc <- rootContext utils bidi

      logTxt "Subscribe to LogEntryAdded event"
      (logEventFired, waitLogEventFired) <- timeLimitLog LogEntryAdded
      subscribeLogEntryAdded $ chkIsConsoleEntry logTxt >=> logEventFired

      (manyLogEventFired, waitManyLogEventFired) <- timeLimitLogMany LogEntryAdded
      subscribeMany [LogEntryAdded] $ chkEventIsConsoleEntry logTxt >=> manyLogEventFired

      logTxt "Triggering console.error()"
      scriptEvaluate $
        MkEvaluate
          { expression = "console.error('Error level message')",
            target = ContextTarget $ MkContextTarget {context = bc, sandbox = Nothing},
            awaitPromise = False,
            resultOwnership = Nothing,
            serializationOptions = Nothing
          }
      sequence_
        [ waitLogEventFired,
          waitManyLogEventFired
        ]

-- >>> runDemo logEventJavascriptErrorFromButton
logEventJavascriptErrorFromButton :: BiDiDemo
logEventJavascriptErrorFromButton =
  demo "Log Events - JavaScript Error from Button Click" action
  where
    action :: DemoActions -> BiDiActions -> IO ()
    action utils@MkDemoActions {..} bidi@MkBiDiActions {..} = do
      logTxt "Navigate to Bad JavaScript test page"
      url <- badJavaScriptUrl
      bc <- rootContext utils bidi
      browsingContextNavigate $ MkNavigate bc url Nothing
      pause

      logTxt "Subscribe to LogEntryAdded event"
      (logEventFired, waitLogEventFired) <- timeLimitLog LogEntryAdded
      subscribeLogEntryAdded $ chkIsJavaScriptEntry logTxt >=> logEventFired

      (manyLogEventFired, waitManyLogEventFired) <- timeLimitLogMany LogEntryAdded
      subscribeMany [LogEntryAdded] $ chkEventIsJavaScriptEntry logTxt >=> manyLogEventFired

      logTxt "Locate the 'Bad JS' button"
      buttonResult <-
        browsingContextLocateNodes $
          MkLocateNodes
            { context = bc,
              locator = CSS {value = "#badJsButton"},
              maxNodeCount = Nothing,
              serializationOptions = Nothing,
              startNodes = Nothing
            }
      logShow "Button search result" buttonResult

      let MkLocateNodesResult nodes = buttonResult
          buttonId :: SharedId
          buttonId = case nodes of
            [button] -> fromJust button.sharedId
            _ -> error "Failed to locate Bad JS button"

      logTxt "Click the 'Bad JS' button to trigger JavaScript error"
      inputPerformActions $
        MkPerformActions
          { context = bc,
            actions =
              [ PointerSourceActions $
                  MkPointerSourceActions
                    { pointerId = "mouse1",
                      pointer = Just $ MkPointer {pointerType = Just MousePointer},
                      pointerActions =
                        [ PointerMove
                            { x = 0,
                              y = 0,
                              duration = Just 300,
                              origin =
                                Just $
                                  ElementOrigin $
                                    MkSharedReference
                                      { sharedId = buttonId,
                                        handle = Nothing,
                                        extensions = Nothing
                                      },
                              pointerCommonProperties = defaultPointerProps
                            },
                          PointerDown
                            { button = 0,
                              pointerCommonProperties = defaultPointerProps
                            },
                          PointerUp
                            { button = 0
                            }
                        ]
                    }
              ]
          }

      logTxt "Waiting for JavaScript error log events..."
      sequence_
        [ waitLogEventFired,
          waitManyLogEventFired
        ]

{-
Doesn't work - can't work out how to generate a generic log entry
-- >>> runDemo logEventGenericEntry
logEventGenericEntry :: BiDiDemo
logEventGenericEntry =
  demo "Log Events - Generic Log Entry" action
  where
    -- Generic log entries are less common and typically generated by browser internals
    -- or specific browser features. This demo shows how to subscribe to all log events
    -- and observe generic entries if they occur.
    action :: DemoUtils -> BiDiActions -> IO ()
    action utils@MkDemoActions {..} bidi@MkBiDiActions {..} = do
      logTxt "Subscribe to LogEntryAdded event (all types)"
      (logEventFired, waitLogEventFired) <- timeLimitLog LogEntryAdded
      subscribeLogEntryAdded logEventFired

      (manyLogEventFired, waitManyLogEventFired) <- timeLimitLogMany LogEntryAdded
      subscribeMany [LogEntryAdded] manyLogEventFired

      logTxt "Navigate to checkboxes page"
      url <- checkboxesUrl
      bc <- rootContext utils bidi
      browsingContextNavigate $ MkNavigate bc url Nothing
      pause

      logTxt "Generate various log entries"
      scriptEvaluate $
        MkEvaluate
          { expression = "console.log('This generates a console log entry')",
            target = ContextTarget $ MkContextTarget {context = bc, sandbox = Nothing},
            awaitPromise = False,
            resultOwnership = Nothing,
            serializationOptions = Nothing
          }
      sequence_
        [ waitLogEventFired,
          waitManyLogEventFired
        ]

      logTxt "Note: Generic log entries are browser-generated and may not occur in this demo"
      logTxt "The subscription will capture console and javascript entries as demonstrated"
-}


-- Helpers

defaultPointerProps :: PointerCommonProperties
defaultPointerProps =
  MkPointerCommonProperties
    { width = Nothing,
      height = Nothing,
      pressure = Nothing,
      tangentialPressure = Nothing,
      twist = Nothing,
      altitudeAngle = Nothing,
      azimuthAngle = Nothing
    }


chkIsConsoleEntry :: (Text -> IO ()) -> LogEntry -> IO LogEntry
chkIsConsoleEntry log =
  \case
    entry@ConsoleEntry {} -> log "Received Console Log Entry" >> pure entry
    entry -> fail $ "Expected Console Log Entry But Got: " <> show entry

chkIsJavaScriptEntry :: (Text -> IO ()) -> LogEntry -> IO LogEntry
chkIsJavaScriptEntry log =
  \case
    entry@JavascriptEntry {} -> log "Received JavaScript Log Entry" >> pure entry
    entry -> fail $ "Expected JavaScript Log Entry But Got: " <> show entry


chkEventIsConsoleEntry :: (Text -> IO ()) -> Event -> IO LogEntry
chkEventIsConsoleEntry log =
  \case
    LogEvent event -> log "Received Log Event" >> chkIsConsoleEntry log (coerce event)
    event -> fail $ "Expected Console Event But Got: " <> show event


chkEventIsJavaScriptEntry :: (Text -> IO ()) -> Event -> IO LogEntry
chkEventIsJavaScriptEntry log =
  \case
    LogEvent event -> log "Received Log Event" >> chkIsJavaScriptEntry log (coerce event)
    event -> fail $ "Expected JavaScript Event But Got: " <> show event