percyOption fix (#24)
Version 0.0.8 (#25)
@@ -1,15 +1,15 @@
|
|
1 |
class PercyOptions:
|
2 |
IGNORE_ERRORS = 'ignoreErrors'
|
3 |
ENABLED = 'enabled'
|
4 |
-
PERCY_OPTIONS = ['percy:options']
|
5 |
|
6 |
def __init__(self, capabilities):
|
7 |
self._capabilities = capabilities
|
8 |
self.percy_options = self._parse_percy_options() or {}
|
9 |
|
10 |
def _parse_percy_options(self):
|
11 |
options = list(map(self._capabilities.get, self.PERCY_OPTIONS))
|
12 |
-
options = (options[0]
|
13 |
if options: return options
|
14 |
if options is not None and self.IGNORE_ERRORS not in options:
|
15 |
options[self.IGNORE_ERRORS] = self._capabilities.get(f'percy.{self.IGNORE_ERRORS}', True)
|
1 |
class PercyOptions:
|
2 |
IGNORE_ERRORS = 'ignoreErrors'
|
3 |
ENABLED = 'enabled'
|
4 |
+
PERCY_OPTIONS = ['percy:options', 'percyOptions']
|
5 |
|
6 |
def __init__(self, capabilities):
|
7 |
self._capabilities = capabilities
|
8 |
self.percy_options = self._parse_percy_options() or {}
|
9 |
|
10 |
def _parse_percy_options(self):
|
11 |
options = list(map(self._capabilities.get, self.PERCY_OPTIONS))
|
12 |
+
options = (options[0] or options[1]) if any(options) else {}
|
13 |
if options: return options
|
14 |
if options is not None and self.IGNORE_ERRORS not in options:
|
15 |
options[self.IGNORE_ERRORS] = self._capabilities.get(f'percy.{self.IGNORE_ERRORS}', True)
|
@@ -1 +1 @@
|
|
1 |
-
__version__ = '0.0.
|
1 |
+
__version__ = '0.0.8'
|
@@ -12,6 +12,10 @@
|
|
12 |
"enabled": true,
|
13 |
"ignoreErrors": false
|
14 |
},
|
|
|
|
|
|
|
|
|
15 |
"platformName": "Android",
|
16 |
"bstack:options": {
|
17 |
"appiumVersion": "1.17.0"
|
@@ -42,6 +46,10 @@
|
|
42 |
"enabled": true,
|
43 |
"ignoreErrors": true
|
44 |
},
|
|
|
|
|
|
|
|
|
45 |
"platformName": "Android",
|
46 |
"bstack:options": {
|
47 |
"appiumVersion": "1.17.0"
|
12 |
"enabled": true,
|
13 |
"ignoreErrors": false
|
14 |
},
|
15 |
+
"percyOptions": {
|
16 |
+
"enabled": true,
|
17 |
+
"ignoreErrors": false
|
18 |
+
},
|
19 |
"platformName": "Android",
|
20 |
"bstack:options": {
|
21 |
"appiumVersion": "1.17.0"
|
46 |
"enabled": true,
|
47 |
"ignoreErrors": true
|
48 |
},
|
49 |
+
"percyOptions": {
|
50 |
+
"enabled": true,
|
51 |
+
"ignoreErrors": true
|
52 |
+
},
|
53 |
"platformName": "Android",
|
54 |
"bstack:options": {
|
55 |
"appiumVersion": "1.17.0"
|
@@ -11,6 +11,10 @@
|
|
11 |
"enabled": true,
|
12 |
"ignoreErrors": true
|
13 |
},
|
|
|
|
|
|
|
|
|
14 |
"platformName": "iOS",
|
15 |
"bstack:options": {
|
16 |
"appiumVersion": "1.21.0"
|
11 |
"enabled": true,
|
12 |
"ignoreErrors": true
|
13 |
},
|
14 |
+
"percyOptions": {
|
15 |
+
"enabled": true,
|
16 |
+
"ignoreErrors": false
|
17 |
+
},
|
18 |
"platformName": "iOS",
|
19 |
"bstack:options": {
|
20 |
"appiumVersion": "1.21.0"
|
@@ -35,6 +35,8 @@ def setUp(self, mock_webdriver):
|
|
35 |
def tearDown(self) -> None:
|
36 |
self.mock_android_webdriver.capabilities['desired']['percy:options'] = {'enabled': True}
|
37 |
self.mock_android_webdriver.capabilities['percy:options'] = {'enabled': True}
|
|
|
|
|
38 |
|
39 |
@patch.object(CLIWrapper, 'post_screenshots', MagicMock(return_value=comparison_response))
|
40 |
@patch.object(GenericProvider, '_write_screenshot', MagicMock(return_value='path-to-png-file'))
|
@@ -101,6 +103,13 @@ def test_screenshot_with_percy_options_disabled(self):
|
|
101 |
app_percy = AppPercy(self.mock_android_webdriver)
|
102 |
self.assertIsNone(app_percy.screenshot('screenshot 1'))
|
103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
@patch('percy.screenshot.log')
|
105 |
@patch.object(AppPercy, 'screenshot', MagicMock(side_effect = Exception('RealException')))
|
106 |
@patch.object(CLIWrapper, 'is_percy_enabled', MagicMock(return_value=True))
|
@@ -119,6 +128,16 @@ def test_percy_options_ignore_errors_not_raise(self, _mock_log):
|
|
119 |
percy_screenshot(self.mock_android_webdriver, 'screenshot')
|
120 |
_mock_log.assert_called_with(exception, on_debug=True)
|
121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
@patch.object(GenericProvider, 'supports', MagicMock(return_value=False))
|
123 |
def test_invalid_provider(self):
|
124 |
mock_command_executor = Mock()
|
35 |
def tearDown(self) -> None:
|
36 |
self.mock_android_webdriver.capabilities['desired']['percy:options'] = {'enabled': True}
|
37 |
self.mock_android_webdriver.capabilities['percy:options'] = {'enabled': True}
|
38 |
+
self.mock_android_webdriver.capabilities['desired']['percyOptions'] = {'enabled': True}
|
39 |
+
self.mock_android_webdriver.capabilities['percyOptions'] = {'enabled': True}
|
40 |
|
41 |
@patch.object(CLIWrapper, 'post_screenshots', MagicMock(return_value=comparison_response))
|
42 |
@patch.object(GenericProvider, '_write_screenshot', MagicMock(return_value='path-to-png-file'))
|
103 |
app_percy = AppPercy(self.mock_android_webdriver)
|
104 |
self.assertIsNone(app_percy.screenshot('screenshot 1'))
|
105 |
|
106 |
+
def test_screenshot_with_percyOptions_disabled(self):
|
107 |
+
self.mock_android_webdriver.capabilities['percyOptions'] = {'enabled': False}
|
108 |
+
self.mock_android_webdriver.capabilities['percy:options'] = None
|
109 |
+
|
110 |
+
app_percy = AppPercy(self.mock_android_webdriver)
|
111 |
+
self.assertIsNone(app_percy.screenshot('screenshot 1'))
|
112 |
+
|
113 |
@patch('percy.screenshot.log')
|
114 |
@patch.object(AppPercy, 'screenshot', MagicMock(side_effect = Exception('RealException')))
|
115 |
@patch.object(CLIWrapper, 'is_percy_enabled', MagicMock(return_value=True))
|
128 |
percy_screenshot(self.mock_android_webdriver, 'screenshot')
|
129 |
_mock_log.assert_called_with(exception, on_debug=True)
|
130 |
|
131 |
+
@patch('percy.screenshot.log')
|
132 |
+
@patch.object(CLIWrapper, 'is_percy_enabled', MagicMock(return_value=True))
|
133 |
+
def test_percyOptions_ignore_errors_not_raise(self, _mock_log):
|
134 |
+
with patch.object(AppPercy, 'screenshot') as mock_screenshot:
|
135 |
+
exception = Exception('Some Exception')
|
136 |
+
mock_screenshot.side_effect = exception
|
137 |
+
self.mock_android_webdriver.capabilities['percyOptions'] = {'ignoreErrors': True}
|
138 |
+
percy_screenshot(self.mock_android_webdriver, 'screenshot')
|
139 |
+
_mock_log.assert_called_with(exception, on_debug=True)
|
140 |
+
|
141 |
@patch.object(GenericProvider, 'supports', MagicMock(return_value=False))
|
142 |
def test_invalid_provider(self):
|
143 |
mock_command_executor = Mock()
|
@@ -1,7 +1,7 @@
|
|
1 |
import unittest
|
2 |
from percy.lib.percy_options import PercyOptions
|
3 |
|
4 |
-
|
5 |
class TestPercyOptions(unittest.TestCase):
|
6 |
def test_percy_options_not_provided(self): # Defaults
|
7 |
capabilities = {}
|
@@ -15,6 +15,12 @@ def test_percy_options_w3c_enabled(self):
|
|
15 |
self.assertTrue(percy_options.enabled)
|
16 |
self.assertTrue(percy_options.ignore_errors)
|
17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
def test_percy_options_json_wire_enabled(self):
|
19 |
capabilities = {'percy.enabled': True}
|
20 |
percy_options = PercyOptions(capabilities)
|
@@ -27,6 +33,12 @@ def test_percy_options_w3c_not_enabled(self):
|
|
27 |
self.assertFalse(percy_options.enabled)
|
28 |
self.assertTrue(percy_options.ignore_errors)
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
def test_percy_options_json_wire_not_enabled(self):
|
31 |
capabilities = {'percy.enabled': False}
|
32 |
percy_options = PercyOptions(capabilities)
|
@@ -39,6 +51,12 @@ def test_percy_options_w3c_ignore_errors(self):
|
|
39 |
self.assertTrue(percy_options.ignore_errors)
|
40 |
self.assertTrue(percy_options.enabled)
|
41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
def test_percy_options_json_wire_ignore_errors(self):
|
43 |
capabilities = {'percy.ignoreErrors': True}
|
44 |
percy_options = PercyOptions(capabilities)
|
@@ -51,6 +69,12 @@ def test_percy_options_w3c_not_ignore_errors(self):
|
|
51 |
self.assertFalse(percy_options.ignore_errors)
|
52 |
self.assertTrue(percy_options.enabled)
|
53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
def test_percy_options_json_wire_not_ignore_errors(self):
|
55 |
capabilities = {'percy.ignoreErrors': False}
|
56 |
percy_options = PercyOptions(capabilities)
|
@@ -63,6 +87,12 @@ def test_percy_options_w3c_all_options_false(self):
|
|
63 |
self.assertFalse(percy_options.ignore_errors)
|
64 |
self.assertFalse(percy_options.enabled)
|
65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
def test_percy_options_json_wire_all_options_false(self):
|
67 |
capabilities = {'percy.ignoreErrors': False, 'percy.enabled': False}
|
68 |
percy_options = PercyOptions(capabilities)
|
@@ -75,6 +105,12 @@ def test_percy_options_w3c_all_options_true(self):
|
|
75 |
self.assertTrue(percy_options.ignore_errors)
|
76 |
self.assertTrue(percy_options.enabled)
|
77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
def test_percy_options_json_wire_all_options_true(self):
|
79 |
capabilities = {'percy.ignoreErrors': True, 'percy.enabled': True}
|
80 |
percy_options = PercyOptions(capabilities)
|
@@ -92,3 +128,9 @@ def test_percy_options_json_wire_and_w3c_case_2(self):
|
|
92 |
percy_options = PercyOptions(capabilities)
|
93 |
self.assertFalse(percy_options.ignore_errors)
|
94 |
self.assertTrue(percy_options.enabled)
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import unittest
|
2 |
from percy.lib.percy_options import PercyOptions
|
3 |
|
4 |
+
# pylint: disable=R0904
|
5 |
class TestPercyOptions(unittest.TestCase):
|
6 |
def test_percy_options_not_provided(self): # Defaults
|
7 |
capabilities = {}
|
15 |
self.assertTrue(percy_options.enabled)
|
16 |
self.assertTrue(percy_options.ignore_errors)
|
17 |
|
18 |
+
def test_percyOptions_w3c_enabled(self):
|
19 |
+
capabilities = {'percyOptions': {'enabled': True}}
|
20 |
+
percy_options = PercyOptions(capabilities)
|
21 |
+
self.assertTrue(percy_options.enabled)
|
22 |
+
self.assertTrue(percy_options.ignore_errors)
|
23 |
+
|
24 |
def test_percy_options_json_wire_enabled(self):
|
25 |
capabilities = {'percy.enabled': True}
|
26 |
percy_options = PercyOptions(capabilities)
|
33 |
self.assertFalse(percy_options.enabled)
|
34 |
self.assertTrue(percy_options.ignore_errors)
|
35 |
|
36 |
+
def test_percyOptions_w3c_not_enabled(self):
|
37 |
+
capabilities = {'percyOptions': {'enabled': False}}
|
38 |
+
percy_options = PercyOptions(capabilities)
|
39 |
+
self.assertFalse(percy_options.enabled)
|
40 |
+
self.assertTrue(percy_options.ignore_errors)
|
41 |
+
|
42 |
def test_percy_options_json_wire_not_enabled(self):
|
43 |
capabilities = {'percy.enabled': False}
|
44 |
percy_options = PercyOptions(capabilities)
|
51 |
self.assertTrue(percy_options.ignore_errors)
|
52 |
self.assertTrue(percy_options.enabled)
|
53 |
|
54 |
+
def test_percyOptions_w3c_ignore_errors(self):
|
55 |
+
capabilities = {'percyOptions': {'ignoreErrors': True}}
|
56 |
+
percy_options = PercyOptions(capabilities)
|
57 |
+
self.assertTrue(percy_options.ignore_errors)
|
58 |
+
self.assertTrue(percy_options.enabled)
|
59 |
+
|
60 |
def test_percy_options_json_wire_ignore_errors(self):
|
61 |
capabilities = {'percy.ignoreErrors': True}
|
62 |
percy_options = PercyOptions(capabilities)
|
69 |
self.assertFalse(percy_options.ignore_errors)
|
70 |
self.assertTrue(percy_options.enabled)
|
71 |
|
72 |
+
def test_percyOptions_w3c_not_ignore_errors(self):
|
73 |
+
capabilities = {'percyOptions': {'ignoreErrors': False}}
|
74 |
+
percy_options = PercyOptions(capabilities)
|
75 |
+
self.assertFalse(percy_options.ignore_errors)
|
76 |
+
self.assertTrue(percy_options.enabled)
|
77 |
+
|
78 |
def test_percy_options_json_wire_not_ignore_errors(self):
|
79 |
capabilities = {'percy.ignoreErrors': False}
|
80 |
percy_options = PercyOptions(capabilities)
|
87 |
self.assertFalse(percy_options.ignore_errors)
|
88 |
self.assertFalse(percy_options.enabled)
|
89 |
|
90 |
+
def test_percyOptions_w3c_all_options_false(self):
|
91 |
+
capabilities = {'percyOptions': {'ignoreErrors': False, 'enabled': False}}
|
92 |
+
percy_options = PercyOptions(capabilities)
|
93 |
+
self.assertFalse(percy_options.ignore_errors)
|
94 |
+
self.assertFalse(percy_options.enabled)
|
95 |
+
|
96 |
def test_percy_options_json_wire_all_options_false(self):
|
97 |
capabilities = {'percy.ignoreErrors': False, 'percy.enabled': False}
|
98 |
percy_options = PercyOptions(capabilities)
|
105 |
self.assertTrue(percy_options.ignore_errors)
|
106 |
self.assertTrue(percy_options.enabled)
|
107 |
|
108 |
+
def test_percyOptions_w3c_all_options_true(self):
|
109 |
+
capabilities = {'percyOptions': {'ignoreErrors': True, 'enabled': True}}
|
110 |
+
percy_options = PercyOptions(capabilities)
|
111 |
+
self.assertTrue(percy_options.ignore_errors)
|
112 |
+
self.assertTrue(percy_options.enabled)
|
113 |
+
|
114 |
def test_percy_options_json_wire_all_options_true(self):
|
115 |
capabilities = {'percy.ignoreErrors': True, 'percy.enabled': True}
|
116 |
percy_options = PercyOptions(capabilities)
|
128 |
percy_options = PercyOptions(capabilities)
|
129 |
self.assertFalse(percy_options.ignore_errors)
|
130 |
self.assertTrue(percy_options.enabled)
|
131 |
+
|
132 |
+
def test_percyOptions_json_wire_and_w3c_case_2(self):
|
133 |
+
capabilities = {'percy.enabled': False, 'percyOptions': {'ignoreErrors': False}}
|
134 |
+
percy_options = PercyOptions(capabilities)
|
135 |
+
self.assertFalse(percy_options.ignore_errors)
|
136 |
+
self.assertTrue(percy_options.enabled)
|
Percy visual testing for Python Appium.
npm install @percy/cli
:
$ npm install --save-dev @percy/cli
pip install Percy appium package:
$ pip install percy-appium-app
This is an example test using the percy_screenshot
function.
from appium import webdriver
from percy import percy_screenshot
driver = webdriver.Remote("https://" + userName + ":" + accessKey + "@hub-cloud.browserstack.com/wd/hub", desired_caps)
# take a screenshot
percy_screenshot(driver, 'here is some name')
Running the test above normally will result in the following log:
[percy] Percy is not running, disabling screenshots
When running with percy
app:exec
, and your project's
PERCY_TOKEN
, a new Percy build will be created and screenshots will be uploaded to your project.
$ export PERCY_TOKEN=[your-project-token]
$ percy app:exec -- [python test command]
[percy] Percy has started!
[percy] Created build #1: https://percy.io/[your-project]
[percy] Screenshot taken "Python example"
[percy] Stopping percy...
[percy] Finalized build #1: https://percy.io/[your-project]
[percy] Done!
percy_screenshot(driver, name[, **kwargs])
driver
(required) - A appium driver instancename
(required) - The screenshot name; must be unique to each screenshotdevice_name
(optional) - The device name used for capturing screenshotorientation
(optional) - Orientation of device while capturing screeenshot; Allowed values [portrait
| landscape
]status_bar_height
(optional) - Height of status bar; numbernav_bar_height
(optional) - Height of navigation bar; numberfull_screen
(optional) - Indicate whether app is full screen; booleanIf you have a previous Percy configuration file, migrate it to the newest version with the
config:migrate
command:
$ percy config:migrate