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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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}"