From 6a90fd7ee15e4ae01762990d9dff45fe9ab71451 Mon Sep 17 00:00:00 2001 From: zephraph Date: Mon, 13 Jul 2015 16:43:42 -0500 Subject: [PATCH 1/6] Added screenshot_root_directory --- src/Selenium2Library/__init__.py | 9 ++++++++- src/Selenium2Library/keywords/_logging.py | 12 +++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index fad8a88c5..63dbada02 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -135,7 +135,13 @@ class Selenium2Library( ROBOT_LIBRARY_SCOPE = 'GLOBAL' ROBOT_LIBRARY_VERSION = VERSION - def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page Screenshot'): + def __init__(self, + timeout=5.0, + implicit_wait=0.0, + run_on_failure='Capture Page Screenshot', + screenshot_root_directory=None + ): + """Selenium2Library can be imported with optional arguments. `timeout` is the default timeout used to wait for all waiting actions. @@ -165,6 +171,7 @@ def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page """ for base in Selenium2Library.__bases__: base.__init__(self) + self.screenshot_root_directory = screenshot_root_directory self.set_selenium_timeout(timeout) self.set_selenium_implicit_wait(implicit_wait) self.register_keyword_to_run_on_failure(run_on_failure) diff --git a/src/Selenium2Library/keywords/_logging.py b/src/Selenium2Library/keywords/_logging.py index 142ba1fde..ad1f2676c 100644 --- a/src/Selenium2Library/keywords/_logging.py +++ b/src/Selenium2Library/keywords/_logging.py @@ -12,7 +12,17 @@ def _debug(self, message): logger.debug(message) def _get_log_dir(self): - variables = BuiltIn().get_variables() + # Use screenshot root directory if set + if self.screenshot_root_directory is not None: + return self.screenshot_root_directory + + # If robotframework isn't running use current directory + try: + variables = BuiltIn().get_variables() + except RobotNotRunningError: + return os.getcwd() + + # Otherwise use the log directory provided by RF logfile = variables['${LOG FILE}'] if logfile != 'NONE': return os.path.dirname(logfile) From 1f606f3b88d754dd0f7c10d86273e6330712e239 Mon Sep 17 00:00:00 2001 From: zephraph Date: Mon, 13 Jul 2015 17:18:24 -0500 Subject: [PATCH 2/6] Added documentation --- src/Selenium2Library/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index 63dbada02..da5696fc1 100644 --- a/src/Selenium2Library/__init__.py +++ b/src/Selenium2Library/__init__.py @@ -162,6 +162,9 @@ def __init__(self, `Register Keyword To Run On Failure` keyword for more information about this functionality. + `screenshot_root_directory` specifies the default root directory that screenshots should be + stored in. If not provided the default directory will be where robotframework places its logfile. + Examples: | Library `|` Selenium2Library `|` 15 | # Sets default timeout to 15 seconds | | Library `|` Selenium2Library `|` 0 `|` 5 | # Sets default timeout to 0 seconds and default implicit_wait to 5 seconds | From 07effe976e9e49fc7e14c11e8b07738a6aea2846 Mon Sep 17 00:00:00 2001 From: Zephraph Date: Tue, 14 Jul 2015 14:57:33 -0500 Subject: [PATCH 3/6] Added reference to screenshot_root_directory --- CHANGES.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 36dab2508..2c8c1ff7c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,8 +1,12 @@ Release Notes ============= -1.7.2 +1.7.2 (Unreleased) ---------------- +- Added an argument called screenshot_root_directory that can be passed into S2L's + constructor to specify where to store screenshots. + [zephraph] + - Fixed an error where regular functions were not able to be used as a custom locator [zephraph] From e43e782a5a0173a4dce2d60ce5f46e6ce3b78f78 Mon Sep 17 00:00:00 2001 From: Zephraph Date: Tue, 14 Jul 2015 15:00:34 -0500 Subject: [PATCH 4/6] Imported RobotNotRunningError --- src/Selenium2Library/keywords/_logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Selenium2Library/keywords/_logging.py b/src/Selenium2Library/keywords/_logging.py index ad1f2676c..fcdc7a9c4 100644 --- a/src/Selenium2Library/keywords/_logging.py +++ b/src/Selenium2Library/keywords/_logging.py @@ -1,6 +1,6 @@ import os import sys -from robot.libraries.BuiltIn import BuiltIn +from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError from robot.api import logger from keywordgroup import KeywordGroup From dbd00d24bba724c74a7153f940c8ae08bf29e8c1 Mon Sep 17 00:00:00 2001 From: zephraph Date: Thu, 16 Jul 2015 23:20:43 -0500 Subject: [PATCH 5/6] Rework, added specs, madness --- src/Selenium2Library/keywords/_logging.py | 13 +--- src/Selenium2Library/keywords/_screenshot.py | 69 +++++++++++++++---- .../locators/elementfinder.py | 6 +- .../utils/events/scope_event.py | 6 ++ test/acceptance/keywords/screenshots.robot | 11 +++ 5 files changed, 74 insertions(+), 31 deletions(-) diff --git a/src/Selenium2Library/keywords/_logging.py b/src/Selenium2Library/keywords/_logging.py index fcdc7a9c4..a632e4eb8 100644 --- a/src/Selenium2Library/keywords/_logging.py +++ b/src/Selenium2Library/keywords/_logging.py @@ -1,8 +1,8 @@ import os import sys -from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError from robot.api import logger from keywordgroup import KeywordGroup +from robot.libraries.BuiltIn import BuiltIn class _LoggingKeywords(KeywordGroup): @@ -12,17 +12,8 @@ def _debug(self, message): logger.debug(message) def _get_log_dir(self): - # Use screenshot root directory if set - if self.screenshot_root_directory is not None: - return self.screenshot_root_directory + variables = BuiltIn().get_variables() - # If robotframework isn't running use current directory - try: - variables = BuiltIn().get_variables() - except RobotNotRunningError: - return os.getcwd() - - # Otherwise use the log directory provided by RF logfile = variables['${LOG FILE}'] if logfile != 'NONE': return os.path.dirname(logfile) diff --git a/src/Selenium2Library/keywords/_screenshot.py b/src/Selenium2Library/keywords/_screenshot.py index 74488f864..bbdfe956e 100644 --- a/src/Selenium2Library/keywords/_screenshot.py +++ b/src/Selenium2Library/keywords/_screenshot.py @@ -1,15 +1,35 @@ -import os, errno import robot +import os, errno + from keywordgroup import KeywordGroup -from robot.api import logger +from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError +from Selenium2Library import utils class _ScreenshotKeywords(KeywordGroup): def __init__(self): self._screenshot_index = 0 + self._screenshot_path_stack = [] # Public + def set_screenshot_directory(self, path, persist=False): + """Sets the root output directory for captured screenshots. + + ``path`` argument specifies the location to where the screenshots should + be written to. If the specified ``path`` does not exist, it will be created. + Setting ``persist`` specifies that the given ``path`` should + be used for the rest of the test execution, otherwise the path will be restored + at the end of the currently executing scope. + """ + self._create_directory(path) + if persist is False: + self._screenshot_path_stack.append(self.screenshot_root_directory) + # Restore after current scope ends + utils.events.on('scope_end', 'current', self._restore_screenshot_directory) + + self.screenshot_root_directory = path + def capture_page_screenshot(self, filename=None): """Takes a screenshot of the current page and embeds it into the log. @@ -19,22 +39,14 @@ def capture_page_screenshot(self, filename=None): the Robot Framework log file is written into. The `filename` is also considered relative to the same directory, if it is not given in absolute format. If an absolute or relative path is given - but the path does not exist it will be created. + but the path does not exist it will be created. `css` can be used to modify how the screenshot is taken. By default the bakground color is changed to avoid possible problems with background leaking when the page layout is somehow broken. """ path, link = self._get_screenshot_paths(filename) - - target_dir = os.path.dirname(path) - if not os.path.exists(target_dir): - try: - os.makedirs(target_dir) - except OSError as exc: - if exc.errno == errno.EEXIST and os.path.isdir(target_dir): - pass - else: raise + self._create_directory(path) if hasattr(self._current_browser(), 'get_screenshot_as_file'): if not self._current_browser().get_screenshot_as_file(path): @@ -48,6 +60,33 @@ def capture_page_screenshot(self, filename=None): '' % (link, link)) # Private + def _create_directory(self, path): + target_dir = os.path.dirname(path) + if not os.path.exists(target_dir): + try: + os.makedirs(target_dir) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(target_dir): + pass + else: raise + + def _get_screenshot_directory(self): + + # Use screenshot root directory if set + if self.screenshot_root_directory is not None: + return self.screenshot_root_directory + + # Otherwise use RF's log directory + try: + return self._get_log_dir() + + # Unless robotframework isn't running, then use working directory + except RobotNotRunningError: + return os.getcwd() + + # should only be called by set_screenshot_directory + def _restore_screenshot_directory(self): + self.screenshot_root_directory = self._screenshot_path_stack.pop() def _get_screenshot_paths(self, filename): if not filename: @@ -55,7 +94,7 @@ def _get_screenshot_paths(self, filename): filename = 'selenium-screenshot-%d.png' % self._screenshot_index else: filename = filename.replace('/', os.sep) - logdir = self._get_log_dir() - path = os.path.join(logdir, filename) - link = robot.utils.get_link_path(path, logdir) + screenshotDir = self._get_screenshot_directory() + path = os.path.join(screenshotDir, filename) + link = robot.utils.get_link_path(path, screenshotDir) return path, link diff --git a/src/Selenium2Library/locators/elementfinder.py b/src/Selenium2Library/locators/elementfinder.py index 94fd5c097..041471370 100644 --- a/src/Selenium2Library/locators/elementfinder.py +++ b/src/Selenium2Library/locators/elementfinder.py @@ -1,7 +1,6 @@ from Selenium2Library import utils from robot.api import logger from robot.utils import NormalizedDict -from robot.libraries.BuiltIn import BuiltIn class ElementFinder(object): @@ -45,10 +44,7 @@ def register(self, strategy, persist): if not persist: # Unregister after current scope ends - suite = BuiltIn().get_variable_value('${SUITE NAME}') - test = BuiltIn().get_variable_value('${TEST NAME}', '') - scope = suite + '.' + test if test != '' else suite - utils.events.on('scope_end', scope, self.unregister, strategy.name) + utils.events.on('scope_end', 'current', self.unregister, strategy.name) def unregister(self, strategy_name): if strategy_name in self._default_strategies: diff --git a/src/Selenium2Library/utils/events/scope_event.py b/src/Selenium2Library/utils/events/scope_event.py index 688af554c..5e2913221 100644 --- a/src/Selenium2Library/utils/events/scope_event.py +++ b/src/Selenium2Library/utils/events/scope_event.py @@ -1,4 +1,5 @@ from event import Event +from robot.libraries.BuiltIn import BuiltIn class ScopeEvent(Event): def __init__(self, scope, action, *args, **kwargs): @@ -7,6 +8,11 @@ def __init__(self, scope, action, *args, **kwargs): self.action_args = args self.action_kwargs = kwargs + if scope == 'current': + suite = BuiltIn().get_variable_value('${SUITE NAME}') + test = BuiltIn().get_variable_value('${TEST NAME}', '') + self.scope = suite + '.' + test if test != '' else suite + def trigger(self, *args, **kwargs): if args[0] == self.scope: self.action(*self.action_args, **self.action_kwargs) diff --git a/test/acceptance/keywords/screenshots.robot b/test/acceptance/keywords/screenshots.robot index 901dcfc15..dad7d1a16 100644 --- a/test/acceptance/keywords/screenshots.robot +++ b/test/acceptance/keywords/screenshots.robot @@ -31,3 +31,14 @@ Capture page screenshot to non-existing directory [Setup] Remove Directory ${OUTPUTDIR}/screenshot recursive Capture Page Screenshot screenshot/test-screenshot.png File Should Exist ${OUTPUTDIR}/screenshot/test-screenshot.png + +Capture page screenshot to custom root directory + [Setup] Remove Directory ${OUTPUTDIR}/screenshot recursive + Set Screenshot Directory ${OUTPUTDIR}/screenshot + Capture Page Screenshot custom-root-screenshot.png + File Should Exist ${OUTPUTDIR}/screenshot/custom-root-screenshot.png + +Ensure screenshot captures revert to default root directory + [Setup] Remove Files ${OUTPUTDIR}/default-root-screenshot.png + Capture Page Screenshot default-root-screenshot.png + File Should Exist ${OUTPUTDIR}/default-root-screenshot.png From 189b532a0a43a384b40d3cf6ecdc4fcaf362e61b Mon Sep 17 00:00:00 2001 From: zephraph Date: Wed, 22 Jul 2015 10:01:55 -0500 Subject: [PATCH 6/6] Prevent possible undefined error, fix formatting --- src/Selenium2Library/keywords/_screenshot.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Selenium2Library/keywords/_screenshot.py b/src/Selenium2Library/keywords/_screenshot.py index cc6d998a7..f8c88201d 100644 --- a/src/Selenium2Library/keywords/_screenshot.py +++ b/src/Selenium2Library/keywords/_screenshot.py @@ -3,13 +3,15 @@ from Selenium2Library import utils from keywordgroup import KeywordGroup -from robot.libraries.BuiltIn import BuiltIn, RobotNotRunningError +from robot.libraries.BuiltIn import RobotNotRunningError + class _ScreenshotKeywords(KeywordGroup): def __init__(self): self._screenshot_index = 0 self._screenshot_path_stack = [] + self.screenshot_root_directory = None # Public @@ -68,7 +70,8 @@ def _create_directory(self, path): except OSError as exc: if exc.errno == errno.EEXIST and os.path.isdir(target_dir): pass - else: raise + else: + raise def _get_screenshot_directory(self):