def _initialize_in_thread(self):
        """
        This should be called in the thread when making the first call to the
        LocatorServerManager.
        """
        if self._initialized:
            return

        from robocorp_code.locators.server.locator_server_manager import (
            LocatorServerManager, )
        from robocorp_code.locators_db import LocatorsDB

        self._initialized = True
        self._locator_server_manager = LocatorServerManager()
        self._locators_db = LocatorsDB()
def test_locators_db(tmpdir):
    from robocorp_code.locators_db import LocatorsDB
    import json

    locators_db = LocatorsDB()
    robot_yaml_location = tmpdir.join("robot.yaml")
    locators_db.set_robot_yaml_location(str(robot_yaml_location))
    browser_locator = {
        "strategy": "strategy",
        "value": "value",
        "source": "source",
        "screenshot": "screenshot",
        "type": "browser",
    }
    locators_db.add_browser_locator(browser_locator)
    locators_db.add_browser_locator(browser_locator)

    with open(locators_db.locators_json, "r", encoding="utf-8") as stream:
        assert json.load(stream) == {
            "Browser.Locator.00": {
                "screenshot": "screenshot",
                "source": "source",
                "strategy": "strategy",
                "type": "browser",
                "value": "value",
            },
            "Browser.Locator.01": {
                "screenshot": "screenshot",
                "source": "source",
                "strategy": "strategy",
                "type": "browser",
                "value": "value",
            },
        }
class _LocatorsInThreadAPI:
    """
    An API which allows managing the LocatorServerManager and LocatorsDB in
    a separate thread (note that most of its method calls return a Future).
    """

    def __init__(self):
        from concurrent.futures.thread import ThreadPoolExecutor

        # We only use 1 thread here!
        self._thread_pool = ThreadPoolExecutor(1)
        self._initialized = False

    def shutdown(self):
        self._thread_pool.shutdown(wait=False)

    def _initialize_in_thread(self):
        """
        This should be called in the thread when making the first call to the
        LocatorServerManager.
        """
        if self._initialized:
            return

        from robocorp_code.locators.server.locator_server_manager import (
            LocatorServerManager,
        )
        from robocorp_code.locators_db import LocatorsDB

        self._initialized = True
        self._locator_server_manager = LocatorServerManager()
        self._locators_db = LocatorsDB()

    def _initialize_and_call(self, func):
        self._initialize_in_thread()
        return func()

    def _submit_and_return_from_thread(self, func) -> Future:
        future = self._thread_pool.submit(self._initialize_and_call, func)
        return future

    def create_locator_from_browser_pick(self) -> "Future[ActionResultDict]":
        """
        Creates a locator from a browser pick.
        """
        return self._submit_and_return_from_thread(
            self._in_thread_create_locator_from_browser_pick
        )

    def _in_thread_create_locator_from_browser_pick(
        self
    ) -> ActionResultDictBrowserLocatorContainer:
        from typing import cast

        error_msg = self._locators_db.validate()
        if error_msg:
            return {"success": False, "message": error_msg, "result": None}

        result = self._locator_server_manager.browser_locator_pick()
        if result["success"]:
            # Ok, it did get the info for the browser locator pick, now, let's
            # add it to the json.
            try:
                browser_locator: BrowserLocatorTypedDict = cast(
                    BrowserLocatorTypedDict, result["result"]
                )
                name = self._locators_db.add_browser_locator(browser_locator)
                return {
                    "success": True,
                    "message": None,
                    "result": {"name": name, "locator": result["result"]},
                }

            except Exception as e:
                log.exception("Error creating browser locator.")
                return {"success": False, "message": str(e), "result": None}

        return result

    def browser_locator_stop(self) -> "Future[ActionResultDict]":
        """
        Stops the browser locator.
        """
        return self._submit_and_return_from_thread(self._in_thread_browser_locator_stop)

    def _in_thread_browser_locator_stop(self) -> ActionResultDict:
        return self._locator_server_manager.browser_locator_stop()

    def start_browser_locator(self, robot_yaml) -> "Future[ActionResultDict]":
        """
        Starts the browser locator and sets the robot yaml to be changed.
        """
        return self._submit_and_return_from_thread(
            partial(self._in_thread_start_browser_locator, robot_yaml=robot_yaml)
        )

    def _in_thread_start_browser_locator(self, robot_yaml) -> ActionResultDict:
        self._locators_db.set_robot_yaml_location(robot_yaml)
        return self._locator_server_manager.browser_locator_start()

    def set_robot_yaml_location(self, robot_yaml) -> "Future[None]":
        """
        Sets the robot yaml to be changed.
        """
        return self._submit_and_return_from_thread(
            partial(self._in_thread_set_robot_yaml_location, robot_yaml=robot_yaml)
        )

    def _in_thread_set_robot_yaml_location(self, robot_yaml) -> None:
        self._locators_db.set_robot_yaml_location(robot_yaml)
Esempio n. 4
0
def test_locators_db_images(tmpdir):
    from robocorp_code.locators_db import LocatorsDB
    import json
    from robocorp_code.locators.locator_protocols import ImageLocatorTypedDict
    from robocorp_code_tests.fixtures import IMAGE_IN_BASE64

    locators_db = LocatorsDB()
    robot_yaml_location = tmpdir.join("robot.yaml")
    locators_db.set_robot_yaml_location(str(robot_yaml_location))
    image_locator: ImageLocatorTypedDict = {
        "path_b64": IMAGE_IN_BASE64,
        "source_b64": IMAGE_IN_BASE64,
        "confidence": 80.0,
        "type": "image",
    }
    locators_db.add_image_locator(image_locator)
    locators_db.add_image_locator(image_locator)

    with open(locators_db.locators_json, "r", encoding="utf-8") as stream:

        assert json.load(stream) == {
            "Image.Locator.00": {
                "confidence": 80.0,
                "path": ".images/Image.Locator.00-path.png",
                "source": ".images/Image.Locator.00-source.png",
                "type": "image",
            },
            "Image.Locator.01": {
                "confidence": 80.0,
                "path": ".images/Image.Locator.01-path.png",
                "source": ".images/Image.Locator.01-source.png",
                "type": "image",
            },
        }

    p = Path(str(tmpdir)) / ".images" / "Image.Locator.01-source.png"
    assert p.exists()

    p = Path(str(tmpdir)) / ".images" / "Image.Locator.02-source.png"
    p.write_text("not empty")
    locators_db.add_image_locator(image_locator)

    with open(locators_db.locators_json, "r", encoding="utf-8") as stream:

        assert json.load(stream) == {
            "Image.Locator.00": {
                "confidence": 80.0,
                "path": ".images/Image.Locator.00-path.png",
                "source": ".images/Image.Locator.00-source.png",
                "type": "image",
            },
            "Image.Locator.01": {
                "confidence": 80.0,
                "path": ".images/Image.Locator.01-path.png",
                "source": ".images/Image.Locator.01-source.png",
                "type": "image",
            },
            # i.e.: skip the Image.Locator.02 because the image is there already
            "Image.Locator.03": {
                "confidence": 80.0,
                "path": ".images/Image.Locator.03-path.png",
                "source": ".images/Image.Locator.03-source.png",
                "type": "image",
            },
        }