setup-selenium-testing 0.2.0__tar.gz → 1.0.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,18 +1,16 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: setup-selenium-testing
3
- Version: 0.2.0
3
+ Version: 1.0.0
4
4
  Summary: Setup Selenium for automation testing
5
- Home-page: https://github.com/bandophahita/setup_selenium
6
5
  License: MIT
7
6
  Author: Marcel Wilson
8
7
  Author-email: trenchrats+pypi@gmail.com
9
- Requires-Python: >=3.8,<4.0
8
+ Requires-Python: >=3.9,<4.0
10
9
  Classifier: Intended Audience :: Developers
11
10
  Classifier: License :: OSI Approved :: MIT License
12
11
  Classifier: Natural Language :: English
13
12
  Classifier: Operating System :: OS Independent
14
13
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.8
16
14
  Classifier: Programming Language :: Python :: 3.9
17
15
  Classifier: Programming Language :: Python :: 3.10
18
16
  Classifier: Programming Language :: Python :: 3.11
@@ -52,7 +50,42 @@ in every project. Time to consolidate.
52
50
  [![Build Status](https://github.com/bandophahita/setup_selenium/actions/workflows/lint.yml/badge.svg)](https://github.com/bandophahita/setup_selenium/actions/workflows/lint.yml)
53
51
 
54
52
 
55
- # Basic usage
53
+ # Instantiating SetupSelenium
54
+
55
+ This will automatically handle any downloading of drivers or browsers via `SeleniumManager`
56
+
57
+ ```python
58
+ from setup_selenium import SetupSelenium
59
+
60
+ s = SetupSelenium(headless=True)
61
+ assert s.driver.service.is_connectable()
62
+ ```
63
+
64
+ Advanced usage:
65
+
66
+ ```python
67
+ from setup_selenium import Browser, SetupSelenium
68
+
69
+ s = SetupSelenium(Browser.FIREFOX, headless=True, driver_version="118.0.5993.70")
70
+ s = SetupSelenium(Browser.CHROME, headless=True, driver_version="118.0.5993.70",
71
+ driver_path="/path/to/webdriver"
72
+ )
73
+ ```
74
+
75
+ > [!NOTE]
76
+ > Version and path arguments follow the logic of
77
+ > [SeleniumManager](https://www.selenium.dev/documentation/selenium_manager/).
78
+ > Caution is advised in cases where version and path do not match.
79
+ > See their documentation.
80
+
81
+ # Install Driver only
82
+ ```python
83
+ from setup_selenium import Browser, SetupSelenium
84
+
85
+ driver_path, browser_path = SetupSelenium.install_driver(Browser.CHROME, driver_version="118.0.5993.70")
86
+ ```
87
+
88
+ # Create driver only
56
89
 
57
90
  ```python
58
91
  from setup_selenium import Browser, SetupSelenium
@@ -60,7 +93,8 @@ from setup_selenium import Browser, SetupSelenium
60
93
  driver = SetupSelenium.create_driver(browser=Browser.CHROME, headless=True)
61
94
  ```
62
95
 
63
- # Advanced usage
96
+ Advanced usage:
97
+
64
98
  ```python
65
99
  from setup_selenium import Browser, SetupSelenium
66
100
 
@@ -77,19 +111,11 @@ driver = SetupSelenium.create_driver(
77
111
  ```
78
112
 
79
113
  > [!NOTE]
80
- > It is possible to enable the performance and console logging
114
+ > It is possible to enable the performance and console logging
81
115
  > but only for chrome based browsers. This only enables the browser ability.
82
116
  > It is up to the tester to handle logging the messages.
83
117
 
84
118
 
85
-
86
- # Driver installation
87
- ```python
88
- from setup_selenium import Browser, SetupSelenium
89
-
90
- driver_path, browser_path = SetupSelenium.install_driver(Browser.CHROME, "118.0.5993.70")
91
- ```
92
-
93
119
  # Custom logger
94
120
  ```python
95
121
  import logging
@@ -99,18 +125,6 @@ set_logger(logging.getLogger("your_custom_logger"))
99
125
  driver = SetupSelenium.create_driver(browser=Browser.CHROME, headless=True)
100
126
  ```
101
127
 
102
- # Instantiating SetupSelenium
103
- While it is possible to use the class directly caution is advised; as the class
104
- will create the driver upon instantiation.
105
-
106
- ```python
107
- from setup_selenium import SetupSelenium
108
-
109
- s = SetupSelenium(headless=True)
110
- assert s.driver.service.is_connectable()
111
- ```
112
-
113
-
114
128
  # Automatic driver and browser installation
115
129
  This package not only handles setup of the webdriver but also will
116
130
  automatically install the webdriver and/or browser depending on your
@@ -18,7 +18,42 @@ in every project. Time to consolidate.
18
18
  [![Build Status](https://github.com/bandophahita/setup_selenium/actions/workflows/lint.yml/badge.svg)](https://github.com/bandophahita/setup_selenium/actions/workflows/lint.yml)
19
19
 
20
20
 
21
- # Basic usage
21
+ # Instantiating SetupSelenium
22
+
23
+ This will automatically handle any downloading of drivers or browsers via `SeleniumManager`
24
+
25
+ ```python
26
+ from setup_selenium import SetupSelenium
27
+
28
+ s = SetupSelenium(headless=True)
29
+ assert s.driver.service.is_connectable()
30
+ ```
31
+
32
+ Advanced usage:
33
+
34
+ ```python
35
+ from setup_selenium import Browser, SetupSelenium
36
+
37
+ s = SetupSelenium(Browser.FIREFOX, headless=True, driver_version="118.0.5993.70")
38
+ s = SetupSelenium(Browser.CHROME, headless=True, driver_version="118.0.5993.70",
39
+ driver_path="/path/to/webdriver"
40
+ )
41
+ ```
42
+
43
+ > [!NOTE]
44
+ > Version and path arguments follow the logic of
45
+ > [SeleniumManager](https://www.selenium.dev/documentation/selenium_manager/).
46
+ > Caution is advised in cases where version and path do not match.
47
+ > See their documentation.
48
+
49
+ # Install Driver only
50
+ ```python
51
+ from setup_selenium import Browser, SetupSelenium
52
+
53
+ driver_path, browser_path = SetupSelenium.install_driver(Browser.CHROME, driver_version="118.0.5993.70")
54
+ ```
55
+
56
+ # Create driver only
22
57
 
23
58
  ```python
24
59
  from setup_selenium import Browser, SetupSelenium
@@ -26,7 +61,8 @@ from setup_selenium import Browser, SetupSelenium
26
61
  driver = SetupSelenium.create_driver(browser=Browser.CHROME, headless=True)
27
62
  ```
28
63
 
29
- # Advanced usage
64
+ Advanced usage:
65
+
30
66
  ```python
31
67
  from setup_selenium import Browser, SetupSelenium
32
68
 
@@ -43,19 +79,11 @@ driver = SetupSelenium.create_driver(
43
79
  ```
44
80
 
45
81
  > [!NOTE]
46
- > It is possible to enable the performance and console logging
82
+ > It is possible to enable the performance and console logging
47
83
  > but only for chrome based browsers. This only enables the browser ability.
48
84
  > It is up to the tester to handle logging the messages.
49
85
 
50
86
 
51
-
52
- # Driver installation
53
- ```python
54
- from setup_selenium import Browser, SetupSelenium
55
-
56
- driver_path, browser_path = SetupSelenium.install_driver(Browser.CHROME, "118.0.5993.70")
57
- ```
58
-
59
87
  # Custom logger
60
88
  ```python
61
89
  import logging
@@ -65,18 +93,6 @@ set_logger(logging.getLogger("your_custom_logger"))
65
93
  driver = SetupSelenium.create_driver(browser=Browser.CHROME, headless=True)
66
94
  ```
67
95
 
68
- # Instantiating SetupSelenium
69
- While it is possible to use the class directly caution is advised; as the class
70
- will create the driver upon instantiation.
71
-
72
- ```python
73
- from setup_selenium import SetupSelenium
74
-
75
- s = SetupSelenium(headless=True)
76
- assert s.driver.service.is_connectable()
77
- ```
78
-
79
-
80
96
  # Automatic driver and browser installation
81
97
  This package not only handles setup of the webdriver but also will
82
98
  automatically install the webdriver and/or browser depending on your
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "setup-selenium-testing"
3
- version = "0.2.0"
3
+ version = "1.0.0"
4
4
  description = "Setup Selenium for automation testing"
5
5
  authors = ["Marcel Wilson <trenchrats+pypi@gmail.com>"]
6
6
  license = "MIT"
@@ -10,7 +10,6 @@ readme = "README.md"
10
10
  classifiers = [
11
11
  "Operating System :: OS Independent",
12
12
  "Programming Language :: Python :: 3 :: Only",
13
- "Programming Language :: Python :: 3.8",
14
13
  "Programming Language :: Python :: 3.9",
15
14
  "Programming Language :: Python :: 3.10",
16
15
  "Programming Language :: Python :: 3.11",
@@ -23,7 +22,7 @@ packages = [{include = "setup_selenium"}]
23
22
 
24
23
 
25
24
  [tool.poetry.dependencies]
26
- python = "^3.8"
25
+ python = "^3.9"
27
26
  selenium = ">=4.7.0"
28
27
  semantic-version = "*"
29
28
  typing-extensions = "*"
@@ -67,7 +66,7 @@ build-backend = "poetry.core.masonry.api"
67
66
 
68
67
 
69
68
  [tool.ruff]
70
- target-version = "py38"
69
+ target-version = "py39"
71
70
  line-length = 88
72
71
  extend-exclude = [
73
72
  ".github",
@@ -2,14 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import errno
6
5
  import logging
7
6
  import os as os
8
7
  from enum import Enum
9
8
  from typing import TYPE_CHECKING, Optional, Union
10
9
 
11
10
  from selenium import __version__, webdriver
12
- from selenium.common.exceptions import NoSuchWindowException, WebDriverException
13
11
  from selenium.webdriver.chrome.service import Service as ChromeService
14
12
  from selenium.webdriver.common.selenium_manager import SeleniumManager
15
13
  from selenium.webdriver.edge.service import Service as EdgeService
@@ -18,7 +16,6 @@ from semantic_version import Version # type: ignore[import-untyped]
18
16
  from typing_extensions import TypeAlias
19
17
 
20
18
  if TYPE_CHECKING:
21
- from collections.abc import Mapping
22
19
 
23
20
  from selenium.webdriver import Chrome, Edge, Firefox
24
21
  from selenium.webdriver.common.options import ArgOptions
@@ -68,27 +65,10 @@ class Browser(str, Enum):
68
65
  ################################################################################
69
66
  ################################################################################
70
67
  class SetupSelenium:
71
- DIMENSIONS: Mapping[str, tuple[int, int]] = {
72
- # ratio 4:3
73
- "1024": (1024, 768),
74
- "1280": (1280, 960),
75
- "1600": (1600, 1200),
76
- "1920": (1920, 1440),
77
- # ratio 16:9
78
- "720": (1280, 720),
79
- "1080": (1920, 1080),
80
- "1440": (2560, 1440),
81
- "2160": (3840, 2160), # 4k
82
- "4320": (7680, 4320), # 8k
83
- }
84
-
85
68
  def __init__(
86
69
  self,
87
70
  browser: Browser = Browser.CHROME,
88
- baseurl: str = "",
89
- timeout: int = 15,
90
71
  headless: bool = False,
91
- window_size: str = "720",
92
72
  enable_log_performance: bool = False,
93
73
  enable_log_console: bool = False,
94
74
  enable_log_driver: bool = False,
@@ -99,11 +79,6 @@ class SetupSelenium:
99
79
  browser_path: str | None = None,
100
80
  ) -> None:
101
81
  log_path = os.path.abspath(os.path.expanduser(log_path))
102
- self.main_window_handle: str = ""
103
- self.screenshot_path: str = self.make_screenshot_path(log_path)
104
- self.log_path: str = log_path
105
- self.timeout: int = timeout
106
- self.baseurl: str = baseurl
107
82
 
108
83
  if driver_path:
109
84
  driver_path = os.path.abspath(os.path.expanduser(driver_path))
@@ -131,35 +106,11 @@ class SetupSelenium:
131
106
  driver_path=driver_path,
132
107
  )
133
108
 
134
- # driver must be setup before the following
135
- self.driver.set_window_position(0, 0)
136
- self.set_window_size(window_size)
137
- self.set_main_window_handle()
138
-
139
- ############################################################################
140
- @staticmethod
141
- def make_screenshot_path(
142
- output_dir: str = "./logs", screenshots: str = "screenshots"
143
- ) -> str:
144
- """Set the output directory for where screenshots should go."""
145
- output_dir = os.path.abspath(os.path.expanduser(output_dir))
146
- if os.path.split(output_dir)[-1].lower() != screenshots:
147
- output_dir = os.path.join(output_dir, screenshots)
148
-
149
- try:
150
- os.makedirs(output_dir)
151
- except OSError as e:
152
- if e.errno == errno.EEXIST and os.path.isdir(output_dir):
153
- pass
154
- else:
155
- raise
156
-
157
- return output_dir
158
-
159
109
  ############################################################################
160
110
  @staticmethod
161
111
  def log_options(options: ArgOptions) -> None:
162
112
  """Logs the browser option in clean format"""
113
+ # logger.debug(f"{json.dumps(options.capabilities, indent=2)}") # noqa: ERA001
163
114
  opts = "\n".join(options.arguments)
164
115
  logger.debug(f"{opts}")
165
116
 
@@ -282,6 +233,24 @@ class SetupSelenium:
282
233
  options.set_preference(
283
234
  "extensions.formautofill.addresses.capture.enabled", False
284
235
  )
236
+ # By default, headless Firefox runs as though no pointers capabilities
237
+ # are available.
238
+ # https://github.com/microsoft/playwright/issues/7769#issuecomment-966098074
239
+ #
240
+ # This impacts React Spectrum which uses an '(any-pointer: fine)'
241
+ # media query to determine font size. It also causes certain chart
242
+ # elements to always be visible that should only be visible on
243
+ # hover.
244
+ #
245
+ # Available values for pointer capabilities:
246
+ # NO_POINTER 0x00
247
+ # COARSE_POINTER 0x01
248
+ # FINE_POINTER 0x02
249
+ # HOVER_CAPABLE_POINTER 0x04
250
+ #
251
+ # Setting to 0x02 | 0x04 says the system supports a mouse
252
+ options.set_preference("ui.primaryPointerCapabilities", 0x02 | 0x04)
253
+ options.set_preference("ui.allPointerCapabilities", 0x02 | 0x04)
285
254
  return options
286
255
 
287
256
  @staticmethod
@@ -338,9 +307,29 @@ class SetupSelenium:
338
307
  def chrome_options() -> webdriver.ChromeOptions:
339
308
  """Default options for chrome"""
340
309
  logger.debug("Setting up chrome options")
310
+ # the ultimate list of flags (created by the chromium dev group)
311
+ # https://peter.sh/experiments/chromium-command-line-switches/
312
+
341
313
  # The list of options set below mostly came from this StackOverflow post
342
314
  # https://stackoverflow.com/q/48450594/2532408
343
315
  opts = (
316
+ # "--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync", # noqa: ERA001
317
+ "--disable-back-forward-cache",
318
+ "--disable-background-timer-throttling",
319
+ "--disable-breakpad",
320
+ "--disable-component-extensions-with-background-pages",
321
+ "--disable-ipc-flooding-protection",
322
+ "--enable-features=NetworkService,NetworkServiceInProcess",
323
+ "--enable-logging",
324
+ "--export-tagged-pdf",
325
+ "--force-color-profile=srgb",
326
+ "--metrics-recording-only",
327
+ "--mute-audio",
328
+ "--remote-debugging-pipe",
329
+ # fixes MUI fade issue
330
+ "--disable-renderer-backgrounding",
331
+ # fixes actionchains in headless
332
+ "--disable-backgrounding-occluded-windows",
344
333
  "--disable-extensions",
345
334
  "--allow-running-insecure-content",
346
335
  "--ignore-certificate-errors",
@@ -378,7 +367,7 @@ class SetupSelenium:
378
367
  options.binary_location = binary
379
368
 
380
369
  if headless:
381
- options.add_argument("--headless")
370
+ options.add_argument("--headless=new")
382
371
 
383
372
  logging_prefs = {"browser": "OFF", "performance": "OFF", "driver": "OFF"}
384
373
 
@@ -475,9 +464,29 @@ class SetupSelenium:
475
464
  def edge_options() -> webdriver.EdgeOptions:
476
465
  """Default options for edgedriver"""
477
466
  logger.debug("Setting up edge options")
467
+ # the ultimate list of flags (created by the chromium dev group)
468
+ # https://peter.sh/experiments/chromium-command-line-switches/
469
+
478
470
  # The list of options set below mostly came from this StackOverflow post
479
471
  # https://stackoverflow.com/q/48450594/2532408
480
472
  opts = (
473
+ # "--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync", # noqa: ERA001
474
+ "--disable-back-forward-cache",
475
+ "--disable-background-timer-throttling",
476
+ "--disable-breakpad",
477
+ "--disable-component-extensions-with-background-pages",
478
+ "--disable-ipc-flooding-protection",
479
+ "--enable-features=NetworkService,NetworkServiceInProcess",
480
+ "--enable-logging",
481
+ "--export-tagged-pdf",
482
+ "--force-color-profile=srgb",
483
+ "--metrics-recording-only",
484
+ "--mute-audio",
485
+ "--remote-debugging-pipe",
486
+ # fixes MUI fade issue
487
+ "--disable-renderer-backgrounding",
488
+ # fixes actionchains in headless
489
+ "--disable-backgrounding-occluded-windows",
481
490
  "--disable-extensions",
482
491
  "--allow-running-insecure-content",
483
492
  "--ignore-certificate-errors",
@@ -523,12 +532,11 @@ class SetupSelenium:
523
532
  # by default performance is disabled.
524
533
  if enable_log_performance:
525
534
  logging_prefs["performance"] = "ALL"
526
- options.set_capability(
535
+ options.add_experimental_option(
527
536
  "perfLoggingPrefs",
528
537
  {
529
538
  "enableNetwork": True,
530
539
  "enablePage": False,
531
- "enableTimeline": False,
532
540
  },
533
541
  )
534
542
 
@@ -579,42 +587,3 @@ class SetupSelenium:
579
587
  logger.info(bsrmsg)
580
588
  SetupSelenium.log_options(options)
581
589
  return driver
582
-
583
- ############################################################################
584
- def set_window_size(self, size: str = "720") -> None:
585
- """Helper to set the window size after driver has been instantiated."""
586
- if size == "max":
587
- self.driver.maximize_window()
588
- return
589
-
590
- width, height = SetupSelenium.DIMENSIONS.get(
591
- size, SetupSelenium.DIMENSIONS.get(size, (1280, 720))
592
- )
593
- self.driver.set_window_size(width, height)
594
-
595
- def set_main_window_handle(self, window: str | None = None) -> str:
596
- """
597
- Maintains the initial window handle as an attribute
598
-
599
- Most users will never utilize this. It's part of a legacy requirement for
600
- an old test suite
601
- """
602
- # does the main_window_handle exist and point to an available window?
603
- if not window and not self.main_window_handle:
604
- try:
605
- window = self.driver.current_window_handle
606
- except NoSuchWindowException:
607
- try:
608
- window = self.driver.window_handles[0]
609
- except WebDriverException: # noqa: TRY203
610
- # Have we closed all the windows?
611
- raise
612
- if window:
613
- self.main_window_handle = window
614
- return self.main_window_handle
615
-
616
- ############################################################################
617
- def __repr__(self) -> str:
618
- browser = self.driver.name if self.driver is not None else "NoBrowserSet"
619
- url = self.baseurl
620
- return f"{self.__class__.__name__} :: {browser} -> {url}"