3

I'm working on some Selenium scripts to test sites across different devices, browsers, and platforms. I can get the scripts to work using the same code except for two lines where I define command executor URL and the browser capabilities. I'm trying to get build a single script where I can define these lines using command line arguments.

Here's my code:

from selenium import webdriver
import time
import sys
import getopt
def main(argv):
    #define desired browser capabilities
    desktopCapabilities = {'browserName': 'chrome'} #change browserName to: 'firefox' 'chrome' 'safari' 'internet explorer'
    iosCapabilities = {'platformName': 'iOS' ,'platformVersion': '8.1' ,'deviceName': 'iPad Air','browserName': 'Safari'}
    androidCapabilities = {'chromeOptions': {'androidPackage': 'com.android.chrome'}}
    # Establish the command executor URL
    desktopExecutor = 'http://127.0.0.1:4444/wd/hub'
    iosExecutor = 'http://127.0.0.1:4723/wd/hub'
    androidExecutor = 'http://127.0.0.1:9515'

    cmdExecutor = desktopExecutor
    browserCapabilities = desktopCapabilities
    try:
      opts, args = getopt.getopt(argv,"he:c:",["executor=","capabilities="])
    except getopt.GetoptError:
      print 'test.py -e <executor> -c <capabilities>'
      sys.exit(2)
    for opt, arg in opts:
      if opt == '-h':
         print 'test.py -e <executor> -c <capabilities>'
         sys.exit()
      elif opt in ("-e", "--executor"):
         cmdExecutor = arg
      elif opt in ("-c", "--capabilities"):
         browserCapabilities = arg

    print 'Command executor is:', cmdExecutor
    print 'Desired capabilities are:', browserCapabilities
    driver = webdriver.Remote(command_executor=cmdExecutor, desired_capabilities=browserCapabilities)
    driver.get("http://google.com")
    time.sleep(5)
    driver.quit()
if __name__ == "__main__":
   main(sys.argv[1:])

This code runs as expected if I don't add any arguments via the command line. It also works if I run it with:

python test.py -e 'http://127.0.0.1:4444/wd/hub'

It breaks if I run it using the following command because -c is not passed as a dictionary:

python test.py -e 'http://127.0.0.1:4444/wd/hub' -c {'browserName': 'firefox'}

How can I get this to run this with:

python test.py -e iosExecutor -c iosCapabilities

Here's the output I get when I run the command mentioned above:

 python my_script.py -e iosExecutor --capabilities iosCapabilities
Command executor is: iosExecutor
Desired capabilities are: iosCapabilities
Traceback (most recent call last):
  File "my_script.py", line 38, in <module>
    main(sys.argv[1:])
  File "my_script.py", line 33, in main
    driver = webdriver.Remote(command_executor=cmdExecutor, desired_capabilities=browserCapabilities)
  File "/Library/Python/2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 62, in __init__
    raise WebDriverException("Desired Capabilities must be a dictionary")
selenium.common.exceptions.WebDriverException: Message: Desired Capabilities must be a dictionary

Basically it is running as if I passed line 33 like this:

driver = webdriver.Remote(command_executor="iosExecutor", desired_capabilities="iosCapabilities")

It also works if I hard code lines 15 and 16 with "iosExecutor" and "iosCapabilites" so this tells me it's how I'm passing info from the CLI.

Any advice would be great. I'm quite new to this (programming) so I'm guessing there may be a better way to do this, but Google hasn't cleared it up for me.

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
Beto
  • 95
  • 2
  • 7
  • why not to add `-c` as a string parameter such as "ios", "android" or "desktop" and programmatically select one of the capabilities parameters? i mean if given command line argument is equal to "android" set browserCapabilities=androidCapabilities – laltin Dec 27 '14 at 00:47

1 Answers1

4

Using argparse would make things much easier. For capabilities you can use json.loads, or ast.literal_eval as a type, as, for example, was done here:

As for executor, either pass a url in as a string, or define a user-friendly mapping, like:

EXECUTORS = {
    'desktop': 'http://127.0.0.1:4444/wd/hub',
    'ios': 'http://127.0.0.1:4723/wd/hub',
    'android': 'http://127.0.0.1:9515'
}

Here's how the code would look like in the end:

import ast
import time
import argparse

from selenium import webdriver


EXECUTORS = {
    'desktop': 'http://127.0.0.1:4444/wd/hub',
    'ios': 'http://127.0.0.1:4723/wd/hub',
    'android': 'http://127.0.0.1:9515'
}

parser = argparse.ArgumentParser(description='My program.')
parser.add_argument('-c', '--capabilities', type=ast.literal_eval)
parser.add_argument('-e', '--executor', type=str, choices=EXECUTORS)

args = parser.parse_args()

driver = webdriver.Remote(command_executor=EXECUTORS[args.executor], desired_capabilities=args.capabilities)
driver.get("http://google.com")
time.sleep(5)
driver.quit()

Sample script runs:

$ python test.py -e android -c "{'chromeOptions': {'androidPackage': 'com.android.chrome'}}"
$ python test.py -e ios -c "{'platformName': 'iOS' ,'platformVersion': '8.1' ,'deviceName': 'iPad Air','browserName': 'Safari'}" 
$ python test.py -e desktop -c "{'browserName': 'chrome'}"

And, as a bonus, you get a built-in help magically made by argparse:

$ python test.py --help
usage: test.py [-h] [-c CAPABILITIES] [-e {android,ios,desktop}]

My program.

optional arguments:
  -h, --help            show this help message and exit
  -c CAPABILITIES, --capabilities CAPABILITIES
  -e {android,ios,desktop}, --executor {android,ios,desktop}
Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • This is a great answer. It solves my problem and gives a much better approach to what I wanted to do. Thanks! – Beto Dec 27 '14 at 01:53