66from .listener import TestabilityListener
77from .javascript import JS_LOOKUP
88from .logger import get_logger , argstr , kwargstr
9- from .types import WebElementType , LocatorType , OptionalBoolType , OptionalStrType , BrowserLogsType , OptionalDictType
9+ from .types import WebElementType , LocatorType , OptionalBoolType , OptionalStrType , BrowserLogsType , OptionalDictType , is_firefox
1010from robot .utils import is_truthy , timestr_to_secs , secs_to_timestr
1111from selenium .webdriver .support .ui import WebDriverWait
1212from selenium .common .exceptions import TimeoutException , WebDriverException
1313from http .cookies import SimpleCookie
1414from furl import furl
1515from typing import Dict , Callable , Any
1616from selenium .webdriver .common .desired_capabilities import DesiredCapabilities
17+ from selenium .webdriver import FirefoxProfile
1718import wrapt
19+ import re
20+ import json
21+ from time import time
1822
1923
2024@wrapt .decorator
@@ -171,6 +175,7 @@ def __init__(
171175 self .hidden_elements = {} # type: Dict[str, str]
172176 self .browser_warn_shown = False
173177 self .empty_log_warn_shown = False
178+ self .ff_log_pos = {} # type: Dict[str, int]
174179
175180 @log_wrapper
176181 def _inject_testability (self : "SeleniumTestability" ) -> None :
@@ -502,19 +507,58 @@ def element_should_not_be_blocked(self: "SeleniumTestability", locator: LocatorT
502507 if is_blocked :
503508 raise AssertionError ("Element with locator {} is blocked" .format (locator ))
504509
510+ def _get_ff_log (self : "SeleniumTestability" , name : str ) -> BrowserLogsType :
511+ matcher = r"^(?P<source>JavaScript|console)(\s|\.)(?P<level>warn.*?|debug|trace|log|error|info):\s(?P<message>.*)$"
512+ LEVEL_LOOKUP = {
513+ "log" : "INFO" ,
514+ "warn" : "WARN" ,
515+ "warning" : "WARN" ,
516+ "error" : "SEVERE" ,
517+ "info" : "INFO" ,
518+ "trace" : "SEVERE" ,
519+ "debug" : "DEBUG" ,
520+ }
521+ SOURCE_LOOKUP = {"JavaScript" : "javascript" , "console" : "console-api" }
522+ log = []
523+ skip_lines = self .ff_log_pos .get (name , 0 )
524+ buff = []
525+ with open (name , "r" ) as f :
526+ buff = f .read ().split ("\n " )
527+ self .ff_log_pos [name ] = skip_lines + len (buff )
528+ buff = buff [skip_lines :]
529+
530+ for line in buff :
531+ matches = re .search (matcher , line )
532+ if matches :
533+ row = {
534+ "level" : LEVEL_LOOKUP [matches .group ("level" )],
535+ "message" : matches .group ("message" ),
536+ "source" : SOURCE_LOOKUP [matches .group ("source" )],
537+ "timestamp" : int (time () * 1000 ),
538+ }
539+ log .append (json .dumps (row ))
540+ return log
541+
505542 @log_wrapper
506543 @keyword
507544 def get_log (self : "SeleniumTestability" , log_type : str = "browser" ) -> BrowserLogsType :
508545 """
509- Returns logs determined by ``log_type`` from the current browser.
546+ Returns logs determined by ``log_type`` from the current browser. What is returned
547+ depends on desired_capabilities passed to `Open Browser`.
548+
549+ Note: On firefox, the firefox profile has to have `devtools.console.stdout.content` property to be set.
550+ This can be done automatically with `Generate Firefox Profile` and then pass that to `Open Browser`.
510551 """
511552 ret = [] # type: BrowserLogsType
512553 try :
513- ret = self .ctx .driver .get_log (log_type )
554+ if is_firefox (self .ctx .driver ) and log_type == "browser" :
555+ ret = self ._get_ff_log (self .ctx .driver .service .log_file .name )
556+ else :
557+ ret = self .ctx .driver .get_log (log_type )
514558 except WebDriverException :
515559 if not self .browser_warn_shown :
516560 self .browser_warn_shown = True
517- self .warn ("Current browser does not support fetching logs from the browser" )
561+ self .warn ("Current browser does not support fetching logs from the browser with log_type: {}" . format ( log_type ) )
518562 return []
519563 if not ret and not self .empty_log_warn_shown :
520564 self .empty_log_warn_shown = True
@@ -534,7 +578,6 @@ def get_default_capabilities(self: "SeleniumTestability", browser_name: str) ->
534578 self .logger .debug (e )
535579 return None
536580
537-
538581 @log_wrapper
539582 @keyword
540583 def set_element_attribute (self : "SeleniumTestability" , locator : LocatorType , attribute : str , value : str ) -> None :
@@ -543,3 +586,27 @@ def set_element_attribute(self: "SeleniumTestability", locator: LocatorType, att
543586 """
544587 from_element = self .el .find_element (locator )
545588 self .ctx .driver .execute_script (JS_LOOKUP ["set_element_attribute" ], from_element , attribute , value )
589+
590+ @log_wrapper
591+ @keyword
592+ def generate_firefox_profile (
593+ self : "SeleniumTestability" ,
594+ options : OptionalDictType = None ,
595+ accept_untrusted_certs : bool = False ,
596+ proxy : OptionalStrType = None ,
597+ ) -> FirefoxProfile :
598+ profile = FirefoxProfile ()
599+ if options :
600+ for key , value in options .items (): # type: ignore
601+ profile .set_preference (key , value )
602+
603+ profile .set_preference ("devtools.console.stdout.content" , True )
604+
605+ if accept_untrusted_certs :
606+ profile .accept_untrusted_certs
607+
608+ if proxy :
609+ profile .set_proxy (proxy )
610+
611+ profile .update_preferences ()
612+ return profile
0 commit comments