robotframework / OldSeleniumLibrary

Deprecated Selenium library for Robot Framework
Apache License 2.0
13 stars 3 forks source link

Support for taking screenshots on remote systems to `Capture Screenshot` keyword #89

Closed spooning closed 9 years ago

spooning commented 9 years ago

Originally submitted to Google Code by Andreas.EbbertKarroum on 4 Feb 2010

In order to use the screenshot as String feature from Selenium, which allows for taking screenshots on a remote system, I added this to init.py. I'm sure it can be done better, but I hope you get the idea :) One of the problems is that the String that holds the binary image, is encoded in the default encoding of the platform on which the screenshot is taken. Since this can vary, we have to provide a parameter for that:

def _absnorm(self, path):
    return os.path.normpath(os.path.abspath(path.replace('/', os.sep)))

def _write_to_file(self, path, content, encoding, mode):
    path = self._absnorm(path)
    parent = os.path.dirname(path)
    if not os.path.exists(parent):
        os.makedirs(parent)
    f = open(path, mode+'b')
    f.write(content.encode(encoding))
    f.close()
    return path

def capture_remote_screenshot(self, codec, path=None):
    """Captures a screenshot and returns it as a string

    Given path must be relative to Robot Framework output directory,
    otherwise the embedded image is not shown in the log file. If path is
    not given, file with name similar to 'selenium-image-x.png' is created
    directly under the output directory.
    """

    # configure path
    if path and os.path.isabs(path):
        raise RuntimeError("Given path must be relative to Robot outpudir")
    if not path:
        path = self._namegen.next()
    outdir = NAMESPACES.current.variables['${outputdir}']
    fullpath = os.path.join(outdir, path)
    if not os.path.exists(os.path.split(fullpath)[0]):
        os.makedirs(os.path.split(fullpath)[0])

    # retrieve remote screenshot
    self._info("Retrieving Screenshot")
    screenshot =

self._selenium.capture_entire_page_screenshot_to_string("background=#CCFFDD") screenshot=base64.b64decode(screenshot) try: screenshot = screenshot.decode(codec) except UnicodeDecodeError: raise RuntimeError("Got error when decoding Screenshot-String with '%s'" % codec)

    # save screenshot
    self._info("Saving screenshot to file '%s'" % fullpath)
    self._write_to_file(fullpath, screenshot, codec, 'w')
    self._html('<td></td></tr><tr><td colspan="3"><a href="%s">'
               '<img src="%s" width="700px"></a></td></tr>' % (path,

path))

spooning commented 9 years ago

Originally submitted to Google Code by Andreas.EbbertKarroum on 10 Feb 2010

I improved the en-/decoding bits. would be nice, if this is included in the upcoming release:

def _absnorm(self, path):
    return os.path.normpath(os.path.abspath(path.replace('/', os.sep)))

def _write_to_file(self, path, content, mode):
    path = self._absnorm(path)
    parent = os.path.dirname(path)
    if not os.path.exists(parent):
        os.makedirs(parent)
    f = open(path, mode+'b')
    f.write(content)
    f.close()
    return path

def capture_remote_screenshot(self, path=None):
    """Captures a screenshot and returns it as a string

    Given path must be relative to Robot Framework output directory,
    otherwise the embedded image is not shown in the log file. If path is
    not given, file with name similar to 'selenium-image-x.png' is created
    directly under the output directory.
    """

    # configure path
    if path and os.path.isabs(path):
        raise RuntimeError("Given path must be relative to Robot outpudir")
    if not path:
        path = self._namegen.next()
    outdir = NAMESPACES.current.variables['${outputdir}']
    fullpath = os.path.join(outdir, path)
    if not os.path.exists(os.path.split(fullpath)[0]):
        os.makedirs(os.path.split(fullpath)[0])

    # retrieve remote screenshot
    self._info("Retrieving Screenshot")
    screenshot =

self._selenium.capture_entire_page_screenshot_to_string("background=#CCFFDD") screenshot=base64.b64decode(screenshot)

    # save screenshot
    self._info("Saving screenshot to file '%s'" % fullpath)
    self._write_to_file(fullpath, screenshot, 'w')
    self._html('<td></td></tr><tr><td colspan="3"><a href="%s">'
               '<img src="%s" width="700px"></a></td></tr>' % (path, path)) 
spooning commented 9 years ago

Originally submitted to Google Code by @pekkaklarck on 10 Feb 2010

Do I get it right that the existing Capture Screenshot keyword cannot be used in this case because it saves the image to the host where Selenium server is running? And it's not a good idea to use capture_entire_page_screenshot_to_string with the local screenshots because that approach doesn't work fully with all browsers?

Instead of adding a new keyword, could we enhance Capture Screenshot so that it would use capture_entire_page_screenshot_to_string method when not connected to a server on localhost? Could you Andreas try to implement a patch using this approach?

Notice that I just refactored the capture_screenshot method so I recommend you to update your svn checkout. I also added some tests for the existing functionality and you can run them e.g. with command atest/run_tests.py python ff --suite screenshots.

spooning commented 9 years ago

Originally submitted to Google Code by @yanne on 11 Feb 2010

Andreas, the latter patch omits the codec argument and decoding with that.

Is that a mistake or was it not needed in the original patch?

spooning commented 9 years ago

Originally submitted to Google Code by @yanne on 11 Feb 2010

I modified the current Capture Screenshot so that if the server host is anything else than 'localhost', capture_entire_page_screenshot_to_string method is used to create the screenshot. I also made some tests which start the server to 127.0.0.1 and they seem to pass and screenshots are created.

I cannot easily test this with a real remote, can you Andreas do that? Just run the latest trunk from svn.

spooning commented 9 years ago

Originally submitted to Google Code by @pekkaklarck on 11 Feb 2010

I did some further tuning in r364.

spooning commented 9 years ago

Originally submitted to Google Code by @pekkaklarck on 12 Feb 2010

We noticed that capture_screenshot_to_string works with all browsers regardless was the server running on local or remote machine and the latest version of the Capture Screenshot uses that. The acceptance tests for this functionality pass and documentation has been updated so I'll close this issue. Andread, could you still please test the latest version in your system with a real remote server?

We also decided to implement a separate Capture Page Screenshot keyword ( issue 93 ) that uses capture_entire_page_screenshot_to_string.

spooning commented 9 years ago

Originally submitted to Google Code by Andreas.EbbertKarroum on 12 Feb 2010

Hi,

from a review of the sources, it looks good. Nice refactoring! One hint to the "Capture Screenshot": yes it works with all browsers, because it does not need the browser to take the screenshot. The selenium server is using java to access the screen content. The concrete problem with this approach is: we are running the selenium server on windows, and as soon as you log out (but leave the process running), you will get screenshots that are all black. (see my blog post about that)

the capture entire page also works with IE, but only if you use "*ieproxy" as browser profile and run the server in -singleWindow. Maybe you want to include that in the documentation for the keyword.

I'll now checkout the code and give it a test!

spooning commented 9 years ago

Originally submitted to Google Code by @pekkaklarck on 12 Feb 2010

Do you Andreas have a concrete proposal on how to enhance the documentation of these Capture Screenshot keywords so that they would include the information about black screenshots when logged out on Windows and the need to use *ieproxy and -singleWindow? Do you know is this stuff documented somewhere other than in the source so that we could add a like to the kw doc too?

spooning commented 9 years ago

Originally submitted to Google Code by Andreas.EbbertKarroum on 12 Feb 2010

Hi,

and here are the results of the german jury ;)

1) Capture Screenshot & localhost & iexploreproxy => OK 2) Capture Page Screenshot & localhost & iexploreproxy/-singleWindow => OK, but Why do I have to provide a filename? Why not: def capture_page_screenshot(self, filename=None, css='background=#CCFFDD'): I would have expected to have the file written to the default directory with default, as with Capture Screenshot

3) Capture Screenshot & localhost & ff => OK 4) Capture Page Screenshot & localhost & ff => OK

5) Capture Screenshot & 127.0.0.1 & iexploreproxy => not tested 6) Capture Page Screenshot & 127.0.0.1 & iexploreproxy => not tested 7) Capture Screenshot & remote & ff => NOK 8) Capture Page Screenshot & remote & ff => NOK There is a file created, but it's not a valid png, because the file is not opened in binary mode: open(path, 'wb').

7 & attached patch applied => Expected NOK: screenshot is all black 8 & attached patch applied => OK

spooning commented 9 years ago

Originally submitted to Google Code by @pekkaklarck on 12 Feb 2010

Thanks a lot for testing Andreas! I fixed 2), 7), and 8) in r412. Could you still test this version to verify how everything works?

Notice also that there's nowadays a separate screenshot.py module. The code was accidentally added back to init.py in r408 but I re-removed it in r411.

spooning commented 9 years ago

Originally submitted to Google Code by @pekkaklarck on 15 Feb 2010

I enhanced the documentation in r420. The link to the latest version is below and comments are appreciated: http://robotframework-seleniumlibrary.googlecode.com/svn/trunk/doc/SeleniumLibrary.html#Capture%20Screenshot

spooning commented 9 years ago

Originally submitted to Google Code by @pekkaklarck on 15 Feb 2010

After the latest fixes and doc enhancements this ought to be done.