Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
Release Notes
=============

1.7
1.7.2
----------------
- Fixed an error where regular functions were not able to be used as a custom locator
[zephraph]

- Changed all test files to have a '.robot' extension
[zephraph]

1.7.1 (hotfix)
----------------
- Remove references to GLOBAL_VARIABLES for RF 2.9 compatibility

1.7
----------------
- Added keyword 'List Windows' to return a list of all window handles.
[divfor]
Expand Down
9 changes: 5 additions & 4 deletions src/Selenium2Library/keywords/_logging.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import sys
from robot.variables import GLOBAL_VARIABLES
from robot.libraries.BuiltIn import BuiltIn
from robot.api import logger
from keywordgroup import KeywordGroup

Expand All @@ -12,10 +12,11 @@ def _debug(self, message):
logger.debug(message)

def _get_log_dir(self):
logfile = GLOBAL_VARIABLES['${LOG FILE}']
variables = BuiltIn().get_variables()
logfile = variables['${LOG FILE}']
if logfile != 'NONE':
return os.path.dirname(logfile)
return GLOBAL_VARIABLES['${OUTPUTDIR}']
return variables['${OUTPUTDIR}']

def _html(self, message):
logger.info(message, True, False)
Expand All @@ -38,4 +39,4 @@ def _log_list(self, items, what='item'):
return items

def _warn(self, message):
logger.warn(message)
logger.warn(message)
84 changes: 67 additions & 17 deletions src/Selenium2Library/keywords/_screenshot.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,57 @@
import os, errno
import robot
from keywordgroup import KeywordGroup
from robot.api import logger

class _ScreenshotKeywords(KeywordGroup):

def __init__(self):
self._screenshot_index = 0
self._screenshot_index = {}

# Public

def capture_page_screenshot(self, filename=None):
def capture_page_screenshot(self, filename=None, overwrite=False):
"""Takes a screenshot of the current page and embeds it into the log.

`filename` argument specifies the name of the file to write the
screenshot into. If no `filename` is given, the screenshot is saved into file
`selenium-screenshot-<counter>.png` under the directory where
the Robot Framework log file is written into. The `filename` is
screenshot into. If no `filename` is given, the screenshot is
saved into file `selenium-screenshot-<counter>.png` under the directory
where 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)
With `overwrite` it is possible to define what is done if file already
exist. By default filename is not overwritten but new one is created
by adding <counter> in the end. Example if capture.png exist and
this is the first overwrite, then new file is created with name
capture-1.png

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd update this documentation to say something like

By default, if filename is specified but a screenshot of that name already exists, an index will be appended to the screenshot. For example, if 'capture.png' exists then the new screenshot will be 'capture-1.png.' This behavior can be overridden by setting overwrite to True.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, the current code does not actually do this, but by quick thinking, doing this should not be too difficult. And at least in my mind, it would make life easier.

So is this what we want, at least it is good for me (and makes my life even more simpler):

| Screen Capture | filename=${BROWSER}.png |
| Screen Capture | filename=${BROWSER}.png |
| File Should Exist  | ${OUTPUTDIR}${/}${BROWSER}.png |
| File Should Exist  | ${OUTPUTDIR}${/}${BROWSER}-1.png |
| Screen Capture |
| Screen Capture |
| File Should Exist  | ${OUTPUTDIR}${/}selenium-screenshot-2.png |
| File Should Exist  | ${OUTPUTDIR}${/}selenium-screenshot-3.png |
| Screen Capture | filename=overwrite-${BROWSER}.png | overwrite=${True} |
| Screen Capture | filename=overwrite-${BROWSER}.png | overwrite=${True} |
| File Should Exist  | ${OUTPUTDIR}${/}overwrite-${BROWSER}.png | 
| File Should Not Exist | ${OUTPUTDIR}${/}overwrite-${BROWSER}-4.png | 

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's close to what I was thinking. The only thing about that I'm not that fond of is the fact that there's a global counter. I would much rather each file have it's own count, though I know that would be a little bit more work.

Essentially the flow would be like this.

# This code is more psudo than an actual implementation
if not overwrite:
  files = listdir(path)
  file = filename
  while file in files:
    file = filename + '-' + index + extension
    index += 1
# write file

That's only slightly more complicated, but it works more constantly to how I would expect it to.

Your test would then be like this

| Screen Capture | filename=${BROWSER}.png |
| Screen Capture | filename=${BROWSER}.png |
| File Should Exist  | ${OUTPUTDIR}${/}${BROWSER}.png |
| File Should Exist  | ${OUTPUTDIR}${/}${BROWSER}-1.png |
| Screen Capture |
| Screen Capture |
| File Should Exist  | ${OUTPUTDIR}${/}selenium-screenshot-1.png |
| File Should Exist  | ${OUTPUTDIR}${/}selenium-screenshot-2.png |
| Screen Capture | filename=overwrite-${BROWSER}.png | overwrite=${True} |
| Screen Capture | filename=overwrite-${BROWSER}.png | overwrite=${True} |
| File Should Exist  | ${OUTPUTDIR}${/}overwrite-${BROWSER}.png | 
| File Should Not Exist | ${OUTPUTDIR}${/}overwrite-${BROWSER}-1.png | 

Example:
| Open Browser | www.someurl.com | browser=${BROWSER} |
| Capture Page Screenshot | filename=${BROWSER} |
| Capture Page Screenshot | filename=${BROWSER} |
| Capture Page Screenshot | filename=${BROWSER} |
| File Should Exist | ${OUTPUTDIR}${/}${BROWSER}.png |
| File Should Exist | ${OUTPUTDIR}${/}${BROWSER}-1.png |
| File Should Exist | ${OUTPUTDIR}${/}${BROWSER}-2.png |
| Capture Page Screenshot |
| Capture Page Screenshot |
| File Should Exist | ${OUTPUTDIR}${/}selenium-screenshot-1.png |
| File Should Exist | ${OUTPUTDIR}${/}selenium-screenshot-2.png |
| Capture Page Screenshot | filename=DefaultName.png | overwrite=${True} |
| Capture Page Screenshot | filename=DefaultName.png | overwrite=${True} |
| File Should Exist | ${OUTPUTDIR}${/}DefaultName.png |
| File Should Not Exist | ${OUTPUTDIR}${/}DefaultName-1.png |

*NOTE:* The `overwrite` is ignored if `filename` is not defined
Example:
| Open Browser | www.someurl.com | browser=${BROWSER} |
| Capture Page Screenshot | overwrite=${True} | # overwrite is ignored |
| Capture Page Screenshot | overwrite=${True} | # overwrite is ignored |
| File Should Exist | ${OUTPUTDIR}${/}selenium-screenshot-1.png |
| File Should Exist | ${OUTPUTDIR}${/}selenium-screenshot-2.png |
"""
path, link = self._get_screenshot_paths(filename, overwrite=overwrite)
target_dir = os.path.dirname(path)
if not os.path.exists(target_dir):
try:
Expand All @@ -49,13 +74,38 @@ def capture_page_screenshot(self, filename=None):

# Private

def _get_screenshot_paths(self, filename):
def _get_screenshot_paths(self, filename, overwrite=False):
if not filename:
self._screenshot_index += 1
filename = 'selenium-screenshot-%d.png' % self._screenshot_index
index = self._get_new_index('selenium-screenshot')
filename = 'selenium-screenshot-%d.png' % index
elif filename and not overwrite:
filename = self._screenshot_existence(filename.replace('/',
os.sep))
else:
filename = filename.replace('/', os.sep)
logdir = self._get_log_dir()
path = os.path.join(logdir, filename)
path, logdir = self._get_logdir_path(filename)
link = robot.utils.get_link_path(path, logdir)
return path, link

def _screenshot_existence(self, filename):
if os.path.exists(self._get_logdir_path(filename)[0]):
index = self._get_new_index(filename)
try:
return '-%s.png'.join(filename.rsplit('.png', 1)) % index
except TypeError:
return filename + '-%s' % index
else:
return filename

def _get_logdir_path(self, filename):
logdir = self._get_log_dir()
return os.path.join(logdir, filename), logdir

def _get_new_index(self, filename):
try:
index = self._screenshot_index[filename] + 1
self._screenshot_index[filename] = index
return index
except KeyError:
self._screenshot_index[filename] = 1
return 1
28 changes: 13 additions & 15 deletions src/Selenium2Library/keywords/_tableelement.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import os
import sys
from robot.variables import GLOBAL_VARIABLES
from robot.api import logger
from Selenium2Library.locators import TableElementFinder
from keywordgroup import KeywordGroup

Expand All @@ -17,10 +15,10 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'):

Row and column number start from 1. Header and footer rows are
included in the count. A negative row or column number can be used
to get rows counting from the end (end: -1). Cell content from header
or footer rows can be obtained with this keyword. To understand how
to get rows counting from the end (end: -1). Cell content from header
or footer rows can be obtained with this keyword. To understand how
tables are identified, please take a look at the `introduction`.

See `Page Should Contain` for explanation about `loglevel` argument.
"""
row = int(row)
Expand All @@ -32,13 +30,13 @@ def get_table_cell(self, table_locator, row, column, loglevel='INFO'):
table = self._table_element_finder.find(self._current_browser(), table_locator)
if table is not None:
rows = table.find_elements_by_xpath("./thead/tr")
if row_index >= len(rows) or row_index < 0:
if row_index >= len(rows) or row_index < 0:
rows.extend(table.find_elements_by_xpath("./tbody/tr"))
if row_index >= len(rows) or row_index < 0:
if row_index >= len(rows) or row_index < 0:
rows.extend(table.find_elements_by_xpath("./tfoot/tr"))
if row_index < len(rows):
columns = rows[row_index].find_elements_by_tag_name('th')
if column_index >= len(columns) or column_index < 0:
if column_index >= len(columns) or column_index < 0:
columns.extend(rows[row_index].find_elements_by_tag_name('td'))
if column_index < len(columns):
return columns[column_index].text
Expand All @@ -58,7 +56,7 @@ def table_cell_should_contain(self, table_locator, row, column, expected, loglev

To understand how tables are identified, please take a look at
the `introduction`.

See `Page Should Contain` for explanation about `loglevel` argument.
"""
message = ("Cell in table '%s' in row #%s and column #%s "
Expand All @@ -78,7 +76,7 @@ def table_cell_should_contain(self, table_locator, row, column, expected, loglev
def table_column_should_contain(self, table_locator, col, expected, loglevel='INFO'):
"""Verifies that a specific column contains `expected`.

The first leftmost column is column number 1. A negative column
The first leftmost column is column number 1. A negative column
number can be used to get column counting from the end of the row (end: -1).
If the table contains cells that span multiple columns, those merged cells
count as a single column. For example both tests below work,
Expand Down Expand Up @@ -136,10 +134,10 @@ def table_header_should_contain(self, table_locator, expected, loglevel='INFO'):
def table_row_should_contain(self, table_locator, row, expected, loglevel='INFO'):
"""Verifies that a specific table row contains `expected`.

The uppermost row is row number 1. A negative column
number can be used to get column counting from the end of the row
(end: -1). For tables that are structured with thead, tbody and tfoot,
only the tbody section is searched. Please use `Table Header Should Contain`
The uppermost row is row number 1. A negative column
number can be used to get column counting from the end of the row
(end: -1). For tables that are structured with thead, tbody and tfoot,
only the tbody section is searched. Please use `Table Header Should Contain`
or `Table Footer Should Contain` for tests against the header or
footer content.

Expand Down Expand Up @@ -169,4 +167,4 @@ def table_should_contain(self, table_locator, expected, loglevel='INFO'):
if element is None:
self.log_source(loglevel)
raise AssertionError("Table identified by '%s' should have contained text '%s'." \
% (table_locator, expected))
% (table_locator, expected))
2 changes: 1 addition & 1 deletion src/Selenium2Library/locators/customlocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def find(self, *args):
# Allow custom locators to be keywords or normal methods
if isinstance(self.finder, string_type):
element = BuiltIn().run_keyword(self.finder, *args)
elif hasattr(self.finder, '__caller__'):
elif hasattr(self.finder, '__call__'):
element = self.finder(*args)
else:
raise AttributeError('Invalid type provided for Custom Locator %s' % self.name)
Expand Down
2 changes: 1 addition & 1 deletion src/Selenium2Library/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = '1.7.0'
VERSION = '1.7.1'
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
*** Setting ***
Resource resource.txt
Resource resource.robot
Force Tags Regression

*** Variables ***
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
*Setting*
Resource resource.txt
Resource resource.robot
Library Collections

*Test Cases*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*Setting*
Resource ../resource.txt
Resource ../resource.robot
Suite Setup Open Browser To Start Page
Suite Teardown Close All Browsers
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*** Settings ***
Test Setup Go To Page "javascript/dynamic_content.html"
Suite Teardown Set Selenium Timeout 5 seconds
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Should Not Timeout If Callback Invoked Immediately
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*** Settings ***
Test Setup Go To Page "forms/prefilled_email_form.html"
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Checkbox Should Be Selected
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*** Settings ***
Suite Setup Go To Page "javascript/click.html"
Test Setup Initialize Page
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Click Element
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*** Settings ***
Suite Setup Go To Page "javascript/click_at_coordinates.html"
Test Setup Initialize Page
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Click Element At Coordinates
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*** Settings ***
Test Setup Go To Front Page
Default Tags assertions
Resource ../resource.txt
Resource ../resource.robot


*** Test Cases ***
Expand Down
2 changes: 1 addition & 1 deletion test/acceptance/keywords/cookies.robot
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Suite Setup Go To Page "cookies.html"
Suite Teardown Delete All Cookies
Test Setup Add Cookies
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Get Cookies
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*** Settings ***
Test Setup Go To Page "forms/enabled_disabled_fields_form.html"
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Input Text
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*** Settings ***
Test Setup Go To Page "links.html"
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Get Elements
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*** Settings ***
Test Setup Go To Page "forms/named_submit_buttons.html"
Resource ../resource.txt
Resource ../resource.robot
Library OperatingSystem


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
*Setting*
Resource ../resource.txt
Resource ../resource.robot
Test Setup Go To Page "frames/frameset.html"
Test Teardown UnSelect Frame

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*** Settings ***
Test Setup Go To Page "javascript/dynamic_content.html"
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Clicking Elements Should Activate Javascript
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*** Settings ***
Test Setup Go To Page "forms/prefilled_email_form.html"
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Get List Items From Single-Select List
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*** Settings ***
Test Setup Go To Page "mouse/index.html"
Resource ../resource.txt
Resource ../resource.robot

*** Test Cases ***
Mouse Over
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
*** Settings ***
Test Setup Go To Page "links.html"
Resource ../resource.txt
Resource ../resource.robot

*** Variables ***
${LINKS TITLE} (root)/links.html
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*** Settings ***
Suite Setup Run Keywords Go To Front Page Set Info Loglevel
Suite Teardown Set Debug Loglevel
Resource ../resource.txt
Resource ../resource.robot

*** Variables ***
${PAGE TITLE} (root)/index.html
Expand Down
Loading