|
1 | 1 | """Tests for Robot Framework library integration.""" |
| 2 | +import importlib |
| 3 | +import sys |
2 | 4 | import unittest |
3 | 5 | from unittest.mock import MagicMock, patch |
4 | 6 |
|
|
7 | 9 | _parse_bool, |
8 | 10 | _parse_csv, |
9 | 11 | _parse_json, |
| 12 | + _parse_padding, |
10 | 13 | _parse_widths, |
11 | 14 | ) |
12 | 15 |
|
@@ -65,6 +68,44 @@ def test_parse_json_dict(self): |
65 | 68 | def test_parse_json_none(self): |
66 | 69 | self.assertIsNone(_parse_json(None)) |
67 | 70 |
|
| 71 | + def test_parse_widths_unsupported_type(self): |
| 72 | + self.assertIsNone(_parse_widths(123)) |
| 73 | + |
| 74 | + def test_parse_csv_list(self): |
| 75 | + self.assertEqual(_parse_csv(["a", "b"]), ["a", "b"]) |
| 76 | + |
| 77 | + def test_parse_csv_unsupported_type(self): |
| 78 | + self.assertIsNone(_parse_csv(123)) |
| 79 | + |
| 80 | + def test_parse_json_unsupported_type(self): |
| 81 | + self.assertIsNone(_parse_json(123)) |
| 82 | + |
| 83 | + |
| 84 | +class TestParsePadding(unittest.TestCase): |
| 85 | + def test_parse_padding_none(self): |
| 86 | + self.assertIsNone(_parse_padding(None)) |
| 87 | + |
| 88 | + def test_parse_padding_json_object_string(self): |
| 89 | + self.assertEqual( |
| 90 | + _parse_padding('{"top": 1, "bottom": 2, "left": 3, "right": 4}'), |
| 91 | + {"top": 1, "bottom": 2, "left": 3, "right": 4}, |
| 92 | + ) |
| 93 | + |
| 94 | + def test_parse_padding_numeric_string(self): |
| 95 | + self.assertEqual(_parse_padding("10"), {"top": 10, "bottom": 10, "left": 10, "right": 10}) |
| 96 | + |
| 97 | + def test_parse_padding_invalid_string(self): |
| 98 | + self.assertIsNone(_parse_padding("not-json-not-int")) |
| 99 | + |
| 100 | + def test_parse_padding_int(self): |
| 101 | + self.assertEqual(_parse_padding(5), {"top": 5, "bottom": 5, "left": 5, "right": 5}) |
| 102 | + |
| 103 | + def test_parse_padding_dict(self): |
| 104 | + self.assertEqual(_parse_padding({"top": 1}), {"top": 1}) |
| 105 | + |
| 106 | + def test_parse_padding_unsupported_type(self): |
| 107 | + self.assertIsNone(_parse_padding(["unexpected"])) |
| 108 | + |
68 | 109 |
|
69 | 110 | class TestPercyLibraryKeywords(unittest.TestCase): |
70 | 111 | @patch("percy.robot_library.percy_snapshot") |
@@ -122,3 +163,62 @@ def test_create_percy_region_keyword(self, mock_create): |
122 | 163 |
|
123 | 164 | mock_create.assert_called_once() |
124 | 165 | self.assertEqual(result["algorithm"], "ignore") |
| 166 | + |
| 167 | + @patch("percy.robot_library.BuiltIn") |
| 168 | + def test_get_driver_requires_selenium_library(self, mock_builtin): |
| 169 | + mock_builtin.return_value.get_library_instance.side_effect = RuntimeError("not imported") |
| 170 | + lib = PercyLibrary() |
| 171 | + with self.assertRaises(RuntimeError) as cm: |
| 172 | + lib._get_driver() # pylint: disable=protected-access |
| 173 | + self.assertIn("SeleniumLibrary", str(cm.exception)) |
| 174 | + |
| 175 | + @patch("percy.robot_library.percy_automate_screenshot") |
| 176 | + @patch("percy.robot_library.BuiltIn") |
| 177 | + def test_percy_screenshot_keyword_basic(self, mock_builtin, mock_screenshot): |
| 178 | + mock_driver = MagicMock() |
| 179 | + mock_builtin.return_value.get_library_instance.return_value.driver = mock_driver |
| 180 | + lib = PercyLibrary() |
| 181 | + lib.percy_screenshot_keyword("Homepage") |
| 182 | + mock_screenshot.assert_called_once() |
| 183 | + args, kwargs = mock_screenshot.call_args |
| 184 | + self.assertIs(args[0], mock_driver) |
| 185 | + self.assertEqual(args[1], "Homepage") |
| 186 | + self.assertEqual(kwargs["options"], {}) |
| 187 | + |
| 188 | + @patch("percy.robot_library.percy_automate_screenshot") |
| 189 | + @patch("percy.robot_library.BuiltIn") |
| 190 | + def test_percy_screenshot_keyword_with_region_elements(self, mock_builtin, mock_screenshot): |
| 191 | + mock_driver = MagicMock() |
| 192 | + selib = mock_builtin.return_value.get_library_instance.return_value |
| 193 | + selib.driver = mock_driver |
| 194 | + selib.find_element.side_effect = lambda loc: f"el:{loc}" |
| 195 | + lib = PercyLibrary() |
| 196 | + lib.percy_screenshot_keyword( |
| 197 | + "Page", |
| 198 | + ignore_region_selenium_elements="id:banner, css:.ad", |
| 199 | + consider_region_selenium_elements="id:main", |
| 200 | + ) |
| 201 | + options = mock_screenshot.call_args[1]["options"] |
| 202 | + self.assertEqual(options["ignore_region_selenium_elements"], ["el:id:banner", "el:css:.ad"]) |
| 203 | + self.assertEqual(options["consider_region_selenium_elements"], ["el:id:main"]) |
| 204 | + |
| 205 | + |
| 206 | +class TestRobotNotInstalled(unittest.TestCase): |
| 207 | + """When robotframework is absent, PercyLibrary degrades to a stub that |
| 208 | + raises a clear, actionable error on use.""" |
| 209 | + |
| 210 | + def test_stub_library_raises_without_robotframework(self): |
| 211 | + from percy import robot_library as rl # pylint: disable=import-outside-toplevel |
| 212 | + blocked = {name: None for name in ( |
| 213 | + 'robot', 'robot.api', 'robot.api.deco', |
| 214 | + 'robot.libraries', 'robot.libraries.BuiltIn', 'robot.version', |
| 215 | + )} |
| 216 | + with patch.dict(sys.modules, blocked): |
| 217 | + importlib.reload(rl) |
| 218 | + try: |
| 219 | + self.assertFalse(rl.ROBOT_AVAILABLE) |
| 220 | + with self.assertRaises(ImportError) as cm: |
| 221 | + rl.PercyLibrary() |
| 222 | + self.assertIn("robotframework is not installed", str(cm.exception)) |
| 223 | + finally: |
| 224 | + importlib.reload(rl) |
0 commit comments