diff --git a/CHANGES.rst b/CHANGES.rst index f65d3bece..29bcfdc2e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,13 +3,20 @@ Release Notes 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. +- Added new keyword 'set_screenshot_directory' which can be used to set the output + of screenshots. + [zephraph] + +- Added new keyword Input Text Into Prompt - Modified 'get_alert_message' to accept a parameter 'dismiss' (defaults to true) which can be used to prevent closing the alert message and instead will just return the alerts text. Also created new keyword 'dismiss_alert' to dismiss (default) or confirm the alert without reading the text of the alert. [KingWarin] - -- Added new keyword Input Text Into Prompt + +- Added new keyword Input Text Into Prompt [boakley][ekasteel] - Fixed issue that caused tests to fail when selenium > 2.26 diff --git a/src/Selenium2Library/__init__.py b/src/Selenium2Library/__init__.py index fad8a88c5..da5696fc1 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. @@ -156,6 +162,9 @@ def __init__(self, timeout=5.0, implicit_wait=0.0, run_on_failure='Capture Page `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 | @@ -165,6 +174,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..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 from robot.api import logger from keywordgroup import KeywordGroup +from robot.libraries.BuiltIn import BuiltIn class _LoggingKeywords(KeywordGroup): @@ -13,6 +13,7 @@ def _debug(self, message): def _get_log_dir(self): variables = BuiltIn().get_variables() + 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 a873b11b9..f8c88201d 100644 --- a/src/Selenium2Library/keywords/_screenshot.py +++ b/src/Selenium2Library/keywords/_screenshot.py @@ -1,14 +1,37 @@ -import os, errno import robot +import os, errno + +from Selenium2Library import utils from keywordgroup import KeywordGroup +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 + 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. @@ -18,22 +41,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): @@ -47,6 +62,34 @@ 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: @@ -54,7 +97,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