Add configurable UPC and RA setup flows

This commit is contained in:
2026-09-29 13:33:45 +05:30
parent 97158d2006
commit ec38d5f65f
9 changed files with 228 additions and 29 deletions
+74 -7
View File
@@ -1,5 +1,5 @@
{ {
"url": "https://utilsuat.pxil.in/Pxil/", "url": "https://utilseastern.pxil.in/Pxil/",
"stagingurl": "https://utilseastern.pxil.in/Pxil/", "stagingurl": "https://utilseastern.pxil.in/Pxil/",
"uaturl": "https://utilsuat.pxil.in/Pxil/", "uaturl": "https://utilsuat.pxil.in/Pxil/",
"login": { "login": {
@@ -19,7 +19,7 @@
}, },
"products": { "products": {
"IDAS": { "IDAS": {
"enabled": true, "enabled": false,
"me_instance": "55", "me_instance": "55",
"session": {}, "session": {},
"token": { "token": {
@@ -47,7 +47,7 @@
} }
}, },
"RTM": { "RTM": {
"enabled": true, "enabled": false,
"run_session": false, "run_session": false,
"run_token": true, "run_token": true,
"me_instance": "52", "me_instance": "52",
@@ -58,7 +58,9 @@
"token": { "token": {
"market_code": "NATIONAL", "market_code": "NATIONAL",
"product_type": "RTM", "product_type": "RTM",
"product_codes": ["RTM"], "product_codes": [
"RTM"
],
"generation_mode": "RTM", "generation_mode": "RTM",
"delivery_offset_days": 0, "delivery_offset_days": 0,
"delivery_from_time": "00:00", "delivery_from_time": "00:00",
@@ -69,7 +71,64 @@
}, },
"UPC": { "UPC": {
"enabled": false, "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": {}, "session": {},
"token": { "token": {
"market_code": "NATIONAL", "market_code": "NATIONAL",
@@ -77,12 +136,20 @@
"product_codes": [], "product_codes": [],
"delivery_offset_days": 1, "delivery_offset_days": 1,
"expiry_time": "20:30", "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": {} "time_sessions": {}
} }
}, },
"RA": { "RA": {
"enabled": false, "enabled": true,
"me_instance": "", "run_session": true,
"run_token": false,
"me_instance": "64",
"session": {}, "session": {},
"token": { "token": {
"market_code": "NATIONAL", "market_code": "NATIONAL",
+22
View File
@@ -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. - `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`. 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`.
+18 -2
View File
@@ -43,12 +43,28 @@ def get_setup_configs(mode, config=None):
for name, product in products.items(): for name, product in products.items():
if not product.get("enabled", True): if not product.get("enabled", True):
continue 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) resolved = get_product_config(name, config)
setup = resolved.get("setup", {}) setup = resolved.get("setup", {})
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, "combined": session and token or (name.startswith("UPC_") and token),
"session": session and not token, "session": session and not token,
"token": token and not session, "token": token and not session,
} }
@@ -59,7 +75,7 @@ def get_setup_configs(mode, config=None):
if token: if token:
settings = resolved["token"] settings = resolved["token"]
if not settings.get("market_code") or not ( 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")) or parse_product_codes(settings.get("product_code"))
): ):
raise ValueError(f"products.{name}.token requires market_code and product_code(s)") raise ValueError(f"products.{name}.token requires market_code and product_code(s)")
+1 -1
View File
@@ -50,7 +50,7 @@ def pytest_configure(config):
# ----------------------------- # -----------------------------
# WebDriver Fixture # WebDriver Fixture
# ----------------------------- # -----------------------------
@pytest.fixture @pytest.fixture(scope="module")
def driver(): def driver():
driver = create_chrome_driver() driver = create_chrome_driver()
+10
View File
@@ -22,6 +22,7 @@ class Locator(object):
tokenandtimesessionview="//a[@href='#/tradingMaintenance/tokenTimeSessionMap' or normalize-space()='Token and Time Session View']" tokenandtimesessionview="//a[@href='#/tradingMaintenance/tokenTimeSessionMap' or normalize-space()='Token and Time Session View']"
addsessionbtn="//button[contains(text(),'Add')]" addsessionbtn="//button[contains(text(),'Add')]"
addnewtokenbtn="//button[@data-ng-click='addToken()' and normalize-space()='Add New Token']" 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']" setmeinstance="//span[@aria-label='meInstanceId activate']"
#inputmeid="meInstanceId activate" #inputmeid="meInstanceId activate"
#"//li[@id='ui-select-choices-178']" #"//li[@id='ui-select-choices-178']"
@@ -35,6 +36,15 @@ class Locator(object):
tokendeliverystartdate = "//input[@placeholder='DELIVERY_START_DATE']" tokendeliverystartdate = "//input[@placeholder='DELIVERY_START_DATE']"
tokendeliveryenddate = "//input[@placeholder='DELIVERY_END_DATE']" tokendeliveryenddate = "//input[@placeholder='DELIVERY_END_DATE']"
tokenexpirydate = "//input[@placeholder='Token Expiry 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']" savetokenbtn = "//button[@data-ng-click='saveToken()' and normalize-space()='save Token']"
tokentimesessionbtn = "//button[@data-ng-click='redirectToTimeSession()' and normalize-space()='Time Session']" tokentimesessionbtn = "//button[@data-ng-click='redirectToTimeSession()' and normalize-space()='Time Session']"
viewproducttype = "pmProductType" viewproducttype = "pmProductType"
+3 -4
View File
@@ -5,7 +5,7 @@ from selenium.webdriver.support.ui import WebDriverWait
import pytest import pytest
from tests.step_actions import StepActions 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.config_reader import get_setup_configs
from tests.locators import Locator from tests.locators import Locator
@@ -24,9 +24,8 @@ def get_session_window(config):
def create_session(driver, step, config): def create_session(driver, step, config):
step.click((By.XPATH, Locator.tradingmaintenance), "trading_maintenance") open_trading_maintenance_page(driver, Locator.tradingsession)
step.capture("trading_session")
step.click((By.XPATH, Locator.tradingsession), "trading_session")
step.click((By.XPATH, Locator.addsessionbtn), "add_session") step.click((By.XPATH, Locator.addsessionbtn), "add_session")
+5 -6
View File
@@ -18,17 +18,17 @@ def test_session_and_token_setup(driver, test_context, config):
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, 20)
step = StepActions(driver, wait, test_context) step = StepActions(driver, wait, test_context)
if not getattr(driver, "_pxil_logged_in", False):
driver.get(config["url"]) driver.get(config["url"])
step.capture("open_application") step.capture("open_application")
login( if not getattr(driver, "_pxil_logged_in", False):
driver, login(driver, config["login"]["username"], config["login"]["password"])
config["login"]["username"], driver._pxil_logged_in = True
config["login"]["password"]
)
step.capture("login") step.capture("login")
save_session_backup(driver, test_context, "login_session_backup") save_session_backup(driver, test_context, "login_session_backup")
if config.get("setup", {}).get("run_session", True):
create_session(driver, step, config) create_session(driver, step, config)
for product_code in get_product_codes(token): for product_code in get_product_codes(token):
@@ -36,4 +36,3 @@ def test_session_and_token_setup(driver, test_context, config):
product_token["product_code"] = product_code product_token["product_code"] = product_code
run_token_setup(driver, step, product_token) run_token_setup(driver, step, product_token)
logout(driver)
+61 -1
View File
@@ -73,7 +73,7 @@ def get_time_session_times(token, row_text, default_start_time, default_end_time
def get_product_codes(token): 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: if product_codes:
return product_codes return product_codes
@@ -225,7 +225,36 @@ def create_rtm_token(driver, step, token):
handle_transient_popups(driver) 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): 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": if token.get("product_type", "").upper() == "RTM":
create_rtm_token(driver, step, token) create_rtm_token(driver, step, token)
else: else:
@@ -251,10 +280,37 @@ def create_and_activate_token(driver, step, token):
select_labeled_ui_dropdown(driver, "PRODUCT CODE", product_code) select_labeled_ui_dropdown(driver, "PRODUCT CODE", product_code)
step.capture(f"select_product_code_{product_code.lower()}") 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()}") step.click((By.XPATH, Locator.addnewtokenbtn), f"click_add_new_token_{product_code.lower()}")
next_day, token_expiry = get_delivery_date_and_expiry(token) next_day, token_expiry = get_delivery_date_and_expiry(token)
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.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.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.type((By.XPATH, Locator.tokenexpirydate), token_expiry, f"enter_token_expiry_date_{product_code.lower()}")
@@ -262,6 +318,10 @@ def create_and_activate_token(driver, step, token):
step.click((By.XPATH, Locator.savetokenbtn), f"save_token_{product_code.lower()}") step.click((By.XPATH, Locator.savetokenbtn), f"save_token_{product_code.lower()}")
handle_transient_popups(driver) 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) complete_time_session_edits_from_saved_token(driver, step, token)
+26
View File
@@ -52,3 +52,29 @@ def test_missing_required_settings_fail_before_browser_start():
def test_legacy_config_remains_supported(): def test_legacy_config_remains_supported():
config = {"product": {"me_instance": "55"}, "trading_token_idas": {"market_code": "NATIONAL", "product_code": "IDAS4"}} 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"] 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) == []