Add product-specific session and token setup
This commit is contained in:
+41
-1
@@ -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:
|
||||||
|
|
||||||
|
|||||||
+62
-6
@@ -6,21 +6,23 @@
|
|||||||
"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",
|
||||||
@@ -30,7 +32,7 @@
|
|||||||
"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": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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"]
|
||||||
Reference in New Issue
Block a user