Add product-specific session and token setup

This commit is contained in:
2026-09-23 09:27:55 +05:30
parent 4749efe45f
commit 9d16b20eb4
8 changed files with 249 additions and 66 deletions
+41 -1
View File
@@ -11,6 +11,46 @@ cd "C:\Selenium Test"
Edit `config.json` before running. Edit `config.json` before running.
Common URL, login credentials, login waits, and step delay stay at the top level.
`setup.run_session` and `setup.run_token` are defaults for every enabled product.
`setup.session` provides common session timing defaults.
`products` contains separate `IDAS`, `RTM`, `UPC`, `RA`, and `REC` sections.
Each product has its own `me_instance`, optional `session` timing overrides, and
`token` settings (codes, delivery offset, expiry, and time sessions).
Existing IDAS settings are preserved. Other product templates are disabled until
you fill in their actual ME instance/token codes and set `enabled` to `true`.
Additional products can be added using the same structure.
To override the defaults for one product, add flags directly inside that product:
```json
"RTM": {
"enabled": true,
"run_session": false,
"run_token": true,
"me_instance": "",
"token": {
"market_code": "NATIONAL",
"product_type": "RTM",
"product_codes": "YOUR_ACTUAL_RTM_CODE"
}
}
```
Remove a product's `run_session` or `run_token` key to inherit that common flag
again. Explicit `false` overrides a common `true`; explicit `true` overrides a
common `false`. Both false skips that product; `enabled: false` always skips it.
Session timing overrides inherit any fields omitted from `setup.session`.
Token timing settings belong to each product independently.
Use `pytest tests\setup -s` to run all enabled products. Each product runs in its
own browser: both flags true runs session then tokens in one login; otherwise
only its enabled action runs. Test/report names include the product name.
The session and token subfolder commands below select only products configured
for session-only and token-only respectively; use the full setup command for a
mixture of modes.
Session and token together in one login: Session and token together in one login:
```json ```json
@@ -69,7 +109,7 @@ Run only token setup:
pytest tests\setup\tokens -s --html=reports/html/token_run.html --self-contained-html pytest tests\setup\tokens -s --html=reports/html/token_run.html --self-contained-html
``` ```
Use this when `run_token` is `true`. It will create one token or multiple tokens based on `trading_token_idas.product_codes`. Use this when `run_token` is `true`. It will create one token or multiple tokens based on `products.<product>.token.product_codes`.
Run only session setup: Run only session setup:
+63 -7
View File
@@ -6,31 +6,33 @@
"username": "Tejas", "username": "Tejas",
"password": "Pxil@123" "password": "Pxil@123"
}, },
"product": {
"me_instance": "55"
},
"login_captcha_wait_seconds": 10, "login_captcha_wait_seconds": 10,
"login_post_submit_wait_seconds": 10, "login_post_submit_wait_seconds": 10,
"step_delay_seconds": 1, "step_delay_seconds": 1,
"setup": { "setup": {
"run_session": false, "run_session": true,
"run_token": true, "run_token": true,
"session": { "session": {
"start_offset_minutes": 7, "start_offset_minutes": 7,
"end_time": "20:30" "end_time": "20:30"
} }
}, },
"trading_token_idas": { "products": {
"IDAS": {
"enabled": true,
"me_instance": "55",
"session": {},
"token": {
"market_code": "NATIONAL", "market_code": "NATIONAL",
"product_type": "IDAS", "product_type": "IDAS",
"product_code": "IDAS4", "product_code": "IDAS4",
"product_codes": "IDAS,IDASTEST01,IDASTEST02,IDASTEST03,IDASTEST04,IDASTEST05,IDASTEST06", "product_codes": "IDAS,IDASTEST01,IDASTEST02,IDASTEST03,IDASTEST04,IDASTEST05,IDASTEST06",
"available_codes":",IDASTEST06,IDASTEST07,IDASTEST08,IDASTEST09,IDASTEST10,IDASTEST11,IDASTEST12,IDASTEST13,IDASTEST14,IDASTEST15,IDASTEST16,IDASTEST17,IDASTEST18,IDASTEST19,IDASTEST20,IDASTEST21,IDASTEST22,IDASTEST23,IDASTEST24,IDASTEST25,IDASTEST26,IDASTEST27,IDASTEST28,IDASTEST29,IDASTEST30", "available_codes": ",IDASTEST06,IDASTEST07,IDASTEST08,IDASTEST09,IDASTEST10,IDASTEST11,IDASTEST12,IDASTEST13,IDASTEST14,IDASTEST15,IDASTEST16,IDASTEST17,IDASTEST18,IDASTEST19,IDASTEST20,IDASTEST21,IDASTEST22,IDASTEST23,IDASTEST24,IDASTEST25,IDASTEST26,IDASTEST27,IDASTEST28,IDASTEST29,IDASTEST30",
"delivery_offset_days": 1, "delivery_offset_days": 1,
"expiry_time": "20:30", "expiry_time": "20:30",
"time_sessions": { "time_sessions": {
"ORDER_NOR": { "ORDER_NOR": {
"start_offset_minutes": 5, "start_offset_minutes": 7,
"end_time": "19:30" "end_time": "19:30"
}, },
"ORDER_EXCH": { "ORDER_EXCH": {
@@ -43,4 +45,58 @@
} }
} }
} }
},
"RTM": {
"enabled": false,
"me_instance": "",
"session": {},
"token": {
"market_code": "NATIONAL",
"product_type": "RTM",
"product_codes": [],
"delivery_offset_days": 1,
"expiry_time": "20:30",
"time_sessions": {}
}
},
"UPC": {
"enabled": false,
"me_instance": "",
"session": {},
"token": {
"market_code": "NATIONAL",
"product_type": "UPC",
"product_codes": [],
"delivery_offset_days": 1,
"expiry_time": "20:30",
"time_sessions": {}
}
},
"RA": {
"enabled": false,
"me_instance": "",
"session": {},
"token": {
"market_code": "NATIONAL",
"product_type": "RA",
"product_codes": [],
"delivery_offset_days": 1,
"expiry_time": "20:30",
"time_sessions": {}
}
},
"REC": {
"enabled": false,
"me_instance": "",
"session": {},
"token": {
"market_code": "NATIONAL",
"product_type": "REC",
"product_codes": [],
"delivery_offset_days": 1,
"expiry_time": "20:30",
"time_sessions": {}
}
}
}
} }
+56
View File
@@ -1,4 +1,5 @@
import json import json
from copy import deepcopy
from pathlib import Path from pathlib import Path
@@ -11,6 +12,61 @@ def load_config():
return json.load(f) return json.load(f)
def get_product_config(product_name, config=None):
"""Resolve a product's setup flags and session defaults without mutating input."""
config = deepcopy(load_config() if config is None else config)
if "products" not in config:
if product_name != "IDAS":
raise ValueError(f"Unknown setup product: {product_name}")
config["token"] = config.get("trading_token_idas", {})
else:
product = config["products"][product_name]
setup = config.setdefault("setup", {})
for flag in ("run_session", "run_token"):
setup[flag] = product.get(flag, setup.get(flag, True))
if not isinstance(setup[flag], bool):
raise ValueError(f"products.{product_name}.{flag} must be true or false")
setup["session"] = {**setup.get("session", {}), **product.get("session", {})}
config["product"] = {"me_instance": product.get("me_instance")}
config["token"] = deepcopy(product.get("token", {}))
config["product_name"] = product_name
return config
def get_setup_configs(mode, config=None):
"""Partition products so a full setup run never creates a product twice."""
if mode not in ("combined", "session", "token"):
raise ValueError(f"Unknown setup mode: {mode}")
config = load_config() if config is None else config
products = config.get("products", {"IDAS": {}})
selected = []
for name, product in products.items():
if not product.get("enabled", True):
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,
"session": session and not token,
"token": token and not session,
}
if not matches[mode]:
continue
if session and not resolved.get("product", {}).get("me_instance"):
raise ValueError(f"products.{name}.me_instance is required for session creation")
if token:
settings = resolved["token"]
if not settings.get("market_code") or not (
parse_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)")
selected.append(resolved)
return selected
def get_step_delay_seconds(default=5): def get_step_delay_seconds(default=5):
config = load_config() config = load_config()
+3 -10
View File
@@ -6,16 +6,10 @@ 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
from tests.config_reader import is_combined_setup_enabled, is_setup_session_enabled, load_config from tests.config_reader import get_setup_configs
from tests.locators import Locator from tests.locators import Locator
pytestmark = pytest.mark.skipif(
is_combined_setup_enabled() or not is_setup_session_enabled(),
reason="Standalone session setup is disabled or runs inside combined setup",
)
def get_session_window(config): def get_session_window(config):
session_config = config.get("setup", {}).get("session", {}) session_config = config.get("setup", {}).get("session", {})
@@ -50,9 +44,8 @@ def create_session(driver, step, config):
@pytest.mark.session @pytest.mark.session
def test_session(driver, test_context): @pytest.mark.parametrize("config", get_setup_configs("session"), ids=lambda c: c["product_name"])
def test_session(driver, test_context, config):
config = load_config()
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, 20)
+4 -11
View File
@@ -1,26 +1,19 @@
import pytest import pytest
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from tests.config_reader import is_combined_setup_enabled, load_config from tests.config_reader import get_setup_configs
from tests.helpers import login, logout, save_session_backup from tests.helpers import login, logout, save_session_backup
from tests.setup.sessions.test_session import create_session from tests.setup.sessions.test_session import create_session
from tests.setup.tokens.test_trading_token import create_and_activate_token, get_product_codes from tests.setup.tokens.test_trading_token import create_and_activate_token, get_product_codes
from tests.step_actions import StepActions from tests.step_actions import StepActions
pytestmark = pytest.mark.skipif(
not is_combined_setup_enabled(),
reason="Combined setup requires setup.run_session and setup.run_token to both be true",
)
@pytest.mark.session @pytest.mark.session
@pytest.mark.trading_token @pytest.mark.trading_token
def test_session_and_token_setup(driver, test_context): @pytest.mark.parametrize("config", get_setup_configs("combined"), ids=lambda c: c["product_name"])
def test_session_and_token_setup(driver, test_context, config):
config = load_config() token = config["token"]
token = config.get("trading_token_idas")
assert token, "Add trading_token_idas in config.json before running token setup."
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, 20)
step = StepActions(driver, wait, test_context) step = StepActions(driver, wait, test_context)
+8 -17
View File
@@ -4,7 +4,7 @@ import pytest
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from tests.config_reader import is_combined_setup_enabled, is_setup_token_enabled, load_config, parse_product_codes from tests.config_reader import get_setup_configs, parse_product_codes
from tests.helpers import ( from tests.helpers import (
click_visible_menu_item, click_visible_menu_item,
click_first_grid_button, click_first_grid_button,
@@ -23,20 +23,10 @@ from tests.locators import Locator
from tests.step_actions import StepActions from tests.step_actions import StepActions
pytestmark = pytest.mark.skipif( def get_token_context(driver, test_context, config):
is_combined_setup_enabled() or not is_setup_token_enabled(),
reason="Standalone token setup is disabled or runs inside combined setup",
)
def get_token_context(driver, test_context):
config = load_config()
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, 20)
step = StepActions(driver, wait, test_context) step = StepActions(driver, wait, test_context)
token = config.get("trading_token_idas") token = config["token"]
assert token, "Add trading_token_idas in config.json before running this test."
return config, step, token return config, step, token
@@ -88,7 +78,7 @@ def get_product_codes(token):
return product_codes return product_codes
product_code = token.get("product_code") product_code = token.get("product_code")
assert product_code, "Add product_code or product_codes in config.json trading_token_idas." assert product_code, "Add product_code or product_codes in config.json products.<product>.token."
return parse_product_codes(product_code) return parse_product_codes(product_code)
@@ -224,9 +214,10 @@ def create_and_activate_token(driver, step, token):
@pytest.mark.trading_token @pytest.mark.trading_token
def test_trading_token_creation_idas(driver, test_context): @pytest.mark.parametrize("config", get_setup_configs("token"), ids=lambda c: c["product_name"])
def test_trading_token_creation(driver, test_context, config):
config, step, token = get_token_context(driver, test_context) config, step, token = get_token_context(driver, test_context, config)
open_app_and_login(driver, step, config) open_app_and_login(driver, step, config)
save_session_backup(driver, test_context, "login_session_backup") save_session_backup(driver, test_context, "login_session_backup")
@@ -241,7 +232,7 @@ def test_trading_token_creation_idas(driver, test_context):
# @pytest.mark.trading_token # @pytest.mark.trading_token
# def test_trading_token_time_session_edit_idas(driver, test_context): # def test_trading_token_time_session_edit_idas(driver, test_context):
# #
# config, step, token = get_token_context(driver, test_context) # config, step, token = get_token_context(driver, test_context, config)
# open_app_and_login(driver, step, config) # open_app_and_login(driver, step, config)
# #
# next_day, _ = get_delivery_date_and_expiry(token) # next_day, _ = get_delivery_date_and_expiry(token)
+3 -3
View File
@@ -5,7 +5,7 @@ import pytest
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from .config_reader import load_config from .config_reader import get_product_config
from .helpers import ( from .helpers import (
clear_pending_console_input, clear_pending_console_input,
click_page_body, click_page_body,
@@ -23,7 +23,7 @@ from .step_actions import StepActions
def _bounce_token_generation(driver, step, config): def _bounce_token_generation(driver, step, config):
token = config["trading_token_idas"] token = config["token"]
interactions = random.randint(2, 4) interactions = random.randint(2, 4)
open_trading_maintenance_page(driver, Locator.tokengeneration) open_trading_maintenance_page(driver, Locator.tokengeneration)
@@ -67,7 +67,7 @@ def _bounce_trading_session(driver, step, config):
@pytest.mark.fake_activity @pytest.mark.fake_activity
def test_random_fake_activity_no_save(driver, test_context): def test_random_fake_activity_no_save(driver, test_context):
config = load_config() config = get_product_config("IDAS")
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, 20)
step = StepActions(driver, wait, test_context) step = StepActions(driver, wait, test_context)
+54
View File
@@ -0,0 +1,54 @@
from copy import deepcopy
import pytest
from tests.config_reader import get_product_config, get_setup_configs
@pytest.mark.parametrize("common_session", [False, True])
@pytest.mark.parametrize("common_token", [False, True])
@pytest.mark.parametrize("session_override", [None, False, True])
@pytest.mark.parametrize("token_override", [None, False, True])
def test_product_flags_inherit_or_override(common_session, common_token, session_override, token_override):
product = {"me_instance": "55", "token": {"market_code": "NATIONAL", "product_codes": "TEST"}}
if session_override is not None:
product["run_session"] = session_override
if token_override is not None:
product["run_token"] = token_override
config = {"setup": {"run_session": common_session, "run_token": common_token}, "products": {"RTM": product}}
session = common_session if session_override is None else session_override
token = common_token if token_override is None else token_override
expected = "combined" if session and token else "session" if session else "token" if token else None
for mode in ("combined", "session", "token"):
assert len(get_setup_configs(mode, config)) == int(mode == expected)
def test_mixed_products_defaults_and_no_mutation():
config = {
"setup": {"run_session": True, "run_token": False, "session": {"start_offset_minutes": 7, "end_time": "20:30"}},
"products": {
"IDAS": {"me_instance": "55", "session": {"end_time": "21:00"}},
"RTM": {"run_session": False, "run_token": True, "token": {"market_code": "NATIONAL", "product_codes": ["RTM1"]}},
"REC": {"enabled": False},
"RA": {"run_session": False},
},
}
original = deepcopy(config)
assert [c["product_name"] for c in get_setup_configs("session", config)] == ["IDAS"]
assert [c["product_name"] for c in get_setup_configs("token", config)] == ["RTM"]
assert get_setup_configs("combined", config) == []
resolved = get_product_config("IDAS", config)
assert resolved["setup"]["session"] == {"start_offset_minutes": 7, "end_time": "21:00"}
assert config == original
def test_missing_required_settings_fail_before_browser_start():
with pytest.raises(ValueError, match="me_instance"):
get_setup_configs("session", {"setup": {"run_token": False}, "products": {"REC": {}}})
with pytest.raises(ValueError, match="market_code"):
get_setup_configs("token", {"setup": {"run_session": False}, "products": {"REC": {}}})
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"]