Monday, October 5, 2015

Switch Driver to FB Dialogue, Interact With It, Switch Back to Main Site

Interacting with social media dialogues is not a reason for trepidation. It's actually very easy.

FB_button = self.driver.find_element_by_css_selector("FB_button")
FB_button.click()
time.sleep(2)

#switch to FB dialogue, sign in.
main_site = self.driver.window_handles[0]
FB_dialogue = self.driver.window_handles[1]
if FB_dialogue:
    print "FB dialogue detected"
    self.driver.switch_to_window(FB_dialogue)
    FB_email = self.driver.find_element_by_id("email")
    FB_password = self.driver.find_element_by_id("pass")
    FB_login_button = self.driver.find_element_by_id("u_0_2")
    FB_email.send_keys("username@wherever.tld")
    FB_password.send_keys("password")
    FB_login_button.click()

time.sleep(2)
self.driver.switch_to_window(main_site)

Saturday, September 19, 2015

SPQA - A Mini-framework for Selenium/Python Automation

Please visit: https://github.com/seleniumpythonqa/SPQA

Topics covered include:
  • Desktops
  • Chromedriver (desktop & Android)
  • Browserstack (desktops & devices)
  • Appium - XCODE iOS emulators
  • Chrome Devtools Mobile Emulators
  • Selendroid (Android & Android Virtual Devices)

Chromedriver: "Element is not clickable at point (x, y). Other element would receive the click.."

If you've done any automated testing with Chromedriver, you'll likely have come across the following classic error at some stage:
WebDriverException: Message: unknown error: Element is not clickable at point (x, y). Other element would receive the click: <div> class='header'>...</div>
It's triggered by a block like this one:

element = self.driver.find_element_by_css_selector("a.terms")
element.click()


The solution? Use javascript:

self.driver.execute_script("document.querySelector('a.terms').click();")

Note that the error above is confined to Chrome/Chromedriver only. The JS solution works well cross-browser, cross-device and cross-platform.

More on Stack Overflow

Sunday, August 30, 2015

Generate Fullpage Screenshot in Chrome

Firefox is my go-to browser for any automation tasks at first, because...it's like that, and that's the way it is. Obviously Chrome is a great browser too, and chromedriver is one of the most useful automation tools out there. One area where Chrome falls down however is that it will only screenshot the available viewport.

Here's a workaround. NB This script requires PIL (Python Imaging Library). If you're running PY 3.x, you'll need to have Python 2.7 installed alongside it.

In this demonstration, the method is contained in a file called util.py, which is imported by test.py. All scripts and created images live in the same directory. Obviously, you can adjust format, quality of image etc.

test.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
"""
This script uses a simplified version of the one here:
https://snipt.net/restrada/python-selenium-workaround-for-full-page-screenshot-using-chromedriver-2x/

It contains the *crucial* correction added in the comments by Jason Coutu.
"""

import sys

from selenium import webdriver
import unittest

import util

class Test(unittest.TestCase):
    """ Demonstration: Get Chrome to generate fullscreen screenshot """

    def setUp(self):
        self.driver = webdriver.Chrome()

    def tearDown(self):
        self.driver.quit()

    def test_fullpage_screenshot(self):
        ''' Generate document-height screenshot '''
        url = "http://effbot.org/imagingbook/introduction.htm"
        self.driver.get(url)
        util.fullpage_screenshot(self.driver, "test.png")


if __name__ == "__main__":
    unittest.main(argv=[sys.argv[0]])


util.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import os
import time

from PIL import Image

def fullpage_screenshot(driver, file):

        print("Starting chrome full page screenshot workaround ...")

        total_width = driver.execute_script("return document.body.offsetWidth")
        total_height = driver.execute_script("return document.body.parentNode.scrollHeight")
        viewport_width = driver.execute_script("return document.body.clientWidth")
        viewport_height = driver.execute_script("return window.innerHeight")
        print("Total: ({0}, {1}), Viewport: ({2},{3})".format(total_width, total_height,viewport_width,viewport_height))
        rectangles = []

        i = 0
        while i < total_height:
            ii = 0
            top_height = i + viewport_height

            if top_height > total_height:
                top_height = total_height

            while ii < total_width:
                top_width = ii + viewport_width

                if top_width > total_width:
                    top_width = total_width

                print("Appending rectangle ({0},{1},{2},{3})".format(ii, i, top_width, top_height))
                rectangles.append((ii, i, top_width,top_height))

                ii = ii + viewport_width

            i = i + viewport_height

        stitched_image = Image.new('RGB', (total_width, total_height))
        previous = None
        part = 0

        for rectangle in rectangles:
            if not previous is None:
                driver.execute_script("window.scrollTo({0}, {1})".format(rectangle[0], rectangle[1]))
                print("Scrolled To ({0},{1})".format(rectangle[0], rectangle[1]))
                time.sleep(0.2)

            file_name = "part_{0}.png".format(part)
            print("Capturing {0} ...".format(file_name))

            driver.get_screenshot_as_file(file_name)
            screenshot = Image.open(file_name)

            if rectangle[1] + viewport_height > total_height:
                offset = (rectangle[0], total_height - viewport_height)
            else:
                offset = (rectangle[0], rectangle[1])

            print("Adding to stitched image with offset ({0}, {1})".format(offset[0],offset[1]))
            stitched_image.paste(screenshot, offset)

            del screenshot
            os.remove(file_name)
            part = part + 1
            previous = rectangle

        stitched_image.save(file)
        print("Finishing chrome full page screenshot workaround...")
        return True


Credits
The script used in util.py is essentially a shortened version of the one you will find here. Many thanks to restrada for coming up with it.

Saturday, April 25, 2015

Chrome - Maximise Window

You may have noticed that Chrome seems to ignore the maximise_window() command. Here is a simple solution.

self.driver = webdriver.Chrome()

if not self.driver.capabilities['browserName'] == 'Ie':
     screen_width  = self.driver.execute_script("return window.screen.availWidth;")
     screen_height = self.driver.execute_script("return window.screen.availHeight;")
     self.driver.set_window_size(screen_width, screen_height)
else:
     self.driver.maximize_window()

Not surprisingly, IE will baulk at this, hence the if block. It will get Chrome full-screening nicely however.

Consider the following scenario. You are preparing a Selenium script for a responsive site which in places has slightly different CSS selectors for desktop and devices. By hardcoding a value for screen_width, you can view the device HTM in your desktop browser and inspect the differing selectors (for devices) in devtools in the normal fashion.

Saturday, March 28, 2015

Workaround for broken 'safariIgnoreFraudWarning' Capability on XCODE Simulator via Appium

The Problem
While executing a Selenium test on an XCode simulator using Appium, you encounter the following screen:



To proceed beyond this point, you need to manually click 'Ignore this Warning'. Not good; defeats the purpose of automating your test.

This despite having explicitly instructed the Simulator to suppress the warning in the Desired Capabilites:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
self.DC = {
        'platformName' : 'iOS',
        'platformVersion' : '8.0',
        'deviceName' : 'iPhone 4s',
        'browserName' : 'Safari',
        'safariIgnoreFraudWarning' : 'True',
        'deviceOrientation': 'portrait'
}

self.appium_simulator = webdriver.Remote(command_executor='http://127.0.0.1:4723/wd/hub', desired_capabilities=self.DC)


The Solution
The "Possible Phishing Site" is a regular HTM page and therefore can be interacted with in the normal fashion. You just need to click that 'Ignore this Warning' button...


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def possible_phishing_site(self):

    URL = "https://username:password@testurl.tld"
    self.appium_simulator.get(URL)
    time.sleep(5)

    #'Possible Phising Site' message...
    try:
        ITW = self.appium_simulator.find_element_by_css_selector("button#ignore_this_warning")
        if ITW:
            print "We are on the 'Possible Phising Page' in Safari"
            time.sleep(1)
            ITW.click()
            print "'Ignore this Warning' button clicked"
            time.sleep(5)
    except Exception as e:
        print "Exception:", str(e)
        print "'Possible Phising Page' not reachable via Selenium"




Job done.