diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5be3680..a180105 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,7 +8,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: 3.x + python-version: 3.11 - uses: actions/cache@v3 with: path: ~/.cache/pip diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7a3a06e..fc51017 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: restore-keys: v1/${{ runner.os }}/pypi-${matrix.python}/ - uses: actions/setup-node@v3 with: - node-version: 14 + node-version: 16 - name: Get yarn cache directory path id: yarn-cache-dir-path run: echo "::set-output name=dir::$(yarn cache dir)" diff --git a/percy/__init__.py b/percy/__init__.py index 4f791c3..91647a3 100644 --- a/percy/__init__.py +++ b/percy/__init__.py @@ -20,11 +20,12 @@ def percy_screenshot(driver, name, **kw): raise UnsupportedWebDriverException("Provided driver is not supported") if "RemoteConnection" in driver.command_executor.__class__.__name__: - percy_automate_screenshot(driver, name, **kw) - elif "AppiumConnection" in driver.command_executor.__class__.__name__: + return percy_automate_screenshot(driver, name, **kw) + if "AppiumConnection" in driver.command_executor.__class__.__name__: try: from percy.screenshot import percy_screenshot # pylint: disable=W0621,C0415 - percy_screenshot(driver, name, **kw) + return percy_screenshot(driver, name, **kw) except ImportError as exc: raise ModuleNotFoundError("[percy] `percy-appium` package is not installed, "\ "please install it to use percy_screenshot command with appium") from exc + return None diff --git a/percy/snapshot.py b/percy/snapshot.py index 8d3626d..8f2c4ab 100644 --- a/percy/snapshot.py +++ b/percy/snapshot.py @@ -58,7 +58,7 @@ def fetch_percy_dom(): # Take a DOM snapshot and post it to the snapshot endpoint def percy_snapshot(driver, name, **kwargs): session_type = is_percy_enabled() - if session_type is False: return # Since session_type can be None for old CLI version + if session_type is False: return None # Since session_type can be None for old CLI version if session_type == "automate": raise Exception("Invalid function call - "\ "percy_snapshot(). Please use percy_screenshot() function while using Percy with Automate. "\ "For more information on usage of PercyScreenshot, "\ @@ -79,21 +79,24 @@ def percy_snapshot(driver, name, **kwargs): 'dom_snapshot': dom_snapshot, 'url': driver.current_url, 'name': name - }}, timeout=30) + }}, timeout=600) # Handle errors response.raise_for_status() data = response.json() if not data['success']: raise Exception(data['error']) + if not data["data"]: return None + return data["data"] except Exception as e: print(f'{LABEL} Could not take DOM snapshot "{name}"') print(f'{LABEL} {e}') + return None # Take screenshot on driver def percy_automate_screenshot(driver, name, options = None, **kwargs): session_type = is_percy_enabled() - if session_type is False: return # Since session_type can be None for old CLI version + if session_type is False: return None # Since session_type can be None for old CLI version if session_type == "web": raise Exception("Invalid function call - "\ "percy_screenshot(). Please use percy_snapshot() function for taking screenshot. "\ "percy_screenshot() should be used only while using Percy with Automate. "\ @@ -134,16 +137,19 @@ def percy_automate_screenshot(driver, name, options = None, **kwargs): 'sessionCapabilites': metadata.session_capabilities, 'snapshotName': name, 'options': options - }}, timeout=60) + }}, timeout=600) # Handle errors response.raise_for_status() data = response.json() if not data['success']: raise Exception(data['error']) + if not data['data']: return None + return data['data'] except Exception as e: print(f'{LABEL} Could not take Screenshot "{name}"') print(f'{LABEL} {e}') + return None def get_element_ids(elements): return [element.id for element in elements] diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index cd09581..4a5d7e9 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -43,6 +43,10 @@ def dummy_method1(self): mock_server_thread.daemon = True mock_server_thread.start() +# initializing mock data +data_object = {"sync": "true", "diff": 0} + + # mock helpers def mock_healthcheck(fail=False, fail_how='error', session_type=None): health_body = { "success": True } @@ -71,16 +75,25 @@ def mock_healthcheck(fail=False, fail_how='error', session_type=None): body='window.PercyDOM = { serialize: () => document.documentElement.outerHTML };', status=200) -def mock_snapshot(fail=False): +def mock_snapshot(fail=False, data=False): httpretty.register_uri( httpretty.POST, 'http://localhost:5338/percy/snapshot', - body=('{ "success": ' + ('true' if not fail else 'false, "error": "test"') + '}'), + body = json.dumps({ + "success": "false" if fail else "true", + "error": "test" if fail else None, + "data": data_object if data else None + }), status=(500 if fail else 200)) -def mock_screenshot(fail=False): +def mock_screenshot(fail=False, data=False): + httpretty.register_uri( httpretty.POST, 'http://localhost:5338/percy/automateScreenshot', - body=('{ "success": ' + ('true' if not fail else 'false, "error": "test"') + '}'), + body = json.dumps({ + "success": not fail, + "error": "test" if fail else None, + "data": data_object if data else None + }), status=(500 if fail else 200)) class TestPercySnapshot(unittest.TestCase): @@ -154,7 +167,7 @@ def test_posts_snapshots_to_the_local_percy_server(self): mock_snapshot() percy_snapshot(self.driver, 'Snapshot 1') - percy_snapshot(self.driver, 'Snapshot 2', enable_javascript=True) + response = percy_snapshot(self.driver, 'Snapshot 2', enable_javascript=True) self.assertEqual(httpretty.last_request().path, '/percy/snapshot') @@ -169,6 +182,30 @@ def test_posts_snapshots_to_the_local_percy_server(self): s2 = httpretty.latest_requests()[3].parsed_body self.assertEqual(s2['name'], 'Snapshot 2') self.assertEqual(s2['enable_javascript'], True) + self.assertEqual(response, None) + + def test_posts_snapshots_to_the_local_percy_server_for_sync(self): + mock_healthcheck() + mock_snapshot(False, True) + + percy_snapshot(self.driver, 'Snapshot 1') + response = percy_snapshot(self.driver, 'Snapshot 2', enable_javascript=True, sync=True) + + self.assertEqual(httpretty.last_request().path, '/percy/snapshot') + + s1 = httpretty.latest_requests()[2].parsed_body + self.assertEqual(s1['name'], 'Snapshot 1') + self.assertEqual(s1['url'], 'http://localhost:8000/') + self.assertEqual(s1['dom_snapshot'], 'Snapshot Me') + self.assertRegex(s1['client_info'], r'percy-selenium-python/\d+') + self.assertRegex(s1['environment_info'][0], r'selenium/\d+') + self.assertRegex(s1['environment_info'][1], r'python/\d+') + + s2 = httpretty.latest_requests()[3].parsed_body + self.assertEqual(s2['name'], 'Snapshot 2') + self.assertEqual(s2['enable_javascript'], True) + self.assertEqual(s2['sync'], True) + self.assertEqual(response, data_object) def test_has_a_backwards_compatible_function(self): mock_healthcheck() @@ -283,22 +320,25 @@ def test_disables_screenshot_when_the_driver_is_not_selenium(self): def test_camelcase_options(self): mock_healthcheck() - mock_screenshot() + mock_screenshot(False, True) element = Mock(spec=WebElement) element.id = 'Dummy_id' consider_element = Mock(spec=WebElement) consider_element.id = 'Consider_Dummy_id' - percy_screenshot(self.driver, 'Snapshot C', options = { + response = percy_screenshot(self.driver, 'Snapshot C', options = { "ignoreRegionSeleniumElements": [element], - "considerRegionSeleniumElements": [consider_element] + "considerRegionSeleniumElements": [consider_element], + "sync": "true" }) s = httpretty.latest_requests()[1].parsed_body self.assertEqual(s['snapshotName'], 'Snapshot C') self.assertEqual(s['options']['ignore_region_elements'], ['Dummy_id']) self.assertEqual(s['options']['consider_region_elements'], ['Consider_Dummy_id']) + self.assertEqual(s['options']['sync'], "true") + self.assertEqual(response, data_object) def posts_screenshot_to_the_local_percy_server(self, driver): mock_healthcheck() @@ -311,7 +351,7 @@ def posts_screenshot_to_the_local_percy_server(self, driver): consider_element.id = 'Consider_Dummy_id' percy_screenshot(driver, 'Snapshot 1') - percy_screenshot(driver, 'Snapshot 2', options = { + response = percy_screenshot(driver, 'Snapshot 2', options = { "enable_javascript": True, "ignore_region_selenium_elements": [element], "consider_region_selenium_elements": [consider_element] @@ -334,6 +374,7 @@ def posts_screenshot_to_the_local_percy_server(self, driver): self.assertEqual(s2['options']['enable_javascript'], True) self.assertEqual(s2['options']['ignore_region_elements'], ['Dummy_id']) self.assertEqual(s2['options']['consider_region_elements'], ['Consider_Dummy_id']) + self.assertEqual(response, None) def test_posts_screenshot_to_the_local_percy_server_remote_connection(self): self.posts_screenshot_to_the_local_percy_server(self.driver)