@@ -65,6 +65,7 @@ class SeleniumLibrary(DynamicCore):
6565 - `Run-on-failure functionality`
6666 - `Boolean arguments`
6767 - `Plugins`
68+ - `EventFiringWebDriver`
6869 - `Thread support`
6970 - `Importing`
7071 - `Shortcuts`
@@ -455,6 +456,35 @@ class inherits the
455456
456457 | python -m robot.libdoc SeleniumLibrary::plugins=/path/to/Plugin.py ./SeleniumLibraryWithPlugin.html
457458
459+ = EventFiringWebDriver =
460+
461+ The Selenium
462+ [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver]
463+ offers listener API for firing events before and after certain Selenium API calls.
464+ SeleniumLibrary offers support for Selenium ``EventFiringWebDriver`` listener class, by providing possibility
465+ to import the listener class with ``event_firing_webdriver`` argument. Refer to the Selenium
466+ ``EventFiringWebDriver`` documentation which Selenium API methods which can fire events and how the Selenium
467+ listener class should be implemented.
468+
469+ == Importing listener class ==
470+
471+ Importing Selenium listener class is similar when importing Robot Framework
472+ [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#importing-libraries|libraries]. It
473+ is possible import Selenium listener class with using
474+ [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#using-physical-path-to-library|physical path]
475+ or with
476+ [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#using-library-name|listener name],
477+ exactly in same way as importing libraries in Robot Framework. Selenium listener class is searched from the same
478+ [http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#module-search-path|module search path]
479+ as Robot Framework searches libraries. It is only possible to import listener class written in Python, other
480+ programming languages or Robot Framework test data is not supported. Like with Robot Framework library imports,
481+ Selenium listener class name is case sensitive and spaces are not supported in the class name. It is only
482+ possible to import one Selenium listener class and it is not possible to provide arguments for the Selenium
483+ listener class.
484+
485+ | Library | SeleniumLibrary | event_firing_webdriver=listner.SeleniumListener | # Improts listener with name. |
486+ | Library | SeleniumLibrary | event_firing_webdriver=${CURDIR}/MyListener.py | # Imports listner with physical path. |
487+
458488 = Thread support =
459489
460490 SeleniumLibrary is not thread safe. This is mainly due because the underlying
@@ -468,7 +498,8 @@ class inherits the
468498
469499 def __init__ (self , timeout = 5.0 , implicit_wait = 0.0 ,
470500 run_on_failure = 'Capture Page Screenshot' ,
471- screenshot_root_directory = None , plugins = None ):
501+ screenshot_root_directory = None , plugins = None ,
502+ event_firing_webdriver = None ):
472503 """SeleniumLibrary can be imported with several optional arguments.
473504
474505 - ``timeout``:
@@ -482,6 +513,9 @@ def __init__(self, timeout=5.0, implicit_wait=0.0,
482513 the directory where the log file is written is used.
483514 - ``plugins``:
484515 Allows extending the SeleniumLibrary with external Python classes.
516+ - ``event_firing_webdriver``:
517+ Class for wrapping Selenium with
518+ [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.event_firing_webdriver.html#module-selenium.webdriver.support.event_firing_webdriver|EventFiringWebDriver]
485519 """
486520 self .timeout = timestr_to_secs (timeout )
487521 self .implicit_wait = timestr_to_secs (implicit_wait )
@@ -508,21 +542,15 @@ def __init__(self, timeout=5.0, implicit_wait=0.0,
508542 WindowKeywords (self )
509543 ]
510544 if is_truthy (plugins ):
511- parsed_plugins = self ._string_to_modules (plugins )
512- for index , plugin in enumerate (self ._import_modules (parsed_plugins )):
513- if not isclass (plugin ):
514- message = "Importing test library: '%s' failed." % parsed_plugins [index ].plugin
515- raise DataError (message )
516- plugin = plugin (self , * parsed_plugins [index ].args ,
517- ** parsed_plugins [index ].kw_args )
518- if not isinstance (plugin , LibraryComponent ):
519- message = 'Plugin does not inherit SeleniumLibrary.base.LibraryComponent'
520- raise PluginError (message )
521- self ._store_plugin_keywords (plugin )
522- libraries .append (plugin )
545+ plugin_libs = self ._parse_plugins (plugins )
546+ libraries = libraries + plugin_libs
523547 self ._drivers = WebDriverCache ()
524548 DynamicCore .__init__ (self , libraries )
525549 self .ROBOT_LIBRARY_LISTENER = LibraryListener ()
550+ if is_truthy (event_firing_webdriver ):
551+ self .event_firing_webdriver = self ._parse_listener (event_firing_webdriver )
552+ else :
553+ self .event_firing_webdriver = None
526554
527555 _speed_in_secs = Deprecated ('_speed_in_secs' , 'speed' )
528556 _timeout_in_secs = Deprecated ('_timeout_in_secs' , 'timeout' )
@@ -633,28 +661,55 @@ def _run_on_failure(self):
633661 DeprecationWarning )
634662 self .failure_occurred ()
635663
636- def _string_to_modules (self , plugins ):
637- Plugin = namedtuple ('Plugin' , 'plugin, args, kw_args' )
638- parsed_plugins = []
639- for plugin in plugins .split (',' ):
640- plugin = plugin .strip ()
641- plugin_and_args = plugin .split (';' )
642- plugin_name = plugin_and_args .pop (0 )
664+ def _parse_plugins (self , plugins ):
665+ libraries = []
666+ importer = Importer ('test library' )
667+ for parsed_plugin in self ._string_to_modules (plugins ):
668+ plugin = importer .import_class_or_module (parsed_plugin .module )
669+ if not isclass (plugin ):
670+ message = "Importing test library: '%s' failed." % parsed_plugin .module
671+ raise DataError (message )
672+ plugin = plugin (self , * parsed_plugin .args ,
673+ ** parsed_plugin .kw_args )
674+ if not isinstance (plugin , LibraryComponent ):
675+ message = 'Plugin does not inherit SeleniumLibrary.base.LibraryComponent'
676+ raise PluginError (message )
677+ self ._store_plugin_keywords (plugin )
678+ libraries .append (plugin )
679+ return libraries
680+
681+ def _parse_listener (self , event_firing_webdriver ):
682+ listener_module = self ._string_to_modules (event_firing_webdriver )
683+ listener_count = len (listener_module )
684+ if listener_count > 1 :
685+ message = 'Is is possible import only one listener but there was %s listeners.' % listener_count
686+ raise ValueError (message )
687+ listener_module = listener_module [0 ]
688+ importer = Importer ('test library' )
689+ listener = importer .import_class_or_module (listener_module .module )
690+ if not isclass (listener ):
691+ message = "Importing test Selenium lister class '%s' failed." % listener_module .module
692+ raise DataError (message )
693+ return listener
694+
695+ def _string_to_modules (self , modules ):
696+ Module = namedtuple ('Module' , 'module, args, kw_args' )
697+ parsed_modules = []
698+ for module in modules .split (',' ):
699+ module = module .strip ()
700+ module_and_args = module .split (';' )
701+ module_name = module_and_args .pop (0 )
643702 kw_args = {}
644703 args = []
645- for argument in plugin_and_args :
704+ for argument in module_and_args :
646705 if '=' in argument :
647706 key , value = argument .split ('=' )
648707 kw_args [key ] = value
649708 else :
650709 args .append (argument )
651- plugin = Plugin (plugin = plugin_name , args = args , kw_args = kw_args )
652- parsed_plugins .append (plugin )
653- return parsed_plugins
654-
655- def _import_modules (self , plugins ):
656- importer = Importer ('test library' )
657- return [importer .import_class_or_module (plugin .plugin ) for plugin in plugins ]
710+ module = Module (module = module_name , args = args , kw_args = kw_args )
711+ parsed_modules .append (module )
712+ return parsed_modules
658713
659714 def _store_plugin_keywords (self , plugin ):
660715 dynamic_core = DynamicCore ([plugin ])
0 commit comments