Add RTM session and configurable token generator flow
This commit is contained in:
+34
-4
@@ -18,7 +18,27 @@ Common URL, login credentials, login waits, and step delay stay at the top level
|
|||||||
`products` contains separate `IDAS`, `RTM`, `UPC`, `RA`, and `REC` sections.
|
`products` contains separate `IDAS`, `RTM`, `UPC`, `RA`, and `REC` sections.
|
||||||
Each product has its own `me_instance`, optional `session` timing overrides, and
|
Each product has its own `me_instance`, optional `session` timing overrides, and
|
||||||
`token` settings (codes, delivery offset, expiry, and time sessions).
|
`token` settings (codes, delivery offset, expiry, and time sessions).
|
||||||
Existing IDAS settings are preserved. Other product templates are disabled until
|
RTM currently runs token creation only (`run_session: false`, `run_token: true`).
|
||||||
|
If enabled again, session creation uses ME instance
|
||||||
|
`52` (`52:RTM`). It uses today's date plus a 7-minute start offset and today's
|
||||||
|
`20:30` end time, matching the current IDAS session end-time default. Change
|
||||||
|
`products.RTM.session.start_offset_minutes` and `products.RTM.session.end_time`
|
||||||
|
to configure RTM independently. The shared login, navigation, and Save steps apply
|
||||||
|
to every product. RTM token automation opens Token Generator,
|
||||||
|
selects `NATIONAL`, product code `RTM`, and generation mode `RTM`, then clicks
|
||||||
|
Add New Token. Product Type is populated automatically. It fills delivery dates
|
||||||
|
with today's `00:00` and `23:59`, session start with current time plus 5 minutes,
|
||||||
|
and session end with `20:00`, then clicks Save Token. All four timings are
|
||||||
|
configured under `products.RTM.token` as shown below. `delivery_offset_days: 0`
|
||||||
|
means today. RTM activation is not part of this stage.
|
||||||
|
|
||||||
|
Run RTM token creation through Save Token:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
pytest tests\setup -k RTM -s
|
||||||
|
```
|
||||||
|
|
||||||
|
Existing IDAS settings are preserved. The remaining product templates are disabled until
|
||||||
you fill in their actual ME instance/token codes and set `enabled` to `true`.
|
you fill in their actual ME instance/token codes and set `enabled` to `true`.
|
||||||
Additional products can be added using the same structure.
|
Additional products can be added using the same structure.
|
||||||
|
|
||||||
@@ -29,11 +49,21 @@ To override the defaults for one product, add flags directly inside that product
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"run_session": false,
|
"run_session": false,
|
||||||
"run_token": true,
|
"run_token": true,
|
||||||
"me_instance": "",
|
"me_instance": "52",
|
||||||
|
"session": {
|
||||||
|
"start_offset_minutes": 7,
|
||||||
|
"end_time": "20:30"
|
||||||
|
},
|
||||||
"token": {
|
"token": {
|
||||||
"market_code": "NATIONAL",
|
"market_code": "NATIONAL",
|
||||||
"product_type": "RTM",
|
"product_type": "RTM",
|
||||||
"product_codes": "YOUR_ACTUAL_RTM_CODE"
|
"product_codes": ["RTM"],
|
||||||
|
"generation_mode": "RTM",
|
||||||
|
"delivery_offset_days": 0,
|
||||||
|
"delivery_from_time": "00:00",
|
||||||
|
"delivery_to_time": "23:59",
|
||||||
|
"session_start_offset_minutes": 5,
|
||||||
|
"session_end_time": "20:00"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -45,7 +75,7 @@ Session timing overrides inherit any fields omitted from `setup.session`.
|
|||||||
Token timing settings belong to each product independently.
|
Token timing settings belong to each product independently.
|
||||||
|
|
||||||
Use `pytest tests\setup -s` to run all enabled products. Each product runs in its
|
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
|
own browser: both flags true runs session then the product's token flow in one login; otherwise
|
||||||
only its enabled action runs. Test/report names include the product name.
|
only its enabled action runs. Test/report names include the product name.
|
||||||
The session and token subfolder commands below select only products configured
|
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
|
for session-only and token-only respectively; use the full setup command for a
|
||||||
|
|||||||
+15
-7
@@ -47,16 +47,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"RTM": {
|
"RTM": {
|
||||||
"enabled": false,
|
"enabled": true,
|
||||||
"me_instance": "",
|
"run_session": false,
|
||||||
"session": {},
|
"run_token": true,
|
||||||
|
"me_instance": "52",
|
||||||
|
"session": {
|
||||||
|
"start_offset_minutes": 7,
|
||||||
|
"end_time": "20:30"
|
||||||
|
},
|
||||||
"token": {
|
"token": {
|
||||||
"market_code": "NATIONAL",
|
"market_code": "NATIONAL",
|
||||||
"product_type": "RTM",
|
"product_type": "RTM",
|
||||||
"product_codes": [],
|
"product_codes": ["RTM"],
|
||||||
"delivery_offset_days": 1,
|
"generation_mode": "RTM",
|
||||||
"expiry_time": "20:30",
|
"delivery_offset_days": 0,
|
||||||
"time_sessions": {}
|
"delivery_from_time": "00:00",
|
||||||
|
"delivery_to_time": "23:59",
|
||||||
|
"session_start_offset_minutes": 5,
|
||||||
|
"session_end_time": "20:00"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"UPC": {
|
"UPC": {
|
||||||
|
|||||||
+9
-3
@@ -367,7 +367,7 @@ def type_into_field(driver, locator, value):
|
|||||||
field.send_keys(value)
|
field.send_keys(value)
|
||||||
|
|
||||||
|
|
||||||
def select_bootstrap_dropdown_by_id(driver, element_id, value):
|
def select_bootstrap_dropdown_by_id(driver, element_id, value, exact=False):
|
||||||
|
|
||||||
wait = WebDriverWait(driver, 20)
|
wait = WebDriverWait(driver, 20)
|
||||||
|
|
||||||
@@ -411,12 +411,18 @@ def select_bootstrap_dropdown_by_id(driver, element_id, value):
|
|||||||
search_box.clear()
|
search_box.clear()
|
||||||
search_box.send_keys(value)
|
search_box.send_keys(value)
|
||||||
|
|
||||||
|
option_xpath = (
|
||||||
|
f"//div[@id='{element_id}']//div[contains(@class,'ui-select-choices-row')]"
|
||||||
|
f"[normalize-space()='{value}']"
|
||||||
|
if exact else
|
||||||
|
"//div[contains(@class,'ui-select-choices-row')]"
|
||||||
|
f"//*[normalize-space()='{value}' or starts-with(normalize-space(),'{value}')]"
|
||||||
|
)
|
||||||
option = wait.until(
|
option = wait.until(
|
||||||
EC.element_to_be_clickable(
|
EC.element_to_be_clickable(
|
||||||
(
|
(
|
||||||
By.XPATH,
|
By.XPATH,
|
||||||
"//div[contains(@class,'ui-select-choices-row')]"
|
option_xpath
|
||||||
f"//*[normalize-space()='{value}' or starts-with(normalize-space(),'{value}')]"
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ class Locator(object):
|
|||||||
tradingmaintenance="//span[normalize-space()='TRADING MAINTENANCE']"
|
tradingmaintenance="//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']"
|
||||||
|
generatormarketcode = "marketCode"
|
||||||
|
generatorproductcode = "productCode"
|
||||||
|
generatorgenerationmode = "tokenMstr.genMode"
|
||||||
|
rtmdeliveryfromdate = "//div[@ng-show='showgeneration4']//input[@placeholder='Delivery From Date']"
|
||||||
|
rtmdeliverytodate = "//div[@ng-show='showgeneration4']//input[@placeholder='Delivery To Date']"
|
||||||
|
rtmsessionstarttime = "//div[@ng-show='showgeneration4']//input[@placeholder='Session Start Time']"
|
||||||
|
rtmsessionendtime = "//div[@ng-show='showgeneration4']//input[@placeholder='Session End Time']"
|
||||||
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']"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from selenium.webdriver.support.ui import WebDriverWait
|
|||||||
from tests.config_reader import get_setup_configs
|
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 run_token_setup, get_product_codes
|
||||||
from tests.step_actions import StepActions
|
from tests.step_actions import StepActions
|
||||||
|
|
||||||
|
|
||||||
@@ -34,6 +34,6 @@ def test_session_and_token_setup(driver, test_context, config):
|
|||||||
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
|
||||||
create_and_activate_token(driver, step, product_token)
|
run_token_setup(driver, step, product_token)
|
||||||
|
|
||||||
logout(driver)
|
logout(driver)
|
||||||
|
|||||||
@@ -180,6 +180,58 @@ def complete_time_session_edits_from_saved_token(driver, step, token):
|
|||||||
pause_after_step()
|
pause_after_step()
|
||||||
|
|
||||||
|
|
||||||
|
def open_rtm_new_token(driver, step, token):
|
||||||
|
"""Prepare the RTM generator through Add New Token."""
|
||||||
|
open_trading_maintenance_page(driver, Locator.tokengenerator)
|
||||||
|
handle_transient_popups(driver)
|
||||||
|
step.capture("open_token_generator_rtm")
|
||||||
|
|
||||||
|
for element_id, value, name in (
|
||||||
|
(Locator.generatormarketcode, token["market_code"], "market_code"),
|
||||||
|
(Locator.generatorproductcode, token["product_code"], "product_code"),
|
||||||
|
(Locator.generatorgenerationmode, token["generation_mode"], "generation_mode"),
|
||||||
|
):
|
||||||
|
select_bootstrap_dropdown_by_id(driver, element_id, value, exact=True)
|
||||||
|
step.capture(f"select_{name}_rtm")
|
||||||
|
|
||||||
|
# Product Type is populated by the application after selecting Product Code.
|
||||||
|
step.click((By.XPATH, Locator.addnewtokenbtn), "click_add_new_token_rtm")
|
||||||
|
|
||||||
|
|
||||||
|
def get_rtm_token_times(token, now=None):
|
||||||
|
now = datetime.now() if now is None else now
|
||||||
|
delivery_date = (now + timedelta(days=token.get("delivery_offset_days", 0))).strftime("%d/%m/%Y")
|
||||||
|
return (
|
||||||
|
f"{delivery_date} {token.get('delivery_from_time', '00:00')}",
|
||||||
|
f"{delivery_date} {token.get('delivery_to_time', '23:59')}",
|
||||||
|
(now + timedelta(minutes=token.get("session_start_offset_minutes", 5))).strftime("%H:%M"),
|
||||||
|
token.get("session_end_time", "20:00"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_rtm_token(driver, step, token):
|
||||||
|
open_rtm_new_token(driver, step, token)
|
||||||
|
values = get_rtm_token_times(token)
|
||||||
|
fields = (
|
||||||
|
(Locator.rtmdeliveryfromdate, "delivery_from_date"),
|
||||||
|
(Locator.rtmdeliverytodate, "delivery_to_date"),
|
||||||
|
(Locator.rtmsessionstarttime, "session_start_time"),
|
||||||
|
(Locator.rtmsessionendtime, "session_end_time"),
|
||||||
|
)
|
||||||
|
for (locator, name), value in zip(fields, values):
|
||||||
|
type_first_visible_input(driver, locator, value)
|
||||||
|
step.capture(f"enter_{name}_rtm")
|
||||||
|
step.click((By.XPATH, Locator.savetokenbtn), "save_token_rtm")
|
||||||
|
handle_transient_popups(driver)
|
||||||
|
|
||||||
|
|
||||||
|
def run_token_setup(driver, step, token):
|
||||||
|
if token.get("product_type", "").upper() == "RTM":
|
||||||
|
create_rtm_token(driver, step, token)
|
||||||
|
else:
|
||||||
|
create_and_activate_token(driver, step, token)
|
||||||
|
|
||||||
|
|
||||||
def create_and_activate_token(driver, step, token):
|
def create_and_activate_token(driver, step, token):
|
||||||
|
|
||||||
product_code = token["product_code"]
|
product_code = token["product_code"]
|
||||||
@@ -224,7 +276,7 @@ def test_trading_token_creation(driver, test_context, config):
|
|||||||
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
|
||||||
create_and_activate_token(driver, step, product_token)
|
run_token_setup(driver, step, product_token)
|
||||||
|
|
||||||
logout(driver)
|
logout(driver)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
from unittest.mock import Mock
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from tests.locators import Locator
|
||||||
|
from tests.setup.tokens import test_trading_token as actions
|
||||||
|
|
||||||
|
|
||||||
|
def test_rtm_saves_generator_token_without_idas_activation(monkeypatch):
|
||||||
|
events = []
|
||||||
|
monkeypatch.setattr(actions, "open_trading_maintenance_page", lambda driver, menu: events.append(menu))
|
||||||
|
monkeypatch.setattr(actions, "handle_transient_popups", lambda driver: None)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
actions, "select_bootstrap_dropdown_by_id",
|
||||||
|
lambda driver, field, value, exact=False: events.append((field, value, exact)),
|
||||||
|
)
|
||||||
|
idas_flow = Mock()
|
||||||
|
monkeypatch.setattr(actions, "create_and_activate_token", idas_flow)
|
||||||
|
step = Mock()
|
||||||
|
step.click.side_effect = lambda locator, name: events.append(locator[1])
|
||||||
|
monkeypatch.setattr(actions, "get_rtm_token_times", lambda token: (
|
||||||
|
"23/09/2026 00:00", "23/09/2026 23:59", "10:15", "20:00",
|
||||||
|
))
|
||||||
|
monkeypatch.setattr(actions, "type_first_visible_input", lambda driver, locator, value: events.append((locator, value)))
|
||||||
|
|
||||||
|
actions.run_token_setup(Mock(), step, {
|
||||||
|
"product_type": "RTM", "market_code": "NATIONAL",
|
||||||
|
"product_code": "RTM", "generation_mode": "RTM",
|
||||||
|
})
|
||||||
|
|
||||||
|
assert events == [
|
||||||
|
Locator.tokengenerator,
|
||||||
|
("marketCode", "NATIONAL", True),
|
||||||
|
("productCode", "RTM", True),
|
||||||
|
("tokenMstr.genMode", "RTM", True),
|
||||||
|
Locator.addnewtokenbtn,
|
||||||
|
(Locator.rtmdeliveryfromdate, "23/09/2026 00:00"),
|
||||||
|
(Locator.rtmdeliverytodate, "23/09/2026 23:59"),
|
||||||
|
(Locator.rtmsessionstarttime, "10:15"),
|
||||||
|
(Locator.rtmsessionendtime, "20:00"),
|
||||||
|
Locator.savetokenbtn,
|
||||||
|
]
|
||||||
|
step.type.assert_not_called()
|
||||||
|
idas_flow.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_idas_keeps_existing_creation_and_activation_flow(monkeypatch):
|
||||||
|
idas_flow = Mock()
|
||||||
|
rtm_flow = Mock()
|
||||||
|
monkeypatch.setattr(actions, "create_and_activate_token", idas_flow)
|
||||||
|
monkeypatch.setattr(actions, "open_rtm_new_token", rtm_flow)
|
||||||
|
driver, step = Mock(), Mock()
|
||||||
|
token = {"product_type": "IDAS", "product_code": "IDAS4"}
|
||||||
|
|
||||||
|
actions.run_token_setup(driver, step, token)
|
||||||
|
|
||||||
|
idas_flow.assert_called_once_with(driver, step, token)
|
||||||
|
rtm_flow.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_rtm_token_timing_defaults_and_overrides():
|
||||||
|
now = datetime(2026, 9, 23, 10, 58)
|
||||||
|
assert actions.get_rtm_token_times({}, now) == (
|
||||||
|
"23/09/2026 00:00", "23/09/2026 23:59", "11:03", "20:00",
|
||||||
|
)
|
||||||
|
assert actions.get_rtm_token_times({
|
||||||
|
"delivery_offset_days": 1,
|
||||||
|
"delivery_from_time": "01:30", "delivery_to_time": "22:00",
|
||||||
|
"session_start_offset_minutes": 12, "session_end_time": "21:15",
|
||||||
|
}, now) == (
|
||||||
|
"24/09/2026 01:30", "24/09/2026 22:00", "11:10", "21:15",
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user