Caqui executes commands against Drivers synchronously and asynchronously. The intention is that the user does not worry about which Driver they're using. It can be WebDrivers like Selenium, MobileDrivers like Appium, or DesktopDrivers like Winium. It can also be used in remote calls. The user can start the Driver as a server in any host and provide the URL to Caqui clients.
WebDriver | Version | Remote* | Comment |
---|---|---|---|
Appium | 2.0.0+ | Y | Accepts remote calls by default. Tested with Appium in Docker container |
Firefox (geckodriver) | 113+ | Y | Need to add the host ip, e.g. "--host 123.45.6.78" |
Google Chrome | 113+ | Y | Need to inform the allowed ips to connect, e.g "--allowed-ips=123.45.6.78" |
Opera | 99+ | Y | Need to inform the allowed ips to connect, e.g "--allowed-ips=123.45.6.78". Similar to Google Chrome |
WinAppDriver | 1.2.1+ | Y | Need to define the host ip, e.g. "WinApppage.exe 10.0.0.10 4723" |
Winium Desktop | 1.6.0+ | Y | Accepts remote calls by default |
- Accepts remote requests when running as servers
Install the lastest version of Caqui
pip install caqui
In version 2.0.0+ it is possible to use Python objects similarly to Selenium. Read the API documentation for more information.
Example:
from pytest import mark, fixture
from tests.constants import PAGE_URL
from caqui.easy import AsyncPage
from caqui.by import By
from caqui import synchronous
from caqui.easy.capabilities import ChromeOptionsBuilder
from caqui.easy.options import ChromeOptionsBuilder
from caqui.easy.server import Server
from time import sleep
SERVER_PORT = 9999
SERVER_URL = f"http://localhost:{SERVER_PORT}"
PAGE_URL = "file:///sample.html"
@fixture(autouse=True, scope="session")
def setup_server():
server = Server.get_instance(port=SERVER_PORT)
server.start()
yield
sleep(3)
server.dispose()
@fixture
def setup_environment():
server_url = SERVER_URL
options = ChromeOptionsBuilder().args(["headless"]).to_dict()
capabilities = ChromeCapabilitiesBuilder().accept_insecure_certs(True).add_options(options).to_dict()
page = AsyncPage(server_url, capabilities, PAGE_URL)
yield page
page.quit()
@mark.asyncio
async def test_switch_to_parent_frame_and_click_alert(setup_environment: AsyncPage):
page = setup_environment
await page.get(PAGE_URL)
locator_type = "id"
locator_value = "my-iframe"
locator_value_alert_parent = "alert-button"
locator_value_alert_frame = "alert-button-iframe"
element_frame = await page.find_element(locator_type, locator_value)
assert await page.switch_to.frame(element_frame) is True
alert_button_frame = await page.find_element(locator_type, locator_value_alert_frame)
assert await alert_button_frame.click() is True
assert await page.switch_to.alert.dismiss() is True
assert await page.switch_to.default_content() is True
alert_button_parent = await page.find_element(locator_type, locator_value_alert_parent)
ass
8000
ert await alert_button_parent.get_attribute("any") == "any"
assert await alert_button_parent.click() is True
To execute the test in multiple tasks, use pytest-async-cooperative. It will speed up the execution considerably.
@mark.asyncio_cooperative
async def test_save_screenshot(setup_environment: AsyncPage):
page = setup_environment
assert await page.save_screenshot("/tmp/test.png") is True
@mark.asyncio_cooperative
async def test_object_to_string(setup_environment: AsyncPage):
page = setup_environment
element_string = synchronous.find_element(page.remote, page.session, By.XPATH, "//button")
element = await page.find_element(locator=By.XPATH, value="//button")
assert str(element) == element_string
To run the tests in multiple processes use pytest-xdist. The execution is even faster than running in multiple tasks. Check this article Supercharge Your Web Crawlers with Caqui: Boosting Speed with Multi-Processing to know how to increase the velocity of the executions in 90%.
@mark.asyncio
async def test_save_screenshot(setup_environment: AsyncPage):
page = setup_environment
assert await page.save_screenshot("/tmp/test.png") is True
@mark.asyncio
async def test_object_to_string(setup_environment: AsyncPage):
page = setup_environment
element_string = synchronous.find_element(page.remote, page.session, By.XPATH, "//button")
element = await page.find_element(locator=By.XPATH, value="//button")
assert str(element) == element_string
In case you are using Appium, Winium or other driver not started by the library, just start the driver as a server.
For example. Download the same ChromeDriver version as your installed Chrome and start the Driver as a server using the port "9999"
$ ./chromedriver --port=9999
Starting ChromeDriver 94.0.4606.61 (418b78f5838ed0b1c69bb4e51ea0252171854915-refs/branch-heads/4606@{#1204}) on port 9999
Only local connections are allowed.
Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
ChromeDriver was started successfully.
Caqui depends on Webdriver Manager that can be configured independenly and has some limitations. Check the project documentation for more information.
Read the Code of Conduct before push new Merge Requests. Now, follow the steps in Contributing session.