Skip to main content

Selenium core for Python

Project description

Selenium CORE for Python

Latest Version License Supported Python versions Supported Python implementations Tests codecov

Introduction

It's a library with core functions simplifying work with Selenium-controlled applications.

This package is based on Aquality Selenium CORE for .NET and provides a set of methods related to the most common actions performed with elements. So you shouldn't have a lot of difficulties with this solution if you interacted with Aquality Selenium CORE.

To simplify overriding of implementations this solution uses Dependency Injection.

Supported Python Versions

  • Python 3.7-3.12

Installation

If you have pip on your system, you can simply install or upgrade the Python bindings:

pip install py-selenium-auto-core

Alternately, you can download the source distribution from PyPI, unarchive it, and run:

python setup.py install

Quick start

  1. Setup Dependency Injection container using Startup

The solution offers a ServiceProvider implementation using Dependency Injection container as a single point for interacting with various services and their dependencies:

class ServiceProvider(containers.DeclarativeContainer):
    """Container that allows to resolve dependencies for all services in the library"""
    
    settings_file: Singleton[JsonSettingsFile] = Singleton(JsonSettingsFile({}))
    application: Factory[Application] = Factory(Application)
    logger: Singleton[Logger] = Singleton(Logger)
    logger_configuration: Singleton[LoggerConfiguration] = Singleton(LoggerConfiguration, settings_file)
    timeout_configuration: Singleton[TimeoutConfiguration] = Singleton(TimeoutConfiguration, settings_file)
    localization_manager: Singleton[LocalizationManager] = Singleton(LocalizationManager, logger_configuration, logger)
    ...

This allows you to control dependencies between packages and cause the creation of the objects themselves at the time of accessing them. Example of working with ServiceProvider:

ServiceProvider.logger().info("Message")  # message logging
ServiceProvider.timeout_configuration().interval  # Getting the Timeout.interval value

For ease of use, the solution contains a Startup object that makes it easy to get a ServiceProvider object with redefined dependencies for settings_file and application:

class Startup:

    @staticmethod
    def configure_services(application_provider: Callable, settings: Optional[JsonSettingsFile] = None, service_provider: Optional[T] = None,
    ) -> T | ServiceProvider:
        service_provider: T = service_provider or ServiceProvider()
        settings = settings or Startup.get_settings()

        service_provider.settings_file.override(Singleton(lambda: settings))
        service_provider.application.override(Factory(application_provider))

        return service_provider

    @staticmethod
    def get_settings() -> JsonSettingsFile:
        profile_name = EnvironmentConfiguration.get_variable("profile")
        settings_profile = "settings.json" if not profile_name else f"settings.{profile_name}.json"
        if FileReader.is_resource_file_exist(settings_profile, root_path=RootPathHelper.calling_root_path()):
            return JsonSettingsFile(setting_name=settings_profile, root_path=RootPathHelper.calling_root_path())
        return JsonSettingsFile(setting_name=settings_profile, root_path=RootPathHelper.executing_root_path())

service_provider = Startup.configure_services(application_provider=lambda: YourApplication())
service_provider.application()
service_provider.logger().info("Message")

To use a different implementation between dependencies, you need to execute provider.override:

service_provider = ServiceProvider()
service_provider.timeout_configuration.override(Singleton(CustomTimeoutConfiguration, service_provider.settings_file))

If you want to have your own CustomServiceProvider implementation that complements the base container, you can use inheritance, as in the example below:

class CustomServiceProvider(ServiceProvider):
    timeout: Sigleton[CustomTimeoutConfiguration] = Singleton(CustomTimeoutConfiguration, ServiceProvider.settings_file)

Keep in mind that the internal dependencies of the ServiceProvider container will interact with ServiceProvider.timeout, and not with CustomServiceProvider.timeout. The solution is quite simple:

ServiceProvider.override(CustomServiceProvider)
service_provider = Startup.configure_services(application_provider=lambda: YourApplication(), service_provider=CustomServiceProvider())
ServiceProvider.reset_override()  # necessary because override overrides the base container

or add your implementation of Startup:

class CustomSPStartup(Startup):

    @staticmethod
    def configure_services(
            application_provider: Callable, settings: Optional[JsonSettingsFile] = None, service_provider: Optional[ServiceProvider] = None,
    ) -> CustomServiceProvider:
        ServiceProvider.override(CustomServiceProvider)

        settings = JsonSettingsFile("settings.special.json", RootPathHelper.calling_root_path())
        service_provider = Startup.configure_services(application_provider, settings, CustomServiceProvider())

        ServiceProvider.reset_override()
        return service_provider
  1. Setup BrowserService using CoreServices

The solution also contains a CoreService, which helps to register your container and application, with which you can interact in the future:

The simplest way is to create your own Services class extended from abstract CoreServices with the following simple signature:

class BrowserServices(CoreServices):

    @classmethod
    def is_application_started(cls) -> bool:
        return cls._is_application_started()

    @classmethod
    def application(cls) -> YourApplication:
        return cls._get_application(lambda service: cls._start_application(service))

    @classmethod
    def service_provider(cls) -> ServiceProvider:
        return cls._get_service_provider(lambda service: cls.application())

    @classmethod
    def _start_application(cls, service_provider: ServiceProvider):
        ...  # your implementation

If you need to register your own services / rewrite the implementation, you need override Startup and implement BrowserServices like in example below:

class TestStartup(Startup):

    @staticmethod
    def configure_services(
            application_provider: Callable,
            settings: Optional[JsonSettingsFile] = None,
            service_provider: Optional[ServiceProvider] = None,
    ) -> ServiceProvider:
        settings = JsonSettingsFile("settings.special.json", RootPathHelper.calling_root_path())
        service_provider = Startup.configure_services(application_provider, settings)
        service_provider.timeout_configuration.override(
            Singleton(TestTimeoutConfiguration, service_provider.settings_file)
        )
        return service_provider

class BrowserServices:

    class _BrowserService(CoreServices):

        startup: TestStartup = TestStartup()

        def __init__(self):
            Logger.info("Create")

        @property
        def application(self) -> Application:
            return self._get_application(
                self._start_function,
                lambda: self.startup.configure_services(lambda service: self.application),
            )

        @property
        def service_provider(self) -> ServiceProvider:
            return self._get_service_provider(
                lambda service: self.application,
                lambda: self.startup.configure_services(lambda service: self.application),
            )

        def set_startup(self, startup: Startup):
            if startup is not None:
                self.startup = startup

        @property
        def _start_function(self):
            return lambda serive: Application()

    Instance: _BrowserService = _BrowserService()


class CustomStartup(Startup):

    @staticmethod
    def configure_services(application_provider: Callable, settings: JsonSettingsFile = None) -> ServiceProvider:
        service_provider = Startup.configure_services(application_provider, settings)
        # your implementation service_provider.timeout_configuration.override(Singleton(TimeoutConfiguration, service_provider.settings_file))
        return service_provider
  1. Work with Application via the implemented BrowserServices or via element services

All the services could be resolved from the Dependency Injection container via ServiceProvider

BrowserServices.application().driver.find_element(ELEMENT).click()
BrowserServices.service_provider().conditional_wait().wait_for_driver(
    lambda driver: len(driver.find_elements(Locator(By.XPATH, "//*"))) > 0
)

or with Instance:

BrowserServices.Instance.application.driver.find_element(ELEMENT).click()
BrowserServices.Instance.service_provider.conditional_wait().wait_for_driver(
    lambda driver: len(driver.find_elements(Locator(By.XPATH, "//*"))) > 0
)

License

Library's source code is made available under the Apache 2.0 license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

py_selenium_auto_core-0.5.6.tar.gz (28.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

py_selenium_auto_core-0.5.6-py3-none-any.whl (39.1 kB view details)

Uploaded Python 3

File details

Details for the file py_selenium_auto_core-0.5.6.tar.gz.

File metadata

  • Download URL: py_selenium_auto_core-0.5.6.tar.gz
  • Upload date:
  • Size: 28.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.12.6

File hashes

Hashes for py_selenium_auto_core-0.5.6.tar.gz
Algorithm Hash digest
SHA256 9394df636aafac48ed66616bd859b2256b4f500e5bd423888f27d7bb237e191b
MD5 fd5f0bae43ed248266ef936cd6eefdf5
BLAKE2b-256 f928328dfe3b63595f03b590dd5b2dab394fe41c477e807f5fbc10bc65f5859b

See more details on using hashes here.

File details

Details for the file py_selenium_auto_core-0.5.6-py3-none-any.whl.

File metadata

File hashes

Hashes for py_selenium_auto_core-0.5.6-py3-none-any.whl
Algorithm Hash digest
SHA256 90583471ec5780583da0b610f261dbeed1fdb75234b4dc5b3421c29a20bb3060
MD5 239182e7e182a0601440f999bf8c5c57
BLAKE2b-256 bc2ad745f348ff1898df69568f0a357651501d1b2434ae146dcefd8365bae90a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page