diff --git a/RUN_COMMANDS.md b/RUN_COMMANDS.md index f82b1b8..26ea073 100644 --- a/RUN_COMMANDS.md +++ b/RUN_COMMANDS.md @@ -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. 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 +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`. 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, "run_session": false, "run_token": true, - "me_instance": "", + "me_instance": "52", + "session": { + "start_offset_minutes": 7, + "end_time": "20:30" + }, "token": { "market_code": "NATIONAL", "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. 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. 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 diff --git a/config.json b/config.json index 6d8a1f7..c832a1c 100644 --- a/config.json +++ b/config.json @@ -47,16 +47,24 @@ } }, "RTM": { - "enabled": false, - "me_instance": "", - "session": {}, + "enabled": true, + "run_session": false, + "run_token": true, + "me_instance": "52", + "session": { + "start_offset_minutes": 7, + "end_time": "20:30" + }, "token": { "market_code": "NATIONAL", "product_type": "RTM", - "product_codes": [], - "delivery_offset_days": 1, - "expiry_time": "20:30", - "time_sessions": {} + "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" } }, "UPC": { diff --git a/tests/helpers.py b/tests/helpers.py index 539080c..4259378 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -367,7 +367,7 @@ def type_into_field(driver, locator, 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) @@ -411,12 +411,18 @@ def select_bootstrap_dropdown_by_id(driver, element_id, value): search_box.clear() 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( EC.element_to_be_clickable( ( By.XPATH, - "//div[contains(@class,'ui-select-choices-row')]" - f"//*[normalize-space()='{value}' or starts-with(normalize-space(),'{value}')]" + option_xpath ) ) ) diff --git a/tests/locators.py b/tests/locators.py index 33bfb3e..d0632c5 100644 --- a/tests/locators.py +++ b/tests/locators.py @@ -11,6 +11,14 @@ class Locator(object): tradingmaintenance="//span[normalize-space()='TRADING MAINTENANCE']" tradingsession="//a[normalize-space()='Trading Session Master']" 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']" addsessionbtn="//button[contains(text(),'Add')]" addnewtokenbtn="//button[@data-ng-click='addToken()' and normalize-space()='Add New Token']" diff --git a/tests/setup/test_combined_setup.py b/tests/setup/test_combined_setup.py index 98ab035..c5f6e43 100644 --- a/tests/setup/test_combined_setup.py +++ b/tests/setup/test_combined_setup.py @@ -4,7 +4,7 @@ from selenium.webdriver.support.ui import WebDriverWait from tests.config_reader import get_setup_configs from tests.helpers import login, logout, save_session_backup 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 @@ -34,6 +34,6 @@ def test_session_and_token_setup(driver, test_context, config): for product_code in get_product_codes(token): product_token = dict(token) product_token["product_code"] = product_code - create_and_activate_token(driver, step, product_token) + 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 a3e6f55..4503b1e 100644 --- a/tests/setup/tokens/test_trading_token.py +++ b/tests/setup/tokens/test_trading_token.py @@ -180,6 +180,58 @@ def complete_time_session_edits_from_saved_token(driver, step, token): 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): 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): product_token = dict(token) product_token["product_code"] = product_code - create_and_activate_token(driver, step, product_token) + run_token_setup(driver, step, product_token) logout(driver) diff --git a/tests/test_token_setup_routing.py b/tests/test_token_setup_routing.py new file mode 100644 index 0000000..54b3daa --- /dev/null +++ b/tests/test_token_setup_routing.py @@ -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", + )