This commit is contained in:
2025-11-30 23:00:48 +01:00
parent 4b8c70978f
commit 645f537895
41 changed files with 738 additions and 1631 deletions

View File

@@ -280,6 +280,30 @@ export class IRacingDomInteractor {
const page = this.getPage();
if (!this.isRealMode()) {
const timeout = this.config.timeout;
try {
const footerButtons = page.locator('.wizard-footer a.btn, .wizard-footer button');
const count = await footerButtons.count().catch(() => 0);
if (count > 0) {
const targetText = nextStepName.toLowerCase();
for (let i = 0; i < count; i++) {
const button = footerButtons.nth(i);
const text = (await button.innerText().catch(() => '')).trim().toLowerCase();
if (text && text.includes(targetText)) {
await button.click({ timeout, force: true });
this.log('info', 'Clicked mock next button via footer text match', {
nextStepName,
text,
});
return;
}
}
}
} catch {
}
await this.clickAction('next');
return;
}
@@ -346,7 +370,13 @@ export class IRacingDomInteractor {
try {
const count = await page.locator(h).first().count().catch(() => 0);
if (count > 0) {
const tag = await page.locator(h).first().evaluate((el) => el.tagName.toLowerCase()).catch(() => '');
const tag = await page
.locator(h)
.first()
.evaluate((el: any) =>
String((el as any).tagName || '').toLowerCase(),
)
.catch(() => '');
if (tag === 'select') {
try {
await page.selectOption(h, value);
@@ -514,7 +544,11 @@ export class IRacingDomInteractor {
const count = await locator.count().catch(() => 0);
if (count === 0) continue;
const tagName = await locator.evaluate((el) => el.tagName.toLowerCase()).catch(() => '');
const tagName = await locator
.evaluate((el: any) =>
String((el as any).tagName || '').toLowerCase(),
)
.catch(() => '');
const type = await locator.getAttribute('type').catch(() => '');
if (tagName === 'input' && (type === 'checkbox' || type === 'radio')) {
@@ -648,7 +682,11 @@ export class IRacingDomInteractor {
const count = await locator.count().catch(() => 0);
if (count === 0) continue;
const tagName = await locator.evaluate((el) => el.tagName.toLowerCase()).catch(() => '');
const tagName = await locator
.evaluate((el: any) =>
String((el as any).tagName || '').toLowerCase(),
)
.catch(() => '');
if (tagName === 'input') {
const type = await locator.getAttribute('type').catch(() => '');
if (type === 'range' || type === 'text' || type === 'number') {
@@ -746,7 +784,7 @@ export class IRacingDomInteractor {
const addCarButtonSelector = this.isRealMode()
? IRACING_SELECTORS.steps.addCarButton
: '[data-action="add-car"]';
: `${IRACING_SELECTORS.steps.addCarButton}, [data-action="add-car"]`;
try {
this.log('info', 'Clicking Add Car button to open modal');

View File

@@ -97,20 +97,28 @@ export const IRACING_SELECTORS = {
leagueRacingToggle: '#set-session-information .switch-checkbox, [data-toggle="leagueRacing"]',
// Step 4: Server Details
region: '#set-server-details select.form-control, #set-server-details [data-dropdown="region"], #set-server-details [data-dropdown], [data-dropdown="region"]',
startNow: '#set-server-details .switch-checkbox, #set-server-details input[type="checkbox"], [data-toggle="startNow"], input[data-toggle="startNow"]',
region:
'#set-server-details select.form-control, ' +
'#set-server-details [data-dropdown="region"], ' +
'#set-server-details [data-dropdown], ' +
'[data-dropdown="region"], ' +
'#set-server-details [role="radiogroup"] input[type="radio"]',
startNow:
'#set-server-details .switch-checkbox, ' +
'#set-server-details input[type="checkbox"], ' +
'[data-toggle="startNow"], ' +
'input[data-toggle="startNow"]',
// Step 5/6: Admins
adminSearch: 'input[placeholder*="Search"]',
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: '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 7: Time Limits - Bootstrap-slider uses hidden input[type="text"] with dynamic id
// Fixtures show ids like time-limit-slider1764248520320
practice: '#set-time-limit input[id*="time-limit-slider"]',
qualify: '#set-time-limit input[id*="time-limit-slider"]',
race: '#set-time-limit input[id*="time-limit-slider"]',
// Step 8/9: Cars
carSearch: 'input[placeholder*="Search"]',