Skip to content

Commit c3c2abd

Browse files
committed
Merge pull request #5 from browserstack/args
Custom Arguments
2 parents 518b8ae + 4106d57 commit c3c2abd

File tree

3 files changed

+147
-89
lines changed

3 files changed

+147
-89
lines changed

README.md

+102-25
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,121 @@
22

33
[![Build Status](https://travis-ci.org/browserstack/browserstack-local-python.svg?branch=master)](https://travis-ci.org/browserstack/browserstack-local-python)
44

5-
## Setup
5+
A simple Python wrapper for BrowserStack Local Binary.
6+
7+
## Installation
68

79
```
810
pip install browserstack-local
911
```
1012

11-
## API
13+
## Example
14+
15+
```
16+
from browserstack.local import Local
17+
18+
#creates an instance of Local
19+
bs_local = Local()
20+
21+
#replace <browserstack-accesskey> with your key. You can also set an environment variable - "BROWSERSTACK_ACCESS_KEY".
22+
bs_local_args = { "key": "<browserstack-accesskey>" }
23+
24+
#starts the Local instance with the required arguments
25+
bs_local.start(**bs_local_args)
26+
27+
#check if BrowserStack local instance is running
28+
print bs_local.isRunning()
29+
30+
#stop the Local instance
31+
bs_local.stop()
32+
33+
```
34+
35+
## Arguments
36+
37+
Apart from the key, all other BrowserStack Local modifiers are optional. For the full list of modifiers, refer [BrowserStack Local modifiers](https://www.browserstack.com/local-testing#modifiers). For examples, refer below -
38+
39+
#### Verbose Logging
40+
To enable verbose logging -
41+
```
42+
bs_local_args = { "key": "<browserstack-accesskey>" , "v": "true"}
43+
```
44+
45+
#### Folder Testing
46+
To test local folder rather internal server, provide path to folder as value of this option -
47+
```
48+
bs_local_args = { "key": "<browserstack-accesskey>" , "f": "/my/awesome/folder"}
49+
```
50+
51+
#### Force Start
52+
To kill other running Browserstack Local instances -
53+
```
54+
bs_local_args = { "key": "<browserstack-accesskey>" , "force": "true"}
55+
```
56+
57+
#### Only Automate
58+
To disable local testing for Live and Screenshots, and enable only Automate -
59+
```
60+
bs_local_args = { "key": "<browserstack-accesskey>" , "onlyAutomate": "true"}
61+
```
1262

13-
### Constructor
63+
#### Force Local
64+
To route all traffic via local(your) machine -
65+
```
66+
bs_local_args = { "key": "<browserstack-accesskey>" , "forcelocal": "true"}
67+
```
1468

15-
* `browserstack.Local()`: creates an instance of Local
69+
#### Proxy
70+
To use a proxy for local testing -
1671

17-
### Methods
72+
* proxyHost: Hostname/IP of proxy, remaining proxy options are ignored if this option is absent
73+
* proxyPort: Port for the proxy, defaults to 3128 when -proxyHost is used
74+
* proxyUser: Username for connecting to proxy (Basic Auth Only)
75+
* proxyPass: Password for USERNAME, will be ignored if USERNAME is empty or not specified
1876

19-
* `start(**options)`: starts Local instance with options. The options available are detailed below.
20-
* `stop()`: stops the Local instance
21-
* `isRunning()`: checks if Local instance is running
77+
```
78+
bs_local_args = { "key": "<browserstack-accesskey>", "proxyHost": "127.0.0.1", "proxyPort": "8000", "proxyUser": "user", "proxyPass": "password"}
79+
```
2280

23-
### Options
81+
#### Local Identifier
82+
If doing simultaneous multiple local testing connections, set this uniquely for different processes -
83+
```
84+
bs_local_args = { "key": "<browserstack-accesskey>" , "localIdentifier": "randomstring"}
85+
```
2486

25-
* `key`: BrowserStack Access Key
26-
* `v`: Provides verbose logging
27-
* `f`: If you want to test local folder rather internal server, provide path to folder as value of this option
28-
* `force`: Kill other running Browserstack Local
29-
* `only`: Restricts Local Testing access to specified local servers and/or folders
30-
* `forcelocal`: Route all traffic via local machine
31-
* `onlyAutomate`: Disable Live Testing and Screenshots, just test Automate
32-
* `proxyHost`: Hostname/IP of proxy, remaining proxy options are ignored if this option is absent
33-
* `proxyPort`: Port for the proxy, defaults to 3128 when -proxyHost is used
34-
* `proxyUser`: Username for connecting to proxy (Basic Auth Only)
35-
* `proxyPass`: Password for USERNAME, will be ignored if USERNAME is empty or not specified
36-
* `localIdentifier`: If doing simultaneous multiple local testing connections, set this uniquely for different processes
37-
* `hosts`: List of hosts and ports where Local must be enabled for eg. localhost,3000,1,localhost,3001,0
38-
* `logfile`: Path to file where Local logs be saved to
39-
* `binarypath`: Optional path to Local binary
87+
## Additional Arguments
4088

89+
#### Binary Path
4190

42-
## Tests
91+
By default, BrowserStack local wrappers try downloading and executing the latest version of BrowserStack binary in ~/.browserstack or the present working directory or the tmp folder by order. But you can override these by passing the -binarypath argument.
92+
Path to specify local Binary path -
93+
```
94+
bs_local_args = { "key": "<browserstack-accesskey>" , "binarypath": "/browserstack/BrowserStackLocal"}
95+
```
96+
97+
#### Logfile
98+
To save the logs to the file while running with the '-v' argument, you can specify the path of the file. By default the logs are saved in the local.log file in the present woring directory.
99+
To specify the path to file where the logs will be saved -
100+
```
101+
bs_local_args = { "key": "<browserstack-accesskey>" , "v": "true", "logfile": "/browserstack/logs.txt"}
102+
```
103+
104+
## Contribute
43105

44106
To run the test suite run, `python -m unittest discover`.
45107

108+
### Reporting bugs
109+
110+
You can submit bug reports either in the Github issue tracker.
111+
112+
Before submitting an issue please check if there is already an existing issue. If there is, please add any additional information give it a "+1" in the comments.
113+
114+
When submitting an issue please describe the issue clearly, including how to reproduce the bug, which situations it appears in, what you expect to happen, what actually happens, and what platform (operating system and version) you are using.
115+
116+
### Pull Requests
117+
118+
We love pull requests! We are very happy to work with you to get your changes merged in, however, please keep the following in mind.
119+
120+
* Adhere to the coding conventions you see in the surrounding code.
121+
* Include tests, and make sure all tests pass.
122+
* Before submitting a pull-request, clean up the git history by going over your commits and squashing together minor changes and fixes into the corresponding commits. You can do this using the interactive rebase command.

browserstack/local.py

+22-55
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,38 @@
33
from browserstack.bserrors import BrowserStackLocalError
44

55
class Local:
6-
def __init__(self, key=None, binary_path=None):
7-
self.options = {
8-
'key': key,
9-
'logfile_flag': '-logFile',
10-
'logfile_path': os.path.join(os.getcwd(), 'local.log')
11-
}
12-
self.local_folder_path = None
13-
self.local_logfile_path = self.options['logfile_path']
6+
def __init__(self, key=os.environ['BROWSERSTACK_ACCESS_KEY'], binary_path=None):
7+
self.key = key
8+
self.options = None
9+
self.local_logfile_path = os.path.join(os.getcwd(), 'local.log')
1410

15-
def __xstr(self, obj):
16-
if obj is None:
17-
return ''
18-
return str(obj)
11+
def __xstr(self, key, value):
12+
if key is None:
13+
return ['']
14+
if str(value).lower() == "true":
15+
return ['-' + key]
16+
else:
17+
return ['-' + key, value]
1918

2019
def _generate_cmd(self):
21-
options_order = ['logfile_flag', 'logfile_path', 'folder_flag', 'key', 'folder_path', 'forcelocal', 'local_identifier', 'only', 'only_automate', 'proxy_host', 'proxy_port', 'proxy_user', 'proxy_pass', 'forceproxy', 'force', 'verbose', 'hosts']
22-
cmd = [self.__xstr(self.options.get(o)) for o in options_order if self.options.get(o) is not None]
23-
return [self.binary_path] + cmd
20+
cmd = [self.binary_path, '-logFile', self.local_logfile_path, self.key]
21+
for o in self.options.keys():
22+
if self.options.get(o) is not None:
23+
cmd = cmd + self.__xstr(o, self.options.get(o))
24+
return cmd
2425

2526
def start(self, **kwargs):
26-
for key, value in kwargs.items():
27-
self.__add_arg(key, value)
28-
27+
self.options = kwargs
28+
2929
if 'binarypath' in self.options:
3030
self.binary_path = binary_path
3131
else:
3232
self.binary_path = LocalBinary().get_binary()
3333

34+
if 'logfile' in self.options:
35+
self.local_logfile_path = self.options['logfile']
36+
del self.options['logfile']
37+
3438
if "onlyCommand" in kwargs and kwargs["onlyCommand"]:
3539
return
3640

@@ -56,43 +60,6 @@ def isRunning(self):
5660
return True if self.proc.poll() is None else False
5761
return False
5862

59-
def __add_arg(self, key, value):
60-
if key == 'verbose' and value:
61-
self.options['verbose'] = '-v'
62-
elif key == 'force' and value:
63-
self.options['force'] = '-force'
64-
elif key == 'only' and value:
65-
self.options['only'] = '-only'
66-
elif key == 'onlyAutomate' and value:
67-
self.options['only_automate'] = '-onlyAutomate'
68-
elif key == 'forcelocal' and value:
69-
self.options['forcelocal'] = '-forcelocal'
70-
elif key == 'localIdentifier':
71-
self.options['local_identifier'] = '-localIdentifier ' + str(value)
72-
elif key == 'f':
73-
self.options['folder_flag'] = '-f'
74-
self.options['folder_path'] = str(value)
75-
elif key == 'proxyHost':
76-
self.options['proxy_host'] = '-proxyHost ' + str(value)
77-
elif key == 'proxyPort':
78-
self.options['proxy_port'] = '-proxyPort ' + str(value)
79-
elif key == 'proxyUser':
80-
self.options['proxy_user'] = '-proxyUser ' + str(value)
81-
elif key == 'proxyPass':
82-
self.options['proxy_pass'] = '-proxyPass ' + str(value)
83-
elif key == 'hosts':
84-
self.options['hosts'] = str(value)
85-
elif key == 'forceproxy' and value:
86-
self.options['forceproxy'] = '-forceproxy'
87-
elif key == 'logfile':
88-
self.options['logfile_flag'] = '-logFile'
89-
self.options['logfile'] = str(value)
90-
self.local_logfile_path = str(value)
91-
elif key == 'binarypath':
92-
self.options['binarypath'] = str(value)
93-
elif key != 'onlyCommand':
94-
raise BrowserStackLocalError('Attempted to pass invalid option to binary')
95-
9663
def stop(self):
9764
try:
9865
self.proc.terminate()

tests/test_local.py

+23-9
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def test_start_local(self):
1313
self.assertNotEqual(self.local.proc.pid, 0)
1414

1515
def test_verbose(self):
16-
self.local.start(verbose=True, onlyCommand=True)
16+
self.local.start(v=True, onlyCommand=True)
1717
self.assertIn('-v', self.local._generate_cmd())
1818

1919
def test_local_folder(self):
@@ -33,23 +33,37 @@ def test_force_local(self):
3333
self.local.start(forcelocal=True, onlyCommand=True)
3434
self.assertIn('-forcelocal', self.local._generate_cmd())
3535

36+
def test_custom_boolean_argument(self):
37+
self.local.start(boolArg1=True, boolArg2=True, onlyCommand=True)
38+
self.assertIn('-boolArg1', self.local._generate_cmd())
39+
self.assertIn('-boolArg2', self.local._generate_cmd())
40+
41+
def test_custom_keyval(self):
42+
self.local.start(customKey1="custom value1", customKey2="custom value2", onlyCommand=True)
43+
self.assertIn('-customKey1', self.local._generate_cmd())
44+
self.assertIn('custom value1', self.local._generate_cmd())
45+
self.assertIn('-customKey2', self.local._generate_cmd())
46+
self.assertIn('custom value2', self.local._generate_cmd())
47+
3648
def test_proxy(self):
3749
self.local.start(proxyHost='localhost', proxyPort=2000, proxyUser='hello', proxyPass='test123', onlyCommand=True)
38-
self.assertIn('-proxyHost localhost', self.local._generate_cmd())
39-
self.assertIn('-proxyPort 2000', self.local._generate_cmd())
40-
self.assertIn('-proxyUser hello', self.local._generate_cmd())
41-
self.assertIn('-proxyPass test123', self.local._generate_cmd())
50+
self.assertIn('-proxyHost', self.local._generate_cmd())
51+
self.assertIn('localhost', self.local._generate_cmd())
52+
self.assertIn('-proxyPort', self.local._generate_cmd())
53+
self.assertIn(2000, self.local._generate_cmd())
54+
self.assertIn('-proxyUser', self.local._generate_cmd())
55+
self.assertIn('hello', self.local._generate_cmd())
56+
self.assertIn('-proxyPass', self.local._generate_cmd())
57+
self.assertIn('test123', self.local._generate_cmd())
4258

4359
def test_force_proxy(self):
4460
self.local.start(forceproxy=True, onlyCommand=True)
4561
self.assertIn('-forceproxy', self.local._generate_cmd())
4662

4763
def test_local_identifier(self):
4864
self.local.start(localIdentifier='mytunnel', onlyCommand=True)
49-
self.assertIn('-localIdentifier mytunnel', self.local._generate_cmd())
50-
51-
def test_invalid_option(self):
52-
self.assertRaises(BrowserStackLocalError, lambda: self.local.start(random=True))
65+
self.assertIn('-localIdentifier', self.local._generate_cmd())
66+
self.assertIn('mytunnel', self.local._generate_cmd())
5367

5468
def test_running(self):
5569
self.assertFalse(self.local.isRunning())

0 commit comments

Comments
 (0)