Skip to content

Commit 3fd9f38

Browse files
adding regions support for sdk (#156)
* added regions * verson fix * testing * testing * updated cli * updated readme * readme * updated ubantu * added for python * lint
1 parent 90aa7e6 commit 3fd9f38

File tree

5 files changed

+232
-3
lines changed

5 files changed

+232
-3
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ jobs:
1313
name: Test
1414
strategy:
1515
matrix:
16-
python: [3.6, 3.7, 3.8, 3.9]
17-
runs-on: ${{ matrix.python == 3.6 && 'ubuntu-20.04' || 'ubuntu-latest' }}
16+
python: ["3.8", "3.9", "3.10"]
17+
runs-on: ubuntu-latest
1818

1919
steps:
2020
- uses: actions-ecosystem/action-regex-match@v2

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,68 @@ percy_screenshot(driver, name = 'Screenshot 1')
116116
- `bottom` (int): Bottom coordinate of the consider region.
117117
- `left` (int): Left coordinate of the consider region.
118118
- `right` (int): Right coordinate of the consider region.
119+
- `regions` parameter that allows users to apply snapshot options to specific areas of the page. This parameter is an array where each object defines a custom region with configurations.
120+
- Parameters:
121+
- `elementSelector` (optional, only one of the following must be provided, if this is not provided then full page will be considered as region)
122+
- `boundingBox` (object): Defines the coordinates and size of the region.
123+
- `x` (number): X-coordinate of the region.
124+
- `y` (number): Y-coordinate of the region.
125+
- `width` (number): Width of the region.
126+
- `height` (number): Height of the region.
127+
- `elementXpath` (string): The XPath selector for the element.
128+
- `elementCSS` (string): The CSS selector for the element.
129+
130+
- `algorithm` (mandatory)
131+
- Specifies the snapshot comparison algorithm.
132+
- Allowed values: `standard`, `layout`, `ignore`, `intelliignore`.
133+
134+
- `configuration` (required for `standard` and `intelliignore` algorithms, ignored otherwise)
135+
- `diffSensitivity` (number): Sensitivity level for detecting differences.
136+
- `imageIgnoreThreshold` (number): Threshold for ignoring minor image differences.
137+
- `carouselsEnabled` (boolean): Whether to enable carousel detection.
138+
- `bannersEnabled` (boolean): Whether to enable banner detection.
139+
- `adsEnabled` (boolean): Whether to enable ad detection.
140+
141+
- `assertion` (optional)
142+
- Defines assertions to apply to the region.
143+
- `diffIgnoreThreshold` (number): The threshold for ignoring minor differences.
144+
145+
### Example Usage for regions
146+
147+
```
148+
obj1 = {
149+
"elementSelector": {
150+
"elementCSS": ".ad-banner"
151+
},
152+
"algorithm": "intelliignore",
153+
"configuration": {
154+
"diffSensitivity": 2,
155+
"imageIgnoreThreshold": 0.2,
156+
"carouselsEnabled": true,
157+
"bannersEnabled": true,
158+
"adsEnabled": true
159+
},
160+
"assertion": {
161+
"diffIgnoreThreshold": 0.4,
162+
}
163+
};
164+
165+
# we can use the createRegion function
166+
167+
from percy import percy_snapshot
168+
from percy.snapshot import (create_region)
169+
170+
obj2 = create_region(
171+
algorithm="intellignore",
172+
diffSensitivity=2,
173+
imageIgnoreThreshold=0.2,
174+
carouselsEnabled=True,
175+
adsEnabled=True,
176+
diffIgnoreThreshold=0.4
177+
)
178+
179+
percy_snapshot(page, name="Homepage", regions=[obj1]);
180+
```
119181

120182

121183
### Creating Percy on automate build

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"test": "make test"
55
},
66
"devDependencies": {
7-
"@percy/cli": "1.27.0-beta.0"
7+
"@percy/cli": "1.30.9"
88
}
99
}

percy/snapshot.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,58 @@ def fetch_percy_dom():
7979
response.raise_for_status()
8080
return response.text
8181

82+
# pylint: disable=too-many-arguments, too-many-branches, too-many-locals
83+
def create_region(
84+
boundingBox=None,
85+
elementXpath=None,
86+
elementCSS=None,
87+
padding=None,
88+
algorithm="ignore",
89+
diffSensitivity=None,
90+
imageIgnoreThreshold=None,
91+
carouselsEnabled=None,
92+
bannersEnabled=None,
93+
adsEnabled=None,
94+
diffIgnoreThreshold=None
95+
):
96+
97+
element_selector = {}
98+
if boundingBox:
99+
element_selector["boundingBox"] = boundingBox
100+
if elementXpath:
101+
element_selector["elementXpath"] = elementXpath
102+
if elementCSS:
103+
element_selector["elementCSS"] = elementCSS
104+
105+
region = {
106+
"algorithm": algorithm,
107+
"elementSelector": element_selector
108+
}
109+
110+
if padding:
111+
region["padding"] = padding
112+
113+
if algorithm in ["standard", "intelliignore"]:
114+
config_values = {
115+
"diffSensitivity": diffSensitivity,
116+
"imageIgnoreThreshold": imageIgnoreThreshold,
117+
"carouselsEnabled": carouselsEnabled,
118+
"bannersEnabled": bannersEnabled,
119+
"adsEnabled": adsEnabled,
120+
}
121+
configuration = {k: v for k, v in config_values.items() if v is not None}
122+
if configuration:
123+
region["configuration"] = configuration
124+
125+
assertion = {}
126+
if diffIgnoreThreshold is not None:
127+
assertion["diffIgnoreThreshold"] = diffIgnoreThreshold
128+
129+
if assertion:
130+
region["assertion"] = assertion
131+
132+
return region
133+
82134
def get_serialized_dom(driver, cookies, **kwargs):
83135
dom_snapshot = driver.execute_script(f'return PercyDOM.serialize({json.dumps(kwargs)})')
84136
dom_snapshot['cookies'] = cookies

tests/test_snapshot.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from selenium.webdriver.remote.webelement import WebElement
1313
from selenium.webdriver.remote.remote_connection import RemoteConnection
1414
from selenium.webdriver.safari.remote_connection import SafariRemoteConnection
15+
from percy.snapshot import create_region
1516

1617
from percy import percy_snapshot, percySnapshot, percy_screenshot
1718
import percy.snapshot as local
@@ -562,6 +563,120 @@ def test_posts_snapshots_to_the_local_percy_server(self):
562563
self.assertEqual(s2['name'], 'Snapshot 2')
563564
self.assertEqual(s2['enable_javascript'], True)
564565

566+
class TestCreateRegion(unittest.TestCase):
567+
568+
def test_create_region_with_all_params(self):
569+
result = create_region(
570+
boundingBox={"x": 10, "y": 20, "width": 100, "height": 200},
571+
elementXpath="//*[@id='test']",
572+
elementCSS=".test-class",
573+
padding=10,
574+
algorithm="intelliignore",
575+
diffSensitivity=0.8,
576+
imageIgnoreThreshold=0.5,
577+
carouselsEnabled=True,
578+
bannersEnabled=False,
579+
adsEnabled=True,
580+
diffIgnoreThreshold=0.2
581+
)
582+
583+
expected_result = {
584+
"algorithm": "intelliignore",
585+
"elementSelector": {
586+
"boundingBox": {"x": 10, "y": 20, "width": 100, "height": 200},
587+
"elementXpath": "//*[@id='test']",
588+
"elementCSS": ".test-class"
589+
},
590+
"padding": 10,
591+
"configuration": {
592+
"diffSensitivity": 0.8,
593+
"imageIgnoreThreshold": 0.5,
594+
"carouselsEnabled": True,
595+
"bannersEnabled": False,
596+
"adsEnabled": True
597+
},
598+
"assertion": {
599+
"diffIgnoreThreshold": 0.2
600+
}
601+
}
602+
603+
self.assertEqual(result, expected_result)
604+
605+
def test_create_region_with_minimal_params(self):
606+
result = create_region(
607+
algorithm="standard",
608+
boundingBox={"x": 10, "y": 20, "width": 100, "height": 200}
609+
)
610+
611+
expected_result = {
612+
"algorithm": "standard",
613+
"elementSelector": {
614+
"boundingBox": {"x": 10, "y": 20, "width": 100, "height": 200}
615+
}
616+
}
617+
618+
self.assertEqual(result, expected_result)
619+
620+
def test_create_region_with_padding(self):
621+
result = create_region(
622+
algorithm="ignore",
623+
padding=15
624+
)
625+
626+
expected_result = {
627+
"algorithm": "ignore",
628+
"elementSelector": {},
629+
"padding": 15
630+
}
631+
632+
self.assertEqual(result, expected_result)
633+
634+
def test_create_region_with_configuration_only_for_valid_algorithms(self):
635+
result = create_region(
636+
algorithm="intelliignore",
637+
diffSensitivity=0.9,
638+
imageIgnoreThreshold=0.7
639+
)
640+
641+
expected_result = {
642+
"algorithm": "intelliignore",
643+
"elementSelector": {},
644+
"configuration": {
645+
"diffSensitivity": 0.9,
646+
"imageIgnoreThreshold": 0.7
647+
}
648+
}
649+
650+
self.assertEqual(result, expected_result)
651+
652+
def test_create_region_with_diffIgnoreThreshold_in_assertion(self):
653+
result = create_region(
654+
algorithm="standard",
655+
diffIgnoreThreshold=0.3
656+
)
657+
658+
expected_result = {
659+
"algorithm": "standard",
660+
"elementSelector": {},
661+
"assertion": {
662+
"diffIgnoreThreshold": 0.3
663+
}
664+
}
665+
666+
self.assertEqual(result, expected_result)
667+
668+
def test_create_region_with_invalid_algorithm(self):
669+
result = create_region(
670+
algorithm="invalid_algorithm"
671+
)
672+
673+
expected_result = {
674+
"algorithm": "invalid_algorithm",
675+
"elementSelector": {}
676+
}
677+
678+
self.assertEqual(result, expected_result)
679+
565680

566681
if __name__ == '__main__':
567682
unittest.main()

0 commit comments

Comments
 (0)