setup-selenium-testing 0.1.1__tar.gz → 0.1.3__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: setup-selenium-testing
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Setup Selenium for automation testing
5
5
  Home-page: https://github.com/bandophahita/setup_selenium
6
6
  License: MIT
@@ -21,17 +21,14 @@ Classifier: Programming Language :: Python :: 3 :: Only
21
21
  Provides-Extra: dev
22
22
  Provides-Extra: test
23
23
  Requires-Dist: black ; extra == "dev"
24
- Requires-Dist: coverage ; extra == "dev"
25
- Requires-Dist: flake8 ; extra == "dev"
24
+ Requires-Dist: isort ; extra == "dev"
26
25
  Requires-Dist: mypy ; extra == "dev"
27
- Requires-Dist: pylint ; extra == "dev"
28
26
  Requires-Dist: pytest ; extra == "dev" or extra == "test"
29
27
  Requires-Dist: ruff ; extra == "dev"
30
28
  Requires-Dist: selenium (>=4.7.0)
31
29
  Requires-Dist: semantic-version
32
- Requires-Dist: setuptools ; extra == "dev"
33
30
  Requires-Dist: tox ; extra == "dev" or extra == "test"
34
- Requires-Dist: typing-extensions (>=4.7.1,<5.0.0)
31
+ Requires-Dist: typing-extensions
35
32
  Project-URL: Repository, https://github.com/bandophahita/setup_selenium
36
33
  Description-Content-Type: text/markdown
37
34
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "setup-selenium-testing"
3
- version = "0.1.1"
3
+ version = "0.1.3"
4
4
  description = "Setup Selenium for automation testing"
5
5
  authors = ["Marcel Wilson <trenchrats@gmail.com>"]
6
6
  license = "MIT"
@@ -25,30 +25,23 @@ packages = [{include = "setup_selenium"}]
25
25
  python = "^3.8"
26
26
  selenium = ">=4.7.0"
27
27
  semantic-version = "*"
28
- typing-extensions = "^4.7.1"
28
+ typing-extensions = "*"
29
29
 
30
30
  # extras
31
31
  pytest = {version="*", optional = true}
32
- coverage = {version="*", optional = true}
33
- flake8 = {version="*", optional = true}
34
32
  black = {version="*", optional = true}
35
33
  mypy = {version="*", optional = true}
36
- pylint = {version="*", optional = true}
37
- setuptools = {version="*", optional = true}
38
34
  ruff = {version="*", optional = true}
39
35
  tox = {version="*", optional = true}
40
-
36
+ isort = {version = "*", optional = true}
41
37
 
42
38
  [tool.poetry.extras]
43
39
  dev = [
44
40
  "pytest",
45
- "coverage",
46
- "flake8",
47
41
  "black",
48
42
  "mypy",
49
- "pylint",
50
- "setuptools",
51
43
  "ruff",
44
+ "isort",
52
45
  "tox",
53
46
  ]
54
47
  test = [
@@ -88,72 +81,68 @@ build-backend = "poetry.core.masonry.api"
88
81
  # Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
89
82
  # McCabe complexity (`C901`) by default.
90
83
  select = [
84
+ "A", # flake8-builtins
85
+ "ANN", # flake8-annotations # coming back to this one later to compare against mypy
86
+ "ARG", # flake8-unused-arguments
87
+ "B", # flake8-bugbear
88
+ "BLE", # flake8-blind-except
89
+ "C4", # flake8-comprehensions
90
+ "D", # pydocstyle
91
91
  "E", # pycodestyle error
92
- "W", # pycodestyle warning
92
+ "EM", # flake8-errmsg
93
+ "ERA", # eradicate
93
94
  "F", # Pyflakes
94
- "FA", # flake8-future-annotations
95
+ "FA", # flake8-future-annotations
96
+ # "FBT", # flake8-boolean-trap
97
+ "FIX", # flake8-fixme
98
+ "FLY", # flynt
95
99
  "I", # isort
96
- "D", # pydocstyle
97
- "C4", # flake8-comprehensions
98
- "TCH", # type checking
100
+ "ICN", # flake8-import-conventions
101
+ "ISC", # flake8-implicit-str-concat
102
+ "PGH", # pygrep-hooks
103
+ "PIE", # flake8-pie
104
+ "PL", # pylint
105
+ "PT", # flake8-pytest-style
106
+ "Q", # flake8-quotes
107
+ "RET", # flake8-return
108
+ "RSE", # flake8-raise
99
109
  "RUF", # ruff specific
100
- "ANN", # annotations
110
+ "SIM", # flake8-simplify
111
+ "T10", # flake8-debugger
112
+ "TCH", # flake8-type-checking
113
+ "TD", #TODOs
114
+ "TRY", # tryceratops
101
115
  "UP", # python upgrade
102
- "B", # bugbear
103
- "SIM", # flake8 simplify
104
- "A", # built-ins
105
- "RET", # flake8 return
106
- "YTT", # flake8-2020
107
- "EXE", # flake8-executable
108
- "ISC", # flake8-implicit-str-concat
109
- "PIE", # flake8-pie
110
- "Q", # flake8-quotes
111
- "RSE", # flake8-raise
112
- "FLY", # flynt
113
- "PERF", # perflint
114
- "PL", # pylint
115
- # "ERA", # eradicate
116
- # "N", # pep8 naming -- dear lord, leave this off
116
+ "W", # pycodestyle warning
117
+ "YTT", # flake8-2020
117
118
 
118
119
  # may eventually use but for now these are not helpful
119
- # "FURB", # refurb
120
- # "ARG", # unused arguments
121
- # "PT", # Pytest style
122
- # "TD", #TODOs
123
- # "FBT", # boolean trap
120
+ # "FURB", # refurb # needs --preview flag to run
124
121
  ]
125
122
  ignore = [
126
- # "PT003", # pytest fixture scope implied
127
- # "PT004", # pytest fixture setup doesn't return anything
128
- "RUF100", # blanket noqa
129
123
  "ANN101", # missing-type-self
130
124
  "ANN102", # cls
131
125
  "E501", # line too long -- black will take care of this for us
132
- "E721", # type-comparison WTF?
133
- "B028", # No explicit `stacklevel` keyword argument found
134
- "SIM102", # single if instead of nested -- only sometimes useful
135
- "SIM114", # combine if branches using logical OR -- only sometimes useful
136
126
  "SIM115", # use context handler for open -- situationally useful
137
- "SIM118", # use x in y instead of y.keys() - sometimes we want the keys
138
- "SIM300", # yoda conditions -- meh
127
+ # "SIM300", # yoda conditions -- meh
139
128
 
140
129
  "PERF203", # `try`-`except` within a loop incurs performance overhead
141
130
  "PERF401", # use list comp
142
131
 
143
132
  # NOT OPTIONAL. MUST REMAIN AS SET
144
133
  # these are all completely unnecessary
145
- "D100", # Missing docstring in public module
146
134
  "D101", # Missing docstring in public class
147
- "D102", # Missing docstring in public method
148
- "D103", # Missing docstring in public function
149
135
  "D104", # Missing docstring in public package
150
136
  "D105", # Missing docstring in magic method
151
137
  "D106", # Missing docstring in public nested class
152
138
  "D107", # Missing docstring in `__init__`
153
- "D301", # Use `r"""` if any backslashes in a docstring
139
+ "D202", # no blank lines after docstring
140
+ "D203", # one-blank-line-before-class
141
+ "D204", # blank line required after docstring
142
+ "D205", # blank line between summary and description
143
+ "D212", # Multi-line summary should start at the first line
154
144
  "D400", # First line should end with a period
155
145
  "D401", # imperative mood
156
- "D403", # first word of 1st line caps
157
146
  "D404", # First word of the docstring should not be "This"
158
147
  "D405", # Section name should be properly capitalized
159
148
  "D406", # Section name should end with a newline
@@ -161,21 +150,12 @@ ignore = [
161
150
  "D411", # Missing blank line before section
162
151
  "D412", # No blank lines allowed between a section header and its content
163
152
  "D415", # First line should end with punctuation
164
- "D200", # One-line docstring should fit on one line
165
- "D202", # no blank lines after docstring
166
- "D204", # blank line required after docstring
167
- "D205", # blank line between summary and description
168
- "D203", # one-blank-line-before-class
169
- "D210", # whitespace
170
- "D212", # Multi-line summary should start at the first line
171
- "D214", # Section overindented
172
- "A003", # Class attribute shadow builtin
153
+
173
154
  "PLC0414", # useless-import-alias
174
155
  "PLR0911", # too-many-return-statements
175
156
  "PLR0912", # too-many-branches
176
157
  "PLR0913", # too-many-arguments
177
158
  "PLR0915", # too-many-statements
178
- "PLR2004", # magic-value-comparison
179
159
 
180
160
  # 3.8 & 3.9 compatibility
181
161
  "UP007", # Use `X | Y` for type annotations
@@ -215,11 +195,25 @@ exclude = [
215
195
  ]
216
196
 
217
197
  [tool.ruff.lint]
218
- extend-safe-fixes = ["TCH003"]
198
+ extend-safe-fixes = [
199
+ "EM101",
200
+ "EM102",
201
+ "TCH001", "TCH002", "TCH003", "TCH004",
202
+ # "SIM108"
203
+ "C419",
204
+ "D200", "D205",
205
+ "PT003", "PT006", "PT018",
206
+ "RET504",
207
+ "UP007",
208
+ ]
219
209
 
220
210
  [tool.ruff.per-file-ignores]
221
211
  "__init__.py" = ["F401"]
222
-
212
+ "tests/**" = [
213
+ "D", # we don't need public-API-polished docstrings in tests.
214
+ "FBT", # using a boolean as a test object is useful!
215
+ "PLR", # likewise using specific numbers and strings in tests.
216
+ ]
223
217
 
224
218
  [tool.ruff.isort]
225
219
  combine-as-imports = true
@@ -234,19 +228,16 @@ mark-parentheses = false
234
228
  ignore-overlong-task-comments = true
235
229
 
236
230
 
237
- #[tool.ruff.flake8-annotations]
238
- #mypy-init-return = true
239
-
240
-
241
231
  [tool.ruff.flake8-annotations]
242
232
  # ignore returns types for functions that implicity or explicitly only return None
243
233
  suppress-none-returning = true
244
234
  allow-star-arg-any = true
235
+ #mypy-init-return = true
245
236
 
246
237
 
247
238
  [tool.black]
248
239
  line-length = 88
249
- target-version = ['py310']
240
+ target-version = ['py311']
250
241
 
251
242
  # 'extend-exclude' excludes files or directories in addition to the defaults
252
243
  extend-exclude = '''
@@ -1,10 +1,12 @@
1
+ """Setup selenium for testing"""
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  import errno
4
6
  import logging
5
7
  import os as os
6
8
  from enum import Enum
7
- from typing import TYPE_CHECKING, Union
9
+ from typing import TYPE_CHECKING, Optional, Union
8
10
 
9
11
  from selenium import webdriver
10
12
  from selenium.common.exceptions import NoSuchWindowException, WebDriverException
@@ -12,7 +14,7 @@ from selenium.webdriver.chrome.service import Service as ChromeService
12
14
  from selenium.webdriver.common.selenium_manager import SeleniumManager
13
15
  from selenium.webdriver.edge.service import Service as EdgeService
14
16
  from selenium.webdriver.firefox.service import Service as FirefoxService
15
- from semantic_version import Version # type: ignore
17
+ from semantic_version import Version # type: ignore[import-untyped]
16
18
  from typing_extensions import TypeAlias
17
19
 
18
20
  if TYPE_CHECKING:
@@ -22,6 +24,9 @@ if TYPE_CHECKING:
22
24
  from selenium.webdriver.common.options import ArgOptions
23
25
 
24
26
  T_WebDriver: TypeAlias = Union[Firefox, Chrome, Edge]
27
+ T_DrvOpts: TypeAlias = Union[
28
+ webdriver.FirefoxOptions, webdriver.ChromeOptions, webdriver.EdgeOptions
29
+ ]
25
30
 
26
31
  __all__ = ["SetupSelenium"]
27
32
 
@@ -38,12 +43,11 @@ logger = create_logger("sel")
38
43
 
39
44
 
40
45
  def set_logger(logr: logging.Logger) -> None:
41
- """
42
- Set the global logger with a custom logger
43
- """
46
+ """Set the global logger with a custom logger"""
44
47
  # Check if the logger is a valid logger
45
48
  if not isinstance(logr, logging.Logger):
46
- raise ValueError("The logger must be an instance of logging.Logger")
49
+ msg = "logger must be an instance of logging.Logger"
50
+ raise TypeError(msg)
47
51
 
48
52
  # Bind the logger input to the global logger
49
53
  global logger # noqa: PLW0603
@@ -132,9 +136,7 @@ class SetupSelenium:
132
136
  def make_screenshot_path(
133
137
  output_dir: str = "./logs", screenshots: str = "screenshots"
134
138
  ) -> str:
135
- """
136
- Set the output directory for where screenshots should go.
137
- """
139
+ """Set the output directory for where screenshots should go."""
138
140
  output_dir = os.path.abspath(os.path.expanduser(output_dir))
139
141
  if os.path.split(output_dir)[-1].lower() != screenshots:
140
142
  output_dir = os.path.join(output_dir, screenshots)
@@ -152,6 +154,7 @@ class SetupSelenium:
152
154
  ############################################################################
153
155
  @staticmethod
154
156
  def log_options(options: ArgOptions) -> None:
157
+ """Logs the browser option in clean format"""
155
158
  opts = "\n".join(options.arguments)
156
159
  logger.debug(f"{opts}")
157
160
 
@@ -163,7 +166,7 @@ class SetupSelenium:
163
166
  browser_path: str | None = None,
164
167
  install_browser: bool = False,
165
168
  ) -> tuple[str, str]:
166
- """install the webdriver and browser if needed."""
169
+ """Install the webdriver and browser if needed."""
167
170
  browser = Browser[browser.upper()].lower()
168
171
  driver_version = driver_version or None
169
172
 
@@ -203,19 +206,24 @@ class SetupSelenium:
203
206
  log_dir: str = "./logs",
204
207
  binary: str | None = None,
205
208
  driver_path: str | None = None,
209
+ options: Optional[T_DrvOpts] = None,
206
210
  ) -> T_WebDriver:
211
+ """Instantiates the browser driver"""
207
212
  browser = browser.lower()
208
213
  driver: T_WebDriver
209
214
  if browser == Browser.FIREFOX:
215
+ assert options is None or isinstance(options, webdriver.FirefoxOptions)
210
216
  driver = SetupSelenium.firefox(
211
217
  headless=headless,
212
218
  enable_log_driver=enable_log_driver,
213
219
  log_dir=log_dir,
214
220
  binary=binary,
215
221
  driver_path=driver_path,
222
+ options=options,
216
223
  )
217
224
 
218
225
  elif browser == Browser.CHROME:
226
+ assert options is None or isinstance(options, webdriver.ChromeOptions)
219
227
  driver = SetupSelenium.chrome(
220
228
  headless=headless,
221
229
  enable_log_performance=enable_log_performance,
@@ -224,9 +232,11 @@ class SetupSelenium:
224
232
  log_dir=log_dir,
225
233
  binary=binary,
226
234
  driver_path=driver_path,
235
+ options=options,
227
236
  )
228
237
 
229
238
  elif browser == Browser.EDGE:
239
+ assert options is None or isinstance(options, webdriver.EdgeOptions)
230
240
  driver = SetupSelenium.edge(
231
241
  headless=headless,
232
242
  enable_log_performance=enable_log_performance,
@@ -235,15 +245,18 @@ class SetupSelenium:
235
245
  log_dir=log_dir,
236
246
  binary=binary,
237
247
  driver_path=driver_path,
248
+ options=options,
238
249
  )
239
250
 
240
251
  else:
241
- raise ValueError(f"Unknown browser: {browser}")
252
+ msg = f"Unknown browser: {browser}"
253
+ raise ValueError(msg)
242
254
 
243
255
  return driver
244
256
 
245
257
  @staticmethod
246
258
  def firefox_options() -> webdriver.FirefoxOptions:
259
+ """Default options for firefox"""
247
260
  options = webdriver.FirefoxOptions()
248
261
  options.set_capability("unhandledPromptBehavior", "ignore")
249
262
 
@@ -261,8 +274,9 @@ class SetupSelenium:
261
274
  log_dir: str = "./logs",
262
275
  driver_path: str | None = None,
263
276
  binary: str | None = None,
264
- options: webdriver.FirefoxOptions = None,
277
+ options: webdriver.FirefoxOptions | None = None,
265
278
  ) -> webdriver.Firefox:
279
+ """Instantiates firefox geockodriver"""
266
280
  options = options or SetupSelenium.firefox_options()
267
281
  if binary:
268
282
  options.binary_location = binary
@@ -301,6 +315,7 @@ class SetupSelenium:
301
315
 
302
316
  @staticmethod
303
317
  def chrome_options() -> webdriver.ChromeOptions:
318
+ """Default options for chrome"""
304
319
  logger.debug("Setting up chrome options")
305
320
  # The list of options set below mostly came from this StackOverflow post
306
321
  # https://stackoverflow.com/q/48450594/2532408
@@ -332,8 +347,9 @@ class SetupSelenium:
332
347
  log_dir: str = "./logs",
333
348
  driver_path: str | None = None,
334
349
  binary: str | None = None,
335
- options: webdriver.ChromeOptions = None,
350
+ options: webdriver.ChromeOptions | None = None,
336
351
  ) -> webdriver.Chrome:
352
+ """Instantiates chromedriver"""
337
353
  options = options or SetupSelenium.chrome_options()
338
354
  if binary:
339
355
  options.binary_location = binary
@@ -374,12 +390,12 @@ class SetupSelenium:
374
390
  service = ChromeService(
375
391
  executable_path=driver_path,
376
392
  service_args=args,
377
- log_output=logpath,
393
+ log_output=logpath, # type: ignore[arg-type]
378
394
  )
379
395
  else:
380
396
  service = ChromeService(
381
397
  service_args=args,
382
- log_output=logpath,
398
+ log_output=logpath, # type: ignore[arg-type]
383
399
  )
384
400
 
385
401
  driver = webdriver.Chrome(service=service, options=options)
@@ -405,6 +421,7 @@ class SetupSelenium:
405
421
 
406
422
  @staticmethod
407
423
  def set_throttle(driver: webdriver.Chrome):
424
+ """Experimental settings to slow down browser"""
408
425
  # experimental settings to slow down browser
409
426
  # @formatter:off
410
427
  # fmt: off
@@ -433,6 +450,7 @@ class SetupSelenium:
433
450
 
434
451
  @staticmethod
435
452
  def edge_options() -> webdriver.EdgeOptions:
453
+ """Default options for edgedriver"""
436
454
  logger.debug("Setting up edge options")
437
455
  # The list of options set below mostly came from this StackOverflow post
438
456
  # https://stackoverflow.com/q/48450594/2532408
@@ -447,8 +465,6 @@ class SetupSelenium:
447
465
  # edgedriver crashes without these two in linux
448
466
  "--no-sandbox",
449
467
  "--disable-dev-shm-usage",
450
- # it's possible we no longer need to do these
451
- # "--disable-gpu", # https://stackoverflow.com/q/51959986/2532408
452
468
  )
453
469
  options = webdriver.EdgeOptions()
454
470
  for opt in opts:
@@ -464,8 +480,9 @@ class SetupSelenium:
464
480
  log_dir: str = "./logs",
465
481
  driver_path: str | None = None,
466
482
  binary: str | None = None,
467
- options: webdriver.EdgeOptions = None,
483
+ options: webdriver.EdgeOptions | None = None,
468
484
  ) -> webdriver.Edge:
485
+ """Instantiates edgedriver"""
469
486
  options = options or SetupSelenium.edge_options()
470
487
  if binary:
471
488
  options.binary_location = binary
@@ -507,12 +524,12 @@ class SetupSelenium:
507
524
  service = EdgeService(
508
525
  executable_path=driver_path,
509
526
  service_args=args,
510
- log_output=logpath,
527
+ log_output=logpath, # type: ignore[arg-type]
511
528
  )
512
529
  else:
513
530
  service = EdgeService(
514
531
  service_args=args,
515
- log_output=logpath,
532
+ log_output=logpath, # type: ignore[arg-type]
516
533
  )
517
534
  driver = webdriver.Edge(service=service, options=options)
518
535
 
@@ -540,6 +557,7 @@ class SetupSelenium:
540
557
 
541
558
  ############################################################################
542
559
  def set_window_size(self, size: str = "720") -> None:
560
+ """Helper to set the window size after driver has been instantiated."""
543
561
  if size == "max":
544
562
  self.driver.maximize_window()
545
563
  return
@@ -550,17 +568,22 @@ class SetupSelenium:
550
568
  self.driver.set_window_size(width, height)
551
569
 
552
570
  def set_main_window_handle(self, window: str | None = None) -> str:
553
- if not window:
554
- # does the main_window_handle exist and point to an available window?
555
- if not self.main_window_handle:
571
+ """
572
+ maintains the initial window handle as an attribute
573
+
574
+ Most users will never utilize this. It's part of a legacy requirement for
575
+ an old test suite
576
+ """
577
+ # does the main_window_handle exist and point to an available window?
578
+ if not window and not self.main_window_handle:
579
+ try:
580
+ window = self.driver.current_window_handle
581
+ except NoSuchWindowException:
556
582
  try:
557
- window = self.driver.current_window_handle
558
- except NoSuchWindowException:
559
- try:
560
- window = self.driver.window_handles[0]
561
- except WebDriverException:
562
- # Have we closed all the windows?
563
- raise
583
+ window = self.driver.window_handles[0]
584
+ except WebDriverException: # noqa: TRY302
585
+ # Have we closed all the windows?
586
+ raise
564
587
  if window:
565
588
  self.main_window_handle = window
566
589
  return self.main_window_handle