This commit is contained in:
2025-11-27 18:14:25 +01:00
parent 1348c37675
commit f552649357
52 changed files with 1465 additions and 8765 deletions

View File

@@ -3,7 +3,7 @@
* Uses text-based and ARIA selectors since the site uses React/Chakra UI
* with dynamically generated class names.
*
* VERIFIED against real iRacing HTML captured 2024-11-23
* VERIFIED against html-dumps-optimized 2025-11-27
*/
export const IRACING_SELECTORS = {
// Login page
@@ -27,18 +27,18 @@ export const IRACING_SELECTORS = {
// Common modal/wizard selectors - VERIFIED from real HTML
wizard: {
modal: '#create-race-modal-modal-content, .modal-content',
modalDialog: '.modal-dialog',
modal: '#create-race-modal, .modal.fade.in',
modalDialog: '#create-race-modal-modal-dialog, .modal-dialog',
modalContent: '#create-race-modal-modal-content, .modal-content',
modalTitle: '[data-testid="modal-title"], .modal-title',
// Wizard footer buttons - these are anchor tags styled as buttons
// The "Next" button shows the name of the next step (e.g., "Server Details")
// In the dumps, the footer has two buttons: Previous Step (left) and Next Step (right)
nextButton: '.wizard-footer a.btn:last-child',
backButton: '.wizard-footer a.btn:first-child',
modalTitle: '[data-testid="modal-title"]',
// Wizard footer buttons - CORRECTED: The footer contains navigation buttons and dropup menus
// The main navigation is via the sidebar links, footer has Back/Next style buttons
// Based on dumps, footer has .btn-group with buttons for navigation
nextButton: '.modal-footer .btn-toolbar a.btn:not(.dropdown-toggle), .modal-footer .btn-group a.btn:last-child',
backButton: '.modal-footer .btn-group a.btn:first-child',
// Modal footer actions
confirmButton: '.modal-footer a.btn-success, button:has-text("Confirm"), button:has-text("OK")',
cancelButton: '.modal-footer a.btn-secondary:has-text("Back"), button:has-text("Cancel")',
confirmButton: '.modal-footer a.btn-success, .modal-footer button:has-text("Confirm"), button:has-text("OK")',
cancelButton: '.modal-footer a.btn-secondary, button:has-text("Cancel")',
closeButton: '[data-testid="button-close-modal"]',
// Wizard sidebar navigation links - VERIFIED from dumps
sidebarLinks: {
@@ -72,7 +72,7 @@ export const IRACING_SELECTORS = {
// Form fields - based on actual iRacing DOM structure
fields: {
textInput: 'input.form-control, .chakra-input, input[type="text"], input[data-field], input[data-test], input[placeholder]',
textInput: '.chakra-input, input.form-control, input[type="text"], input[data-field], input[data-test], input[placeholder]',
passwordInput: 'input[type="password"], input[maxlength="32"].form-control, input[data-field="password"], input[name="password"]',
textarea: 'textarea.form-control, .chakra-textarea, textarea, textarea[data-field]',
select: '.chakra-select, select.form-control, select, [data-dropdown], select[data-field]',
@@ -83,14 +83,16 @@ export const IRACING_SELECTORS = {
// Step-specific selectors - VERIFIED from real iRacing HTML structure
steps: {
// Step 3: Race Information - form structure inside #set-session-information
// Form groups have labels followed by inputs
sessionName: '#set-session-information .card-block .form-group:first-of-type input.form-control, #set-session-information [data-field="sessionName"], [data-field="sessionName"]',
sessionNameAlt: '#set-session-information input.form-control[type="text"]:not([maxlength]), input[data-field="sessionName"]',
password: '#set-session-information .card-block .form-group:nth-of-type(2) input.form-control, #set-session-information input[type="password"], #set-session-information input.chakra-input[type="text"]:not([name="Current page"]):not([id*="field-:rue:"]):not([id*="field-:rug:"]):not([id*="field-:ruj:"]):not([id*="field-:rl5b:"]):not([id*="field-:rktk:"]), #set-session-information [data-field="password"], [data-field="password"]',
passwordAlt: '#set-session-information input.form-control[maxlength="32"], input[data-field="password"]',
description: '#set-session-information .card-block .form-group:last-of-type textarea.form-control, #set-session-information textarea[data-field="description"], [data-field="description"]',
descriptionAlt: '#set-session-information textarea.form-control, textarea[data-field="description"]',
// Step 3: Race Information - CORRECTED based on actual HTML structure
// Session name is a text input in a form-group with label "Session Name"
sessionName: '#set-session-information input.form-control[type="text"]:not([maxlength])',
sessionNameAlt: 'input[name="sessionName"], input.form-control[type="text"]',
// Password field has maxlength="32" and is a text input (not type="password")
password: '#set-session-information input.form-control[maxlength="32"]',
passwordAlt: 'input[maxlength="32"][type="text"]',
// Description is a textarea in the form
description: '#set-session-information textarea.form-control',
descriptionAlt: 'textarea.form-control',
// League racing toggle in Step 3
leagueRacingToggle: '#set-session-information .switch-checkbox, [data-toggle="leagueRacing"]',
@@ -100,41 +102,39 @@ export const IRACING_SELECTORS = {
// Step 5/6: Admins
adminSearch: 'input[placeholder*="Search"]',
adminList: '[data-list="admins"]', // Keep generic if not found in dumps, but search input is verified
adminList: '#set-admins table.table.table-striped, #set-admins .card-block table',
addAdminButton: 'a.btn:has-text("Add an Admin")',
// Step 7: Time Limits - Bootstrap-slider uses hidden input[type="text"] with id containing slider name
// Also targets the visible slider handle for interaction
// Dumps show dynamic IDs like time-limit-slider1763726367635
practice: 'input[id*="time-limit-slider"]', // This is risky if multiple sliders share the same ID pattern.
// TODO: Need better selectors for specific sliders if they exist.
// For now, we'll assume the automation handles finding the right one by index or label if possible.
qualify: 'input[id*="qualify"], input[id*="time-limit-slider"]',
race: 'input[id*="race"], input[id*="time-limit-slider"]',
practice: 'label:has-text("Practice") ~ div input[id*="time-limit-slider"]',
qualify: 'label:has-text("Qualify") ~ div input[id*="time-limit-slider"]',
race: 'label:has-text("Race") ~ div input[id*="time-limit-slider"]',
// Step 8/9: Cars
carSearch: 'input[placeholder*="Search"]',
carList: 'table.table.table-striped',
// Add Car button - triggers car selection interface in wizard sidebar
addCarButton: 'a.btn:has-text("Add a Car")',
// Car selection interface - CORRECTED: No separate modal, uses wizard sidebar within main modal
addCarModal: '.wizard-sidebar',
// Select button inside car table row - clicking this adds the car immediately (no confirm step)
carSelectButton: 'a.btn:has-text("Select")',
// Add Car button - CORRECTED: Uses specific class and text
addCarButton: 'a.btn.btn-primary.btn-block.btn-sm:has-text("Add a Car")',
// Car selection interface - drawer that opens within the wizard sidebar
addCarModal: '.drawer-container .drawer',
// Select button inside car dropdown - opens config selection
carSelectButton: 'a.btn.btn-primary.btn-xs.dropdown-toggle:has-text("Select")',
// Step 10/11/12: Track
trackSearch: 'input[placeholder*="Search"]',
trackList: 'table.table.table-striped',
// Add Track button - triggers track selection interface in wizard sidebar
addTrackButton: 'a.btn:has-text("Add a Track")',
// Track selection interface - CORRECTED: No separate modal, uses wizard sidebar within main modal
addTrackModal: '.wizard-sidebar',
// Select button inside track table row - clicking this selects the track immediately (no confirm step)
trackSelectButton: 'a.btn:has-text("Select")',
// Add Track button - CORRECTED: Uses specific class and text
addTrackButton: 'a.btn.btn-primary.btn-block.btn-sm:has-text("Add a Track")',
// Track selection interface - drawer that opens within the card
addTrackModal: '.drawer-container .drawer',
// Select button inside track dropdown - opens config selection
trackSelectButton: 'a.btn.btn-primary.btn-xs.dropdown-toggle:has-text("Select")',
// Dropdown toggle for multi-config tracks - opens a menu of track configurations
trackSelectDropdown: '.wizard-sidebar table a.btn.btn-primary.btn-xs.dropdown-toggle, #set-track table a.btn.btn-primary.btn-xs.dropdown-toggle',
trackSelectDropdown: 'a.btn.btn-primary.btn-xs.dropdown-toggle',
// First item in the dropdown menu for selecting track configuration
trackSelectDropdownItem: '.dropdown-menu.show .dropdown-item:first-child, .dropdown-menu-lg .dropdown-item:first-child',
trackSelectDropdownItem: '.dropdown-menu.dropdown-menu-right .dropdown-item:first-child',
// Step 13: Track Options
trackConfig: '#set-track-options select.form-control, #set-track-options [data-dropdown="trackConfig"]',
@@ -163,7 +163,7 @@ export const IRACING_SELECTORS = {
*/
BLOCKED_SELECTORS: {
// Checkout/payment buttons - NEVER click these (verified from real HTML)
checkout: 'a.btn-success:has(.icon-cart), a.btn:has-text("Check Out"), button:has-text("Check Out"), [data-testid*="checkout"]',
checkout: '.chakra-button:has-text("Check Out"), a.btn-success:has(.icon-cart), a.btn:has-text("Check Out"), button:has-text("Check Out"), [data-testid*="checkout"]',
purchase: 'button:has-text("Purchase"), a.btn:has-text("Purchase"), .chakra-button:has-text("Purchase"), button[aria-label="Purchase"]',
buy: 'button:has-text("Buy"), a.btn:has-text("Buy Now"), button:has-text("Buy Now")',
payment: 'button[type="submit"]:has-text("Submit Payment"), .payment-button, #checkout-button, button:has-text("Pay"), a.btn:has-text("Pay")',