From ec38d5f65f53a5f73318bc01312947429cd63bb1 Mon Sep 17 00:00:00 2001 From: Tejas Shasane Date: Tue, 29 Sep 2026 13:33:45 +0530 Subject: [PATCH] Add configurable UPC and RA setup flows --- config.json | 81 ++++++++++++++++++++++-- config/README.md | 22 +++++++ tests/config_reader.py | 20 +++++- tests/conftest.py | 2 +- tests/locators.py | 10 +++ tests/setup/sessions/test_session.py | 7 +- tests/setup/test_combined_setup.py | 21 +++--- tests/setup/tokens/test_trading_token.py | 68 ++++++++++++++++++-- tests/test_setup_config.py | 26 ++++++++ 9 files changed, 228 insertions(+), 29 deletions(-) diff --git a/config.json b/config.json index c832a1c..bac7719 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "url": "https://utilsuat.pxil.in/Pxil/", + "url": "https://utilseastern.pxil.in/Pxil/", "stagingurl": "https://utilseastern.pxil.in/Pxil/", "uaturl": "https://utilsuat.pxil.in/Pxil/", "login": { @@ -19,7 +19,7 @@ }, "products": { "IDAS": { - "enabled": true, + "enabled": false, "me_instance": "55", "session": {}, "token": { @@ -47,7 +47,7 @@ } }, "RTM": { - "enabled": true, + "enabled": false, "run_session": false, "run_token": true, "me_instance": "52", @@ -58,7 +58,9 @@ "token": { "market_code": "NATIONAL", "product_type": "RTM", - "product_codes": ["RTM"], + "product_codes": [ + "RTM" + ], "generation_mode": "RTM", "delivery_offset_days": 0, "delivery_from_time": "00:00", @@ -69,7 +71,64 @@ }, "UPC": { "enabled": false, - "me_instance": "", + "run_session": true, + "run_token": true, + "session_variants": { + "DAILY": { + "enabled": true, + "run_session": false, + "run_token": true, + "me_instance": "58", + "token": { + "generation_mode": "AnyDayUPC", + "create_product_codes": [ + "DailyC_E" + ], + "available_product_codes": [ + "DailyC_D", + "DailyGR_D", + "DailyC_RTC", + "DailyC_E", + "DailyC_M" + ] + } + }, + "WEEKLY": { + "enabled": true, + "run_session": false, + "run_token": true, + "me_instance": "59", + "token": { + "generation_mode": "WeeklyUPC", + "create_product_codes": [ + "WeeklyC_E" + ], + "available_product_codes": [ + "WeeklyC_D", + "WeeklyC_N", + "WeeklyGR_D", + "WeeklyC_RTC" + ] + } + }, + "MONTHLY": { + "enabled": true, + "run_session": true, + "run_token": true, + "me_instance": "60", + "token": { + "generation_mode": "MonthlyUPC", + "create_product_codes": [ + "MonthlyC_N" + ], + "available_product_codes": [ + "MonthlyC_D", + "MonthlyGR_D", + "MonthlyC_RTC" + ] + } + } + }, "session": {}, "token": { "market_code": "NATIONAL", @@ -77,12 +136,20 @@ "product_codes": [], "delivery_offset_days": 1, "expiry_time": "20:30", + "upc_times": { + "order_normal_start_time": "13:00", + "order_normal_end_time": "17:00", + "trade_session_start_time": "17:00", + "trade_session_end_time": "22:00" + }, "time_sessions": {} } }, "RA": { - "enabled": false, - "me_instance": "", + "enabled": true, + "run_session": true, + "run_token": false, + "me_instance": "64", "session": {}, "token": { "market_code": "NATIONAL", diff --git a/config/README.md b/config/README.md index de4ec04..e2b3c17 100644 --- a/config/README.md +++ b/config/README.md @@ -28,3 +28,25 @@ In `runtime.json`: - `IDAS`, `RTM`, and future top-level product sections: product-wise defaults, available codes, supported flows, bidding config, matching config, and future product-specific controls. Regression config now lives entirely inside `runtime.json`. + +## UPC session setup + +Session creation uses the root `config.json`, independently of regression settings. +Set `products.UPC.enabled` to `true` to include UPC. Under +`products.UPC.session_variants`, enable all three variants or only the ones required: + +| Variant | ME instance ID | UI name | +| --- | --- | --- | +| DAILY | 58 | UPC ANYDAY | +| WEEKLY | 59 | UPC WEEKLY | +| MONTHLY | 60 | UPC MONTHLY | + +Each variant has an `enabled` flag. UPC remains disabled by default; all three +variants are preselected for when it is enabled. Set `UPC.run_session` and +`UPC.run_token` to `true` when both session and token creation are required. Each selected variant uses the +existing session creation flow and inherits `setup.session.start_offset_minutes` +and `setup.session.end_time`. Optional `UPC.session` settings override those +defaults, and a variant's `session` settings can override them individually. + +Run session-only setup with `python -m pytest tests/setup/sessions/test_session.py`. +Selected UPC cases appear as `UPC_DAILY`, `UPC_WEEKLY`, and `UPC_MONTHLY`. diff --git a/tests/config_reader.py b/tests/config_reader.py index e53a167..a66e960 100644 --- a/tests/config_reader.py +++ b/tests/config_reader.py @@ -43,12 +43,28 @@ def get_setup_configs(mode, config=None): for name, product in products.items(): if not product.get("enabled", True): continue + variants = product.get("session_variants") + if variants is not None: + resolved = get_product_config(name, config) + if not resolved["setup"].get("run_session", True): + continue + variant_config = deepcopy(config) + variant_config["products"] = {} + for variant_name, variant in variants.items(): + settings = deepcopy(product) + settings.pop("session_variants") + settings.update(variant) + settings["session"] = {**product.get("session", {}), **variant.get("session", {})} + settings["token"] = {**product.get("token", {}), **variant.get("token", {})} + variant_config["products"][f"{name}_{variant_name}"] = settings + selected.extend(get_setup_configs(mode, variant_config)) + continue resolved = get_product_config(name, config) setup = resolved.get("setup", {}) session = setup.get("run_session", True) token = setup.get("run_token", True) matches = { - "combined": session and token, + "combined": session and token or (name.startswith("UPC_") and token), "session": session and not token, "token": token and not session, } @@ -59,7 +75,7 @@ def get_setup_configs(mode, config=None): if token: settings = resolved["token"] if not settings.get("market_code") or not ( - parse_product_codes(settings.get("product_codes")) + parse_product_codes(settings.get("create_product_codes", settings.get("product_codes"))) or parse_product_codes(settings.get("product_code")) ): raise ValueError(f"products.{name}.token requires market_code and product_code(s)") diff --git a/tests/conftest.py b/tests/conftest.py index a3d0b34..d4bdb3c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,7 +50,7 @@ def pytest_configure(config): # ----------------------------- # WebDriver Fixture # ----------------------------- -@pytest.fixture +@pytest.fixture(scope="module") def driver(): driver = create_chrome_driver() diff --git a/tests/locators.py b/tests/locators.py index d0632c5..0d876b3 100644 --- a/tests/locators.py +++ b/tests/locators.py @@ -22,6 +22,7 @@ class Locator(object): tokenandtimesessionview="//a[@href='#/tradingMaintenance/tokenTimeSessionMap' or normalize-space()='Token and Time Session View']" addsessionbtn="//button[contains(text(),'Add')]" addnewtokenbtn="//button[@data-ng-click='addToken()' and normalize-space()='Add New Token']" + tokengeneratorresetbtn = "//button[@data-ng-click='refresh()' and normalize-space()='Reset']" setmeinstance="//span[@aria-label='meInstanceId activate']" #inputmeid="meInstanceId activate" #"//li[@id='ui-select-choices-178']" @@ -35,6 +36,15 @@ class Locator(object): tokendeliverystartdate = "//input[@placeholder='DELIVERY_START_DATE']" tokendeliveryenddate = "//input[@placeholder='DELIVERY_END_DATE']" tokenexpirydate = "//input[@placeholder='Token Expiry Date']" + tokentradedate = "//input[@placeholder='Trade Date']" + upcordernormalstart = "//input[@name='ses3StartTime']" + upcordernormalend = "//input[@name='ses3EndTime']" + upctradesessionstart = "//input[@name='sesStartTime']" + upctradesessionend = "//input[@name='sesEndTime']" + upcorderstart = "//input[@placeholder='Order Normal Start Time']" + upcorderend = "//input[@placeholder='Order Normal End Time']" + upctradestart = "//input[@placeholder='Trade Start Time' or @placeholder='Trade Session Start Time']" + upctradeend = "//input[@placeholder='Trade End Time' or @placeholder='Trade Session End Time']" savetokenbtn = "//button[@data-ng-click='saveToken()' and normalize-space()='save Token']" tokentimesessionbtn = "//button[@data-ng-click='redirectToTimeSession()' and normalize-space()='Time Session']" viewproducttype = "pmProductType" diff --git a/tests/setup/sessions/test_session.py b/tests/setup/sessions/test_session.py index 05a2f6b..dce576b 100644 --- a/tests/setup/sessions/test_session.py +++ b/tests/setup/sessions/test_session.py @@ -5,7 +5,7 @@ from selenium.webdriver.support.ui import WebDriverWait import pytest from tests.step_actions import StepActions -from tests.helpers import login, select_ui_dropdown +from tests.helpers import login, select_ui_dropdown, open_trading_maintenance_page from tests.config_reader import get_setup_configs from tests.locators import Locator @@ -24,9 +24,8 @@ def get_session_window(config): def create_session(driver, step, config): - step.click((By.XPATH, Locator.tradingmaintenance), "trading_maintenance") - - step.click((By.XPATH, Locator.tradingsession), "trading_session") + open_trading_maintenance_page(driver, Locator.tradingsession) + step.capture("trading_session") step.click((By.XPATH, Locator.addsessionbtn), "add_session") diff --git a/tests/setup/test_combined_setup.py b/tests/setup/test_combined_setup.py index c5f6e43..24c5629 100644 --- a/tests/setup/test_combined_setup.py +++ b/tests/setup/test_combined_setup.py @@ -18,22 +18,21 @@ def test_session_and_token_setup(driver, test_context, config): wait = WebDriverWait(driver, 20) step = StepActions(driver, wait, test_context) - driver.get(config["url"]) - step.capture("open_application") + if not getattr(driver, "_pxil_logged_in", False): + driver.get(config["url"]) + step.capture("open_application") - login( - driver, - config["login"]["username"], - config["login"]["password"] - ) - step.capture("login") - save_session_backup(driver, test_context, "login_session_backup") + if not getattr(driver, "_pxil_logged_in", False): + login(driver, config["login"]["username"], config["login"]["password"]) + driver._pxil_logged_in = True + step.capture("login") + save_session_backup(driver, test_context, "login_session_backup") - create_session(driver, step, config) + if config.get("setup", {}).get("run_session", True): + create_session(driver, step, config) for product_code in get_product_codes(token): product_token = dict(token) product_token["product_code"] = product_code run_token_setup(driver, step, product_token) - logout(driver) diff --git a/tests/setup/tokens/test_trading_token.py b/tests/setup/tokens/test_trading_token.py index 4503b1e..7b877ec 100644 --- a/tests/setup/tokens/test_trading_token.py +++ b/tests/setup/tokens/test_trading_token.py @@ -73,7 +73,7 @@ def get_time_session_times(token, row_text, default_start_time, default_end_time def get_product_codes(token): - product_codes = parse_product_codes(token.get("product_codes")) + product_codes = parse_product_codes(token.get("create_product_codes", token.get("product_codes"))) if product_codes: return product_codes @@ -225,7 +225,36 @@ def create_rtm_token(driver, step, token): handle_transient_popups(driver) +def create_upc_token(driver, step, token): + open_trading_maintenance_page(driver, Locator.tokengenerator) + handle_transient_popups(driver) + step.capture("open_token_generator_upc") + fields = [(Locator.generatormarketcode, token["market_code"], "market_code"), (Locator.generatorproductcode, token["product_code"], "product_code")] + if driver.find_elements(By.ID, Locator.generatorgenerationmode): + fields.append((Locator.generatorgenerationmode, token["generation_mode"], "generation_mode")) + for element_id, value, name in fields: + select_bootstrap_dropdown_by_id(driver, element_id, value, exact=True) + step.capture(f"select_{name}_upc") + step.click((By.XPATH, Locator.addnewtokenbtn), "click_add_new_token_upc") + timings = token.get("upc_times", {}) + for name, locator, value in ( + ("order_normal_start", Locator.upcorderstart, timings.get("order_normal_start_time")), + ("order_normal_end", Locator.upcorderend, timings.get("order_normal_end_time")), + ("trade_start", Locator.upctradestart, timings.get("trade_session_start_time")), + ("trade_end", Locator.upctradeend, timings.get("trade_session_end_time")), + ): + if value: + type_first_visible_input(driver, locator, value) + step.capture(f"enter_{name}_upc") + step.click((By.XPATH, Locator.savetokenbtn), "save_token_upc") + handle_transient_popups(driver) + step.click((By.XPATH, Locator.tokengeneratorresetbtn), "reset_token_generator_upc") + + def run_token_setup(driver, step, token): + if token.get("product_type", "").upper() == "UPC": + create_upc_token(driver, step, token) + return if token.get("product_type", "").upper() == "RTM": create_rtm_token(driver, step, token) else: @@ -251,17 +280,48 @@ def create_and_activate_token(driver, step, token): select_labeled_ui_dropdown(driver, "PRODUCT CODE", product_code) step.capture(f"select_product_code_{product_code.lower()}") + generation_mode = token.get("generation_mode") + if generation_mode: + mode_elements = driver.find_elements(By.ID, Locator.generatorgenerationmode) + if mode_elements: + select_bootstrap_dropdown_by_id(driver, Locator.generatorgenerationmode, generation_mode, exact=True) + step.capture(f"select_generation_mode_{product_code.lower()}") + step.click((By.XPATH, Locator.addnewtokenbtn), f"click_add_new_token_{product_code.lower()}") next_day, token_expiry = get_delivery_date_and_expiry(token) - step.type((By.XPATH, Locator.tokendeliverystartdate), next_day, f"enter_delivery_start_date_{product_code.lower()}") - step.type((By.XPATH, Locator.tokendeliveryenddate), next_day, f"enter_delivery_end_date_{product_code.lower()}") - step.type((By.XPATH, Locator.tokenexpirydate), token_expiry, f"enter_token_expiry_date_{product_code.lower()}") + if token.get("product_type", "").upper() == "UPC": + today = datetime.now().strftime("%d/%m/%Y") + if token.get("generation_mode") == "AnyDayUPC": + step.type((By.XPATH, Locator.tokentradedate), today, f"enter_trade_date_{product_code.lower()}") + timings = token.get("upc_times", {}) + normal_start = timings.get("order_normal_start_time") + if normal_start is None and timings.get("order_normal_start_offset_minutes") is not None: + normal_start = (datetime.now() + timedelta(minutes=timings["order_normal_start_offset_minutes"])).strftime("%H:%M") + values = { + Locator.upcordernormalstart: normal_start, + Locator.upcordernormalend: timings.get("order_normal_end_time"), + Locator.upctradesessionstart: timings.get("trade_session_start_time"), + Locator.upctradesessionend: timings.get("trade_session_end_time"), + } + for locator, value in values.items(): + if value is None: + continue + type_first_visible_input(driver, locator, value) + + if token.get("generation_mode") not in ("WeeklyUPC", "MonthlyUPC"): + step.type((By.XPATH, Locator.tokendeliverystartdate), next_day, f"enter_delivery_start_date_{product_code.lower()}") + step.type((By.XPATH, Locator.tokendeliveryenddate), next_day, f"enter_delivery_end_date_{product_code.lower()}") + step.type((By.XPATH, Locator.tokenexpirydate), token_expiry, f"enter_token_expiry_date_{product_code.lower()}") step.click((By.XPATH, Locator.savetokenbtn), f"save_token_{product_code.lower()}") handle_transient_popups(driver) + if token.get("product_type", "").upper() == "UPC": + step.click((By.XPATH, Locator.tokengeneratorresetbtn), f"reset_token_generator_{product_code.lower()}") + return + complete_time_session_edits_from_saved_token(driver, step, token) diff --git a/tests/test_setup_config.py b/tests/test_setup_config.py index c9f4443..36c232e 100644 --- a/tests/test_setup_config.py +++ b/tests/test_setup_config.py @@ -52,3 +52,29 @@ def test_missing_required_settings_fail_before_browser_start(): def test_legacy_config_remains_supported(): config = {"product": {"me_instance": "55"}, "trading_token_idas": {"market_code": "NATIONAL", "product_code": "IDAS4"}} assert get_setup_configs("combined", config)[0]["token"] == config["trading_token_idas"] + + +@pytest.mark.parametrize("selected", [[], ["DAILY"], ["WEEKLY"], ["MONTHLY"], ["DAILY", "WEEKLY", "MONTHLY"]]) +def test_upc_session_selection_and_shared_timing(selected): + config = { + "setup": {"session": {"start_offset_minutes": 7, "end_time": "20:30"}}, + "products": {"UPC": { + "run_session": True, + "run_token": False, + "session_variants": { + name: {"enabled": name in selected, "me_instance": instance} + for name, instance in (("DAILY", "58"), ("WEEKLY", "59"), ("MONTHLY", "60")) + }, + }}, + } + original = deepcopy(config) + sessions = get_setup_configs("session", config) + assert [c["product_name"] for c in sessions] == [f"UPC_{name}" for name in selected] + ids = {"DAILY": "58", "WEEKLY": "59", "MONTHLY": "60"} + assert [c["product"]["me_instance"] for c in sessions] == [ids[name] for name in selected] + assert all(c["setup"]["session"] == config["setup"]["session"] for c in sessions) + assert get_setup_configs("combined", config) == [] + assert get_setup_configs("token", config) == [] + assert config == original + config["products"]["UPC"]["enabled"] = False + assert get_setup_configs("session", config) == []