Development #11

Merged
tejasshasane merged 8 commits from development into master 2026-11-02 08:19:57 +01:00
8 changed files with 76 additions and 23 deletions
Showing only changes of commit d9218709f0 - Show all commits
+5 -5
View File
@@ -1,14 +1,14 @@
# Core test runner # Core test runner
pytest==8.4.1 pytest==9.1.1
pytest-html==4.2.0 pytest-html==4.2.0
# Browser automation # Browser automation
selenium==4.34.2 selenium==4.50.0
# CAPTCHA OCR and image processing # CAPTCHA OCR and image processing
easyocr==1.7.2 easyocr==1.7.2
opencv-python==4.12.0.88 opencv-python==5.0.0.93
Pillow==11.3.0 Pillow==12.3.0
numpy==2.2.6 numpy==2.5.3
torch==2.14.1 torch==2.14.1
torchvision==0.29.1 torchvision==0.29.1
+4 -1
View File
@@ -67,7 +67,10 @@ def get_setup_configs(mode, config=None):
session = setup.get("run_session", True) session = setup.get("run_session", True)
token = setup.get("run_token", True) token = setup.get("run_token", True)
matches = { matches = {
"combined": session and token or (name.startswith("UPC_") and token), # The full setup run owns every enabled product that has work to do.
# Products without tokens (for example RA) still belong in this
# shared browser flow; the combined test skips only their token step.
"combined": session or token,
"session": session and not token, "session": session and not token,
"token": token and not session, "token": token and not session,
} }
+24
View File
@@ -47,6 +47,30 @@ def pytest_configure(config):
config.option.self_contained_html = True config.option.self_contained_html = True
def pytest_collection_modifyitems(config, items):
"""Keep the full setup run limited to the shared combined flow."""
invocation = " ".join(
str(arg).replace("\\", "/") for arg in config.invocation_params.args
).lower()
full_setup_run = (
"tests/setup" in invocation
and "tests/setup/sessions" not in invocation
and "tests/setup/tokens" not in invocation
and "test_session.py" not in invocation
and "test_trading_token.py" not in invocation
)
if not full_setup_run:
return
deselected = [
item for item in items
if "/tests/setup/sessions/" in f"/{item.nodeid.replace('\\', '/')}"
or "/tests/setup/tokens/" in f"/{item.nodeid.replace('\\', '/')}"
]
items[:] = [item for item in items if item not in deselected]
config.hook.pytest_deselected(items=deselected)
# ----------------------------- # -----------------------------
# WebDriver Fixture # WebDriver Fixture
# ----------------------------- # -----------------------------
+21 -10
View File
@@ -482,23 +482,34 @@ def open_trading_maintenance_page(driver, menu_xpath):
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, 20)
trading_maintenance = wait.until( def visible_elements(xpath):
EC.presence_of_element_located((By.XPATH, Locator.tradingmaintenance)) return [element for element in driver.find_elements(By.XPATH, xpath) if element.is_displayed()]
)
driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", trading_maintenance)
submenu = driver.find_elements(By.XPATH, menu_xpath) submenu_visible = any(
submenu_visible = any(item.is_displayed() for item in submenu) item.is_enabled() for item in visible_elements(menu_xpath)
)
if not submenu_visible: if not submenu_visible:
maintenance_items = visible_elements(Locator.tradingmaintenance)
if not maintenance_items:
wait.until(lambda current_driver: visible_elements(Locator.tradingmaintenance))
maintenance_items = visible_elements(Locator.tradingmaintenance)
maintenance = maintenance_items[0]
driver.execute_script(
"arguments[0].scrollIntoView({block: 'center'});", maintenance
)
try: try:
wait.until(EC.element_to_be_clickable((By.XPATH, Locator.tradingmaintenance))) wait.until(lambda current_driver: maintenance.is_enabled())
trading_maintenance.click() maintenance.click()
except Exception: except Exception:
driver.execute_script("arguments[0].click();", trading_maintenance) driver.execute_script("arguments[0].click();", maintenance)
menu_item = wait.until( menu_item = wait.until(
EC.element_to_be_clickable((By.XPATH, menu_xpath)) lambda current_driver: next(
(item for item in visible_elements(menu_xpath) if item.is_enabled()),
False,
)
) )
try: try:
menu_item.click() menu_item.click()
+3 -1
View File
@@ -8,7 +8,9 @@ class Locator(object):
tacexpiryclosebtn="//div[contains(@class,'modal') and contains(@style,'display: block')]//button[contains(@class,'close')]" tacexpiryclosebtn="//div[contains(@class,'modal') and contains(@style,'display: block')]//button[contains(@class,'close')]"
usermenutoggle="//li[contains(@class,'dropdown') and contains(@class,'navbar-user')]//a[contains(@class,'dropdown-toggle') and @data-toggle='dropdown']" usermenutoggle="//li[contains(@class,'dropdown') and contains(@class,'navbar-user')]//a[contains(@class,'dropdown-toggle') and @data-toggle='dropdown']"
logoutlink="//li[contains(@class,'dropdown') and contains(@class,'navbar-user')]//ul[contains(@class,'dropdown-menu')]//a[@ng-click='logout()' and normalize-space()='Log Out']" logoutlink="//li[contains(@class,'dropdown') and contains(@class,'navbar-user')]//ul[contains(@class,'dropdown-menu')]//a[@ng-click='logout()' and normalize-space()='Log Out']"
tradingmaintenance="//*[contains(normalize-space(), 'TRADING MAINTENANCE')]" # Click the Angular menu anchor, not the nested label span. The anchor
# owns openSubMenu(item), which is required to render its child links.
tradingmaintenance="//a[.//span[normalize-space()='TRADING MAINTENANCE']]"
tradingsession="//a[normalize-space()='Trading Session Master']" tradingsession="//a[normalize-space()='Trading Session Master']"
tokengeneration="//a[@href='#/tradingMaintenance/generateToken' or normalize-space()='Token Generation']" tokengeneration="//a[@href='#/tradingMaintenance/generateToken' or normalize-space()='Token Generation']"
tokengenerator = "//a[@href='#/tradingMaintenance/tokenGenerator']" tokengenerator = "//a[@href='#/tradingMaintenance/tokenGenerator']"
+7 -1
View File
@@ -44,7 +44,13 @@ def create_session(driver, step, config):
@pytest.mark.session @pytest.mark.session
@pytest.mark.parametrize("config", get_setup_configs("session"), ids=lambda c: c["product_name"]) @pytest.mark.parametrize("config", get_setup_configs("session"), ids=lambda c: c["product_name"])
def test_session(driver, test_context, config): def test_session(driver, test_context, config, request):
# The full setup command runs session-only products (such as RA) through
# test_combined_setup.py so they share the same browser/login context.
invocation = " ".join(str(arg).replace("\\", "/") for arg in request.config.invocation_params.args)
if "tests/setup" in invocation and "tests/setup/sessions" not in invocation and "test_session.py" not in invocation:
pytest.skip("Handled by the shared full setup flow")
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, 20)
+1
View File
@@ -31,6 +31,7 @@ def test_session_and_token_setup(driver, test_context, config):
if config.get("setup", {}).get("run_session", True): if config.get("setup", {}).get("run_session", True):
create_session(driver, step, config) create_session(driver, step, config)
if config.get("setup", {}).get("run_token", True):
for product_code in get_product_codes(token): for product_code in get_product_codes(token):
product_token = dict(token) product_token = dict(token)
product_token["product_code"] = product_code product_token["product_code"] = product_code
+7 -1
View File
@@ -339,7 +339,13 @@ def create_and_activate_token(driver, step, token):
@pytest.mark.trading_token @pytest.mark.trading_token
@pytest.mark.parametrize("config", get_setup_configs("token"), ids=lambda c: c["product_name"]) @pytest.mark.parametrize("config", get_setup_configs("token"), ids=lambda c: c["product_name"])
def test_trading_token_creation(driver, test_context, config): def test_trading_token_creation(driver, test_context, config, request):
# The full setup command runs token-enabled products through the combined
# flow so session and token creation use one browser/login context.
invocation = " ".join(str(arg).replace("\\", "/") for arg in request.config.invocation_params.args)
if "tests/setup" in invocation and "tests/setup/tokens" not in invocation and "test_trading_token.py" not in invocation:
pytest.skip("Handled by the shared full setup flow")
config, step, token = get_token_context(driver, test_context, config) config, step, token = get_token_context(driver, test_context, config)
open_app_and_login(driver, step, config) open_app_and_login(driver, step, config)