linting
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import { loadAutomationConfig, getAutomationMode, AutomationMode } from '../../../core/automation/infrastructure/config/AutomationConfig';
|
||||
|
||||
|
||||
describe('AutomationConfig', () => {
|
||||
const originalEnv = process.env;
|
||||
@@ -17,7 +17,7 @@ describe('AutomationConfig', () => {
|
||||
describe('getAutomationMode', () => {
|
||||
describe('NODE_ENV-based mode detection', () => {
|
||||
it('should return production mode when NODE_ENV=production', () => {
|
||||
(process.env as any).NODE_ENV = 'production';
|
||||
(process.env as unknown).NODE_ENV = 'production';
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const mode = getAutomationMode();
|
||||
@@ -26,7 +26,7 @@ describe('AutomationConfig', () => {
|
||||
});
|
||||
|
||||
it('should return test mode when NODE_ENV=test', () => {
|
||||
(process.env as any).NODE_ENV = 'test';
|
||||
(process.env as unknown).NODE_ENV = 'test';
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const mode = getAutomationMode();
|
||||
@@ -35,7 +35,7 @@ describe('AutomationConfig', () => {
|
||||
});
|
||||
|
||||
it('should return test mode when NODE_ENV is not set', () => {
|
||||
delete (process.env as any).NODE_ENV;
|
||||
delete (process.env as unknown).NODE_ENV;
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const mode = getAutomationMode();
|
||||
@@ -44,7 +44,7 @@ describe('AutomationConfig', () => {
|
||||
});
|
||||
|
||||
it('should return test mode for unknown NODE_ENV values', () => {
|
||||
(process.env as any).NODE_ENV = 'staging';
|
||||
(process.env as unknown).NODE_ENV = 'staging';
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const mode = getAutomationMode();
|
||||
@@ -53,7 +53,7 @@ describe('AutomationConfig', () => {
|
||||
});
|
||||
|
||||
it('should return development mode when NODE_ENV=development', () => {
|
||||
(process.env as any).NODE_ENV = 'development';
|
||||
(process.env as unknown).NODE_ENV = 'development';
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const mode = getAutomationMode();
|
||||
@@ -104,7 +104,7 @@ describe('AutomationConfig', () => {
|
||||
|
||||
it('should ignore invalid AUTOMATION_MODE and use NODE_ENV', () => {
|
||||
process.env.AUTOMATION_MODE = 'invalid-mode';
|
||||
(process.env as any).NODE_ENV = 'production';
|
||||
(process.env as unknown).NODE_ENV = 'production';
|
||||
|
||||
const mode = getAutomationMode();
|
||||
|
||||
@@ -116,7 +116,7 @@ describe('AutomationConfig', () => {
|
||||
describe('loadAutomationConfig', () => {
|
||||
describe('default configuration', () => {
|
||||
it('should return test mode when NODE_ENV is not set', () => {
|
||||
delete (process.env as any).NODE_ENV;
|
||||
delete (process.env as unknown).NODE_ENV;
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
@@ -143,7 +143,7 @@ describe('AutomationConfig', () => {
|
||||
|
||||
describe('production mode configuration', () => {
|
||||
it('should return production mode when NODE_ENV=production', () => {
|
||||
(process.env as any).NODE_ENV = 'production';
|
||||
(process.env as unknown).NODE_ENV = 'production';
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
@@ -228,7 +228,7 @@ describe('AutomationConfig', () => {
|
||||
});
|
||||
|
||||
it('should fallback to test mode for invalid NODE_ENV', () => {
|
||||
(process.env as any).NODE_ENV = 'invalid-env';
|
||||
(process.env as unknown).NODE_ENV = 'invalid-env';
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
@@ -239,7 +239,7 @@ describe('AutomationConfig', () => {
|
||||
|
||||
describe('full configuration scenario', () => {
|
||||
it('should load complete test environment configuration', () => {
|
||||
(process.env as any).NODE_ENV = 'test';
|
||||
(process.env as unknown).NODE_ENV = 'test';
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
@@ -249,7 +249,7 @@ describe('AutomationConfig', () => {
|
||||
});
|
||||
|
||||
it('should load complete production environment configuration', () => {
|
||||
(process.env as any).NODE_ENV = 'production';
|
||||
(process.env as unknown).NODE_ENV = 'production';
|
||||
delete process.env.AUTOMATION_MODE;
|
||||
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = { ...originalEnv };
|
||||
delete (process.env as any).NODE_ENV;
|
||||
delete (process.env as unknown).NODE_ENV;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -21,7 +21,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
|
||||
describe('Development Mode with Runtime Control', () => {
|
||||
it('should default to headless in development mode', () => {
|
||||
(process.env as any).NODE_ENV = 'development';
|
||||
(process.env as unknown).NODE_ENV = 'development';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
@@ -31,7 +31,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should allow runtime switch to headless mode in development', () => {
|
||||
(process.env as any).NODE_ENV = 'development';
|
||||
(process.env as unknown).NODE_ENV = 'development';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
loader.setDevelopmentMode('headless');
|
||||
@@ -42,7 +42,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should allow runtime switch to headed mode in development', () => {
|
||||
(process.env as any).NODE_ENV = 'development';
|
||||
(process.env as unknown).NODE_ENV = 'development';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
loader.setDevelopmentMode('headed');
|
||||
@@ -53,7 +53,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should persist runtime setting across multiple load() calls', () => {
|
||||
(process.env as any).NODE_ENV = 'development';
|
||||
(process.env as unknown).NODE_ENV = 'development';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
loader.setDevelopmentMode('headless');
|
||||
@@ -66,7 +66,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should return current development mode via getter', () => {
|
||||
(process.env as any).NODE_ENV = 'development';
|
||||
(process.env as unknown).NODE_ENV = 'development';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
expect(loader.getDevelopmentMode()).toBe('headless');
|
||||
@@ -78,7 +78,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
|
||||
describe('Production Mode', () => {
|
||||
it('should use headless mode when NODE_ENV=production', () => {
|
||||
(process.env as any).NODE_ENV = 'production';
|
||||
(process.env as unknown).NODE_ENV = 'production';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
@@ -88,7 +88,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should ignore setDevelopmentMode in production', () => {
|
||||
(process.env as any).NODE_ENV = 'production';
|
||||
(process.env as unknown).NODE_ENV = 'production';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
loader.setDevelopmentMode('headed');
|
||||
@@ -101,7 +101,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
|
||||
describe('Test Mode', () => {
|
||||
it('should use headless mode when NODE_ENV=test', () => {
|
||||
(process.env as any).NODE_ENV = 'test';
|
||||
(process.env as unknown).NODE_ENV = 'test';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
@@ -111,7 +111,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should ignore setDevelopmentMode in test mode', () => {
|
||||
(process.env as any).NODE_ENV = 'test';
|
||||
(process.env as unknown).NODE_ENV = 'test';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
loader.setDevelopmentMode('headed');
|
||||
@@ -124,7 +124,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
|
||||
describe('Default Mode', () => {
|
||||
it('should default to headless mode when NODE_ENV is not set', () => {
|
||||
delete (process.env as any).NODE_ENV;
|
||||
delete (process.env as unknown).NODE_ENV;
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
@@ -134,7 +134,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should use headless mode for any non-development NODE_ENV value', () => {
|
||||
(process.env as any).NODE_ENV = 'staging';
|
||||
(process.env as unknown).NODE_ENV = 'staging';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
@@ -146,7 +146,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
|
||||
describe('Source Tracking', () => {
|
||||
it('should report GUI as source in development mode', () => {
|
||||
(process.env as any).NODE_ENV = 'development';
|
||||
(process.env as unknown).NODE_ENV = 'development';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
@@ -155,7 +155,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should report NODE_ENV as source in production mode', () => {
|
||||
(process.env as any).NODE_ENV = 'production';
|
||||
(process.env as unknown).NODE_ENV = 'production';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
@@ -164,7 +164,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should report NODE_ENV as source in test mode', () => {
|
||||
(process.env as any).NODE_ENV = 'test';
|
||||
(process.env as unknown).NODE_ENV = 'test';
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
@@ -173,7 +173,7 @@ describe('BrowserModeConfig - GREEN Phase', () => {
|
||||
});
|
||||
|
||||
it('should report NODE_ENV as source when NODE_ENV is not set', () => {
|
||||
delete (process.env as any).NODE_ENV;
|
||||
delete (process.env as unknown).NODE_ENV;
|
||||
|
||||
const loader = new BrowserModeConfigLoader();
|
||||
const config = loader.load();
|
||||
|
||||
@@ -5,12 +5,12 @@ import type { CheckoutInfoDTO } from '../../../application/dto/CheckoutInfoDTO';
|
||||
import { IRACING_SELECTORS } from './dom/IRacingSelectors';
|
||||
|
||||
interface Page {
|
||||
locator(selector: string): Locator;
|
||||
locator(_selector: string): Locator;
|
||||
}
|
||||
|
||||
interface Locator {
|
||||
first(): Locator;
|
||||
locator(selector: string): Locator;
|
||||
locator(_selector: string): Locator;
|
||||
getAttribute(name: string): Promise<string | null>;
|
||||
innerHTML(): Promise<string>;
|
||||
textContent(): Promise<string | null>;
|
||||
|
||||
@@ -190,7 +190,7 @@ describe('AuthenticationGuard', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Login button selector specificity', () => {
|
||||
describe('Login button _selector specificity', () => {
|
||||
test('should detect login button on actual login pages', async () => {
|
||||
// Simulate a real login page with a login form
|
||||
const mockLocator = {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, test, expect, beforeEach } from 'vitest';
|
||||
import { SessionCookieStore } from '@core/automation/infrastructure//automation/auth/SessionCookieStore';
|
||||
import type { Cookie } from 'playwright';
|
||||
|
||||
const logger = console as any;
|
||||
const logger = console as unknown;
|
||||
|
||||
describe('SessionCookieStore - Cookie Validation', () => {
|
||||
let cookieStore: SessionCookieStore;
|
||||
|
||||
@@ -20,9 +20,9 @@ import { Result } from '../../../../../shared/result/Result';
|
||||
import { IRACING_SELECTORS, IRACING_URLS, IRACING_TIMEOUTS, ALL_BLOCKED_SELECTORS, BLOCKED_KEYWORDS } from '../dom/IRacingSelectors';
|
||||
import { SessionCookieStore } from '../auth/SessionCookieStore';
|
||||
import { PlaywrightBrowserSession } from './PlaywrightBrowserSession';
|
||||
import { getFixtureForStep } from '../engine/FixtureServer';
|
||||
|
||||
import { BrowserModeConfigLoader, BrowserMode } from '../../../config/BrowserModeConfig';
|
||||
import { getAutomationMode } from '../../../config/AutomationConfig';
|
||||
|
||||
import { PageStateValidator, PageStateValidation, PageStateValidationResult } from '@core/automation/domain/services/PageStateValidator';
|
||||
import { IRacingDomNavigator } from '../dom/IRacingDomNavigator';
|
||||
import { SafeClickService } from '../dom/SafeClickService';
|
||||
@@ -543,7 +543,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
*/
|
||||
async attachPanel(page?: Page, actionId?: string): Promise<void> {
|
||||
const selector = '#gridpilot-overlay'
|
||||
await this.emitLifecycle({ type: 'panel-attached', actionId, timestamp: Date.now(), payload: { selector } })
|
||||
await this.emitLifecycle({ type: 'panel-attached', actionId, timestamp: Date.now(), payload: { _selector } })
|
||||
await this.emitLifecycle({ type: 'action-started', actionId, timestamp: Date.now() })
|
||||
}
|
||||
private isRealMode(): boolean {
|
||||
@@ -583,7 +583,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
}
|
||||
}
|
||||
|
||||
const actualState = (selector: string): boolean => {
|
||||
const actualState = (_selector: string): boolean => {
|
||||
return selectorChecks[selector] === true;
|
||||
};
|
||||
|
||||
@@ -1247,7 +1247,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
* @param elementText Optional text content of the element (should be direct text only)
|
||||
* @returns true if the selector/text matches a blocked pattern
|
||||
*/
|
||||
private isBlockedSelector(selector: string, elementText?: string): boolean {
|
||||
private isBlockedSelector(_selector: string, elementText?: string): boolean {
|
||||
const selectorLower = selector.toLowerCase();
|
||||
const textLower = elementText?.toLowerCase().trim() ?? '';
|
||||
|
||||
@@ -1293,7 +1293,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
* @param selector The CSS selector of the element to verify
|
||||
* @throws Error if element is a blocked checkout/payment button
|
||||
*/
|
||||
private async verifyNotBlockedElement(selector: string): Promise<void> {
|
||||
private async verifyNotBlockedElement(_selector: string): Promise<void> {
|
||||
if (!this.page) return;
|
||||
|
||||
// In mock mode we bypass safety blocking to allow tests to exercise checkout flows
|
||||
@@ -1394,7 +1394,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
throw error;
|
||||
}
|
||||
// Otherwise ignore - element might not exist yet, safeClick will handle that
|
||||
this.log('debug', 'Could not verify element (may not exist yet)', { selector, error: String(error) });
|
||||
this.log('debug', 'Could not verify element (may not exist yet)', { _selector, error: String(error) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1409,7 +1409,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
* @param options Click options including timeout and force
|
||||
* @returns Promise that resolves when click succeeds or throws after max retries
|
||||
*/
|
||||
private async safeClick(selector: string, options?: { timeout?: number; force?: boolean }): Promise<void> {
|
||||
private async safeClick(_selector: string, options?: { timeout?: number; force?: boolean }): Promise<void> {
|
||||
if (!this.page) {
|
||||
throw new Error('Browser not connected');
|
||||
}
|
||||
@@ -1438,7 +1438,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
try {
|
||||
// On final attempt, use force: true if datetime picker issues detected
|
||||
const useForce = options?.force || attempt === maxRetries;
|
||||
await this.page.click(selector, { timeout, force: useForce });
|
||||
await this.page.click(_selector, { timeout, force: useForce });
|
||||
return; // Success
|
||||
} catch (error) {
|
||||
// Re-throw blocked errors immediately
|
||||
@@ -1496,7 +1496,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
this.log('debug', 'JS fallback click did not find element or failed', { selector });
|
||||
}
|
||||
} catch (e) {
|
||||
this.log('debug', 'JS fallback click error', { selector, error: String(e) });
|
||||
this.log('debug', 'JS fallback click error', { _selector, error: String(e) });
|
||||
}
|
||||
|
||||
this.log('error', 'Max retries reached, click still blocked', { selector });
|
||||
@@ -1559,7 +1559,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
|
||||
if (isVisible) {
|
||||
await radioLabel.click({ timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('info', 'Selected weather type', { weatherType, selector: labelSelector });
|
||||
this.log('info', 'Selected weather type', { weatherType, _selector: labelSelector });
|
||||
} else {
|
||||
this.log('debug', 'Weather type radio not visible, may already be selected or step is different');
|
||||
}
|
||||
@@ -1674,10 +1674,10 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
});
|
||||
// Brief pause for modal animation (reduced from 300ms)
|
||||
await this.page.waitForTimeout(150);
|
||||
this.log('info', 'Add Car modal is visible', { selector: modalSelector });
|
||||
this.log('info', 'Add Car modal is visible', { _selector: modalSelector });
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
this.log('warn', 'Add Car modal not found with primary selector, dumping #create-race-wizard innerHTML and retrying', { error: message });
|
||||
this.log('warn', 'Add Car modal not found with primary _selector, dumping #create-race-wizard innerHTML and retrying', { error: message });
|
||||
const html = await this.page!.innerHTML('#create-race-wizard').catch(() => '');
|
||||
this.log('debug', 'create-race-wizard innerHTML (truncated)', { html: html ? html.slice(0, 2000) : '' });
|
||||
this.log('info', 'Retrying wait for Add Car modal with extended timeout');
|
||||
@@ -1688,7 +1688,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
timeout: 10000,
|
||||
});
|
||||
await this.page.waitForTimeout(150);
|
||||
this.log('info', 'Add Car modal found after retry', { selector: modalSelectorRetry });
|
||||
this.log('info', 'Add Car modal found after retry', { _selector: modalSelectorRetry });
|
||||
} catch {
|
||||
this.log('warn', 'Add Car modal still not found after retry');
|
||||
}
|
||||
@@ -1775,7 +1775,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
for (const selector of directSelectors) {
|
||||
const button = this.page.locator(selector).first();
|
||||
if (await button.count() > 0 && await button.isVisible()) {
|
||||
await this.safeClick(selector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
await this.safeClick(_selector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('info', 'Clicked direct Select button for first search result', { selector });
|
||||
return;
|
||||
}
|
||||
@@ -1788,7 +1788,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
if (await dropdownButton.count() > 0 && await dropdownButton.isVisible()) {
|
||||
// Click dropdown to open menu
|
||||
await this.safeClick(dropdownSelector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('debug', 'Clicked dropdown toggle, waiting for menu', { selector: dropdownSelector });
|
||||
this.log('debug', 'Clicked dropdown toggle, waiting for menu', { _selector: dropdownSelector });
|
||||
|
||||
// Wait for dropdown menu to appear
|
||||
await this.page.waitForSelector('.dropdown-menu.show', { timeout: 3000 }).catch(() => { });
|
||||
@@ -1797,7 +1797,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
const itemSelector = IRACING_SELECTORS.steps.trackSelectDropdownItem;
|
||||
await this.page.waitForTimeout(200);
|
||||
await this.safeClick(itemSelector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('info', 'Clicked first dropdown item to select track config', { selector: itemSelector });
|
||||
this.log('info', 'Clicked first dropdown item to select track config', { _selector: itemSelector });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1805,7 +1805,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
const carRowSelector = '.car-row, .car-item, [data-testid*="car"], [id*="favorite_cars"], [id*="select-car"]';
|
||||
const carRow = this.page.locator(carRowSelector).first();
|
||||
if (await carRow.count() > 0) {
|
||||
this.log('info', 'Fallback: clicking car row/item to select', { selector: carRowSelector });
|
||||
this.log('info', 'Fallback: clicking car row/item to select', { _selector: carRowSelector });
|
||||
// Click the row itself (or its first clickable descendant)
|
||||
try {
|
||||
await this.safeClick(carRowSelector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
@@ -1888,7 +1888,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
(await direct.isVisible().catch(() => false));
|
||||
|
||||
if (hasDirect) {
|
||||
this.log('info', 'Clicking direct New Race button', { selector: directSelector });
|
||||
this.log('info', 'Clicking direct New Race button', { _selector: directSelector });
|
||||
await this.safeClick(directSelector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
} else {
|
||||
const dropdownToggleSelector =
|
||||
@@ -2100,7 +2100,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
}
|
||||
|
||||
try {
|
||||
this.log('debug', `Waiting for wizard step: ${stepName}`, { selector: containerSelector });
|
||||
this.log('debug', `Waiting for wizard step: ${stepName}`, { _selector: containerSelector });
|
||||
// Use 'attached' instead of 'visible' because iRacing wizard steps are marked as
|
||||
// 'active hidden' in the DOM - they exist but are hidden via CSS class
|
||||
await this.page.waitForSelector(containerSelector, {
|
||||
@@ -2129,11 +2129,11 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
// Split combined selectors and try each one
|
||||
const selectors = selector.split(', ').map(s => s.trim());
|
||||
const selectors = _selector.split(', ').map(s => s.trim());
|
||||
|
||||
for (const sel of selectors) {
|
||||
try {
|
||||
this.log('debug', `Trying selector for ${fieldName}`, { selector: sel });
|
||||
this.log('debug', `Trying _selector for ${fieldName}`, { _selector: sel });
|
||||
|
||||
// Check if element exists and is visible
|
||||
const element = this.page.locator(sel).first();
|
||||
@@ -2143,11 +2143,11 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
// Use 'attached' instead of 'visible' because iRacing wizard steps have class="hidden"
|
||||
await element.waitFor({ state: 'attached', timeout });
|
||||
await element.fill(value);
|
||||
this.log('info', `Successfully filled ${fieldName}`, { selector: sel, value });
|
||||
this.log('info', `Successfully filled ${fieldName}`, { _selector: sel, value });
|
||||
return { success: true, fieldName, valueSet: value };
|
||||
}
|
||||
} catch (error) {
|
||||
this.log('debug', `Selector failed for ${fieldName}`, { selector: sel, error: String(error) });
|
||||
this.log('debug', `Selector failed for ${fieldName}`, { _selector: sel, error: String(error) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2155,12 +2155,12 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
try {
|
||||
this.log('debug', `Trying combined selector for ${fieldName}`, { selector });
|
||||
// Use 'attached' instead of 'visible' because iRacing wizard steps have class="hidden"
|
||||
await this.page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await this.page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await this.page.fill(selector, value);
|
||||
return { success: true, fieldName, valueSet: value };
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
this.log('error', `Failed to fill ${fieldName}`, { selector, error: message });
|
||||
this.log('error', `Failed to fill ${fieldName}`, { _selector, error: message });
|
||||
return { success: false, fieldName, valueSet: value, error: message };
|
||||
}
|
||||
}
|
||||
@@ -2194,7 +2194,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
// Some wizard footer buttons are present/attached but not considered "visible" by Playwright
|
||||
// (offscreen, overlapped by overlays, or transitional). Use a forced safe click first,
|
||||
// then fall back to name-based or last-resort selectors if that fails.
|
||||
this.log('debug', 'Attempting next button (primary) with forced click', { selector: nextButtonSelector });
|
||||
this.log('debug', 'Attempting next button (primary) with forced click', { _selector: nextButtonSelector });
|
||||
try {
|
||||
await this.safeClick(nextButtonSelector, { timeout, force: true });
|
||||
this.log('info', `Clicked next button to ${nextStepName} (primary forced)`);
|
||||
@@ -2204,7 +2204,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
}
|
||||
|
||||
// Try fallback with step name (also attempt forced click)
|
||||
this.log('debug', 'Trying fallback next button (forced)', { selector: fallbackSelector });
|
||||
this.log('debug', 'Trying fallback next button (forced)', { _selector: fallbackSelector });
|
||||
try {
|
||||
await this.safeClick(fallbackSelector, { timeout, force: true });
|
||||
this.log('info', `Clicked next button (fallback) to ${nextStepName}`);
|
||||
@@ -2248,7 +2248,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
}
|
||||
|
||||
// Use 'attached' instead of 'visible' because mock fixtures/wizard steps may be present but hidden
|
||||
await this.page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await this.page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await this.safeClick(selector, { timeout });
|
||||
return { success: true, target: selector };
|
||||
}
|
||||
@@ -2260,7 +2260,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
const selector = this.getFieldSelector(fieldName);
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
this.log('debug', 'fillField', { fieldName, selector, mode: this.config.mode });
|
||||
this.log('debug', 'fillField', { fieldName, _selector, mode: this.config.mode });
|
||||
|
||||
// In mock mode, reveal typical fixture-hidden containers to allow Playwright to interact.
|
||||
if (!this.isRealMode()) {
|
||||
@@ -2277,7 +2277,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
}
|
||||
|
||||
// Wait for the element to be attached to the DOM
|
||||
await this.page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await this.page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
|
||||
// Try normal Playwright fill first; fall back to JS injection in mock mode if Playwright refuses due to visibility.
|
||||
try {
|
||||
@@ -2315,7 +2315,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
|
||||
// Try to wait for the canonical selector first
|
||||
try {
|
||||
await this.page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await this.page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await this.page.selectOption(selector, value);
|
||||
return;
|
||||
} catch {
|
||||
@@ -2774,7 +2774,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
// ignore evaluation errors during tests
|
||||
}
|
||||
}
|
||||
await this.page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await this.page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await this.safeClick(selector, { timeout });
|
||||
}
|
||||
|
||||
@@ -2784,7 +2784,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
}
|
||||
// Broaden trigger selector to match multiple fixture variants (buttons, anchors, data-action)
|
||||
const escaped = type.replace(/"/g, '\\"');
|
||||
const selector = `button:has-text("${escaped}"), a:has-text("${escaped}"), [aria-label*="${escaped}" i], [data-action="${escaped}"], [data-modal-trigger="${escaped}"]`;
|
||||
const _selector = `button:has-text("${escaped}"), a:has-text("${escaped}"), [aria-label*="${escaped}" i], [data-action="${escaped}"], [data-modal-trigger="${escaped}"]`;
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
// In mock mode, reveal typical hidden fixture containers so trigger buttons are discoverable.
|
||||
@@ -2802,7 +2802,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
}
|
||||
|
||||
// Use 'attached' instead of 'visible' because iRacing wizard steps have class="hidden"
|
||||
await this.page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await this.page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await this.safeClick(selector, { timeout });
|
||||
}
|
||||
|
||||
@@ -3355,7 +3355,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
* @param currentStep Current step number to determine if modal should be visible
|
||||
* @throws Error with 'WIZARD_DISMISSED' message if modal was closed by user
|
||||
*/
|
||||
async checkWizardDismissed(currentStep: number): Promise<void> {
|
||||
async checkWizardDismissed(_currentStep: number): Promise<void> {
|
||||
if (!this.page || !this.isRealMode() || currentStep < 3) {
|
||||
// Don't check before step 3 (modal opens at step 2)
|
||||
return;
|
||||
@@ -3503,7 +3503,7 @@ export class PlaywrightAutomationAdapter implements IBrowserAutomation, Authenti
|
||||
const locator = this.page.locator(sel).first();
|
||||
const count = await locator.count().catch(() => 0);
|
||||
if (count > 0) {
|
||||
this.log('debug', 'Found checkout candidate button selector', { selector: sel });
|
||||
this.log('debug', 'Found checkout candidate button _selector', { _selector: sel });
|
||||
|
||||
// safeClick will no-op in mock mode if element is hidden and will enforce
|
||||
// verifyNotBlockedElement() in real mode to avoid dangerous clicks.
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as path from 'path';
|
||||
|
||||
import type { LoggerPort } from '@core/automation/application/ports/LoggerPort';
|
||||
import { BrowserModeConfigLoader, BrowserMode } from '../../../config/BrowserModeConfig';
|
||||
import { getAutomationMode } from '../../../config/AutomationConfig';
|
||||
|
||||
import type { PlaywrightConfig } from './PlaywrightAutomationAdapter';
|
||||
import { PlaywrightAutomationAdapter } from './PlaywrightAutomationAdapter';
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { PlaywrightBrowserSession } from './PlaywrightBrowserSession';
|
||||
import { IRacingDomNavigator } from '../dom/IRacingDomNavigator';
|
||||
import { IRacingDomInteractor } from '../dom/IRacingDomInteractor';
|
||||
import { IRACING_SELECTORS } from '../dom/IRacingSelectors';
|
||||
import { getFixtureForStep } from '../engine/FixtureServer';
|
||||
|
||||
import type {
|
||||
PageStateValidation,
|
||||
PageStateValidationResult,
|
||||
@@ -167,7 +167,7 @@ export class WizardStepOrchestrator {
|
||||
await this.navigator.waitForWizardStep(stepName);
|
||||
}
|
||||
|
||||
private async checkWizardDismissed(currentStep: number): Promise<void> {
|
||||
private async checkWizardDismissed(_currentStep: number): Promise<void> {
|
||||
await this.navigator.checkWizardDismissed(currentStep);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { PlaywrightConfig } from '../core/PlaywrightAutomationAdapter';
|
||||
import { PlaywrightBrowserSession } from '../core/PlaywrightBrowserSession';
|
||||
import { IRACING_SELECTORS, IRACING_TIMEOUTS } from './IRacingSelectors';
|
||||
import { SafeClickService } from './SafeClickService';
|
||||
import { getFixtureForStep } from '../engine/FixtureServer';
|
||||
|
||||
|
||||
export class IRacingDomInteractor {
|
||||
constructor(
|
||||
@@ -66,10 +66,10 @@ export class IRacingDomInteractor {
|
||||
const selector = fieldMap[fieldName as keyof typeof fieldMap] ?? IRACING_SELECTORS.fields.textInput;
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
this.log('debug', 'Filling form field', { fieldName, selector, mode: this.config.mode });
|
||||
this.log('debug', 'Filling form field', { fieldName, _selector, mode: this.config.mode });
|
||||
|
||||
try {
|
||||
await page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
|
||||
try {
|
||||
await page.fill(selector, value);
|
||||
@@ -115,8 +115,8 @@ export class IRacingDomInteractor {
|
||||
const selector = this.getActionSelector(target);
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
this.log('debug', 'Clicking element', { target, selector, mode: this.config.mode });
|
||||
await page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
this.log('debug', 'Clicking element', { target, _selector, mode: this.config.mode });
|
||||
await page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await page.click(selector);
|
||||
return { success: true, target };
|
||||
} catch (error) {
|
||||
@@ -165,7 +165,7 @@ export class IRacingDomInteractor {
|
||||
const selector = this.getFieldSelector(fieldName);
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
this.log('debug', 'fillField', { fieldName, selector, mode: this.config.mode });
|
||||
this.log('debug', 'fillField', { fieldName, _selector, mode: this.config.mode });
|
||||
|
||||
if (!this.isRealMode()) {
|
||||
try {
|
||||
@@ -182,7 +182,7 @@ export class IRacingDomInteractor {
|
||||
}
|
||||
}
|
||||
|
||||
await page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
|
||||
try {
|
||||
await page.fill(selector, value);
|
||||
@@ -218,11 +218,11 @@ export class IRacingDomInteractor {
|
||||
const selector = this.getFieldSelector(fieldName);
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
const selectors = selector.split(', ').map((s) => s.trim());
|
||||
const selectors = _selector.split(', ').map((s) => s.trim());
|
||||
|
||||
for (const sel of selectors) {
|
||||
try {
|
||||
this.log('debug', `Trying selector for ${fieldName}`, { selector: sel });
|
||||
this.log('debug', `Trying _selector for ${fieldName}`, { _selector: sel });
|
||||
|
||||
const element = page.locator(sel).first();
|
||||
const isVisible = await element.isVisible().catch(() => false);
|
||||
@@ -230,22 +230,22 @@ export class IRacingDomInteractor {
|
||||
if (isVisible) {
|
||||
await element.waitFor({ state: 'attached', timeout });
|
||||
await element.fill(value);
|
||||
this.log('info', `Successfully filled ${fieldName}`, { selector: sel, value });
|
||||
this.log('info', `Successfully filled ${fieldName}`, { _selector: sel, value });
|
||||
return { success: true, fieldName, valueSet: value };
|
||||
}
|
||||
} catch (error) {
|
||||
this.log('debug', `Selector failed for ${fieldName}`, { selector: sel, error: String(error) });
|
||||
this.log('debug', `Selector failed for ${fieldName}`, { _selector: sel, error: String(error) });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.log('debug', `Trying combined selector for ${fieldName}`, { selector });
|
||||
await page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await page.fill(selector, value);
|
||||
return { success: true, fieldName, valueSet: value };
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
this.log('error', `Failed to fill ${fieldName}`, { selector, error: message });
|
||||
this.log('error', `Failed to fill ${fieldName}`, { _selector, error: message });
|
||||
return { success: false, fieldName, valueSet: value, error: message };
|
||||
}
|
||||
}
|
||||
@@ -273,7 +273,7 @@ export class IRacingDomInteractor {
|
||||
selector = this.getActionSelector(action);
|
||||
}
|
||||
|
||||
await page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await this.safeClickService.safeClick(selector, { timeout });
|
||||
return { success: true, target: selector };
|
||||
}
|
||||
@@ -316,7 +316,7 @@ export class IRacingDomInteractor {
|
||||
const fallbackSelector = `.wizard-footer a.btn:has-text("${nextStepName}")`;
|
||||
|
||||
try {
|
||||
this.log('debug', 'Attempting next button (primary) with forced click', { selector: nextButtonSelector });
|
||||
this.log('debug', 'Attempting next button (primary) with forced click', { _selector: nextButtonSelector });
|
||||
try {
|
||||
await this.safeClickService.safeClick(nextButtonSelector, { timeout, force: true });
|
||||
this.log('info', `Clicked next button to ${nextStepName} (primary forced)`);
|
||||
@@ -325,7 +325,7 @@ export class IRacingDomInteractor {
|
||||
this.log('debug', 'Primary forced click failed, falling back', { error: String(e) });
|
||||
}
|
||||
|
||||
this.log('debug', 'Trying fallback next button (forced)', { selector: fallbackSelector });
|
||||
this.log('debug', 'Trying fallback next button (forced)', { _selector: fallbackSelector });
|
||||
try {
|
||||
await this.safeClickService.safeClick(fallbackSelector, { timeout, force: true });
|
||||
this.log('info', `Clicked next button (fallback) to ${nextStepName}`);
|
||||
@@ -350,7 +350,7 @@ export class IRacingDomInteractor {
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
try {
|
||||
await page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await page.selectOption(selector, value);
|
||||
return;
|
||||
} catch {
|
||||
@@ -754,14 +754,14 @@ export class IRacingDomInteractor {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
await page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await this.safeClickService.safeClick(selector, { timeout });
|
||||
}
|
||||
|
||||
async openModalTrigger(type: string): Promise<void> {
|
||||
const page = this.getPage();
|
||||
const escaped = type.replace(/"/g, '\\"');
|
||||
const selector = `button:has-text("${escaped}"), a:has-text("${escaped}"), [aria-label*="${escaped}" i], [data-action="${escaped}"], [data-modal-trigger="${escaped}"]`;
|
||||
const _selector = `button:has-text("${escaped}"), a:has-text("${escaped}"), [aria-label*="${escaped}" i], [data-action="${escaped}"], [data-modal-trigger="${escaped}"]`;
|
||||
const timeout = this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout;
|
||||
|
||||
if (!this.isRealMode()) {
|
||||
@@ -779,7 +779,7 @@ export class IRacingDomInteractor {
|
||||
}
|
||||
}
|
||||
|
||||
await page.waitForSelector(selector, { state: 'attached', timeout });
|
||||
await page.waitForSelector(_selector, { state: 'attached', timeout });
|
||||
await this.safeClickService.safeClick(selector, { timeout });
|
||||
}
|
||||
|
||||
@@ -816,7 +816,7 @@ export class IRacingDomInteractor {
|
||||
timeout: this.isRealMode() ? IRACING_TIMEOUTS.elementWait : this.config.timeout,
|
||||
});
|
||||
await page.waitForTimeout(150);
|
||||
this.log('info', 'Add Car modal is visible', { selector: modalSelector });
|
||||
this.log('info', 'Add Car modal is visible', { _selector: modalSelector });
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
this.log('warn', 'Add Car modal not found with primary selector, dumping #create-race-wizard innerHTML and retrying', {
|
||||
@@ -832,7 +832,7 @@ export class IRacingDomInteractor {
|
||||
timeout: 10000,
|
||||
});
|
||||
await page.waitForTimeout(150);
|
||||
this.log('info', 'Add Car modal found after retry', { selector: modalSelectorRetry });
|
||||
this.log('info', 'Add Car modal found after retry', { _selector: modalSelectorRetry });
|
||||
} catch {
|
||||
this.log('warn', 'Add Car modal still not found after retry');
|
||||
}
|
||||
@@ -885,7 +885,7 @@ export class IRacingDomInteractor {
|
||||
for (const selector of directSelectors) {
|
||||
const button = page.locator(selector).first();
|
||||
if ((await button.count()) > 0 && (await button.isVisible())) {
|
||||
await this.safeClickService.safeClick(selector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
await this.safeClickService.safeClick(_selector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('info', 'Clicked direct Select button for first search result', { selector });
|
||||
return;
|
||||
}
|
||||
@@ -896,14 +896,14 @@ export class IRacingDomInteractor {
|
||||
|
||||
if ((await dropdownButton.count()) > 0 && (await dropdownButton.isVisible())) {
|
||||
await this.safeClickService.safeClick(dropdownSelector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('debug', 'Clicked dropdown toggle, waiting for menu', { selector: dropdownSelector });
|
||||
this.log('debug', 'Clicked dropdown toggle, waiting for menu', { _selector: dropdownSelector });
|
||||
|
||||
await page.waitForSelector('.dropdown-menu.show', { timeout: 3000 }).catch(() => {});
|
||||
|
||||
const itemSelector = IRACING_SELECTORS.steps.trackSelectDropdownItem;
|
||||
await page.waitForTimeout(200);
|
||||
await this.safeClickService.safeClick(itemSelector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('info', 'Clicked first dropdown item to select track config', { selector: itemSelector });
|
||||
this.log('info', 'Clicked first dropdown item to select track config', { _selector: itemSelector });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -911,7 +911,7 @@ export class IRacingDomInteractor {
|
||||
'.car-row, .car-item, [data-testid*="car"], [id*="favorite_cars"], [id*="select-car"]';
|
||||
const carRow = page.locator(carRowSelector).first();
|
||||
if ((await carRow.count()) > 0) {
|
||||
this.log('info', 'Fallback: clicking car row/item to select', { selector: carRowSelector });
|
||||
this.log('info', 'Fallback: clicking car row/item to select', { _selector: carRowSelector });
|
||||
try {
|
||||
await this.safeClickService.safeClick(carRowSelector, { timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('info', 'Clicked car row fallback selector');
|
||||
@@ -1078,7 +1078,7 @@ export class IRacingDomInteractor {
|
||||
|
||||
if (isVisible) {
|
||||
await radioLabel.click({ timeout: IRACING_TIMEOUTS.elementWait });
|
||||
this.log('info', 'Selected weather type', { weatherType, selector: labelSelector });
|
||||
this.log('info', 'Selected weather type', { weatherType, _selector: labelSelector });
|
||||
} else {
|
||||
this.log('debug', 'Weather type radio not visible, may already be selected or step is different');
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export class IRacingDomNavigator {
|
||||
selector = IRACING_SELECTORS.wizard.modal;
|
||||
}
|
||||
|
||||
this.log('debug', 'Waiting for element', { target, selector, mode: this.config.mode });
|
||||
this.log('debug', 'Waiting for element', { target, _selector, mode: this.config.mode });
|
||||
await page.waitForSelector(selector, {
|
||||
state: 'attached',
|
||||
timeout: maxWaitMs ?? defaultTimeout,
|
||||
@@ -164,7 +164,7 @@ export class IRacingDomNavigator {
|
||||
}
|
||||
|
||||
try {
|
||||
this.log('debug', `Waiting for wizard step: ${stepName}`, { selector: containerSelector });
|
||||
this.log('debug', `Waiting for wizard step: ${stepName}`, { _selector: containerSelector });
|
||||
await page.waitForSelector(containerSelector, {
|
||||
state: 'attached',
|
||||
timeout: 15000,
|
||||
@@ -297,7 +297,7 @@ export class IRacingDomNavigator {
|
||||
}
|
||||
}
|
||||
|
||||
async checkWizardDismissed(currentStep: number): Promise<void> {
|
||||
async checkWizardDismissed(_currentStep: number): Promise<void> {
|
||||
if (!this.isRealMode() || currentStep < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export class SafeClickService {
|
||||
* @param elementText Optional text content of the element (should be direct text only)
|
||||
* @returns true if the selector/text matches a blocked pattern
|
||||
*/
|
||||
private isBlockedSelector(selector: string, elementText?: string): boolean {
|
||||
private isBlockedSelector(_selector: string, elementText?: string): boolean {
|
||||
const selectorLower = selector.toLowerCase();
|
||||
const textLower = elementText?.toLowerCase().trim() ?? '';
|
||||
|
||||
@@ -88,7 +88,7 @@ export class SafeClickService {
|
||||
* @param selector The CSS selector of the element to verify
|
||||
* @throws Error if element is a blocked checkout/payment button
|
||||
*/
|
||||
async verifyNotBlockedElement(selector: string): Promise<void> {
|
||||
async verifyNotBlockedElement(_selector: string): Promise<void> {
|
||||
const page = this.browserSession.getPage();
|
||||
if (!page) return;
|
||||
|
||||
@@ -191,7 +191,7 @@ export class SafeClickService {
|
||||
if (error instanceof Error && error.message.includes('BLOCKED')) {
|
||||
throw error;
|
||||
}
|
||||
this.log('debug', 'Could not verify element (may not exist yet)', { selector, error: String(error) });
|
||||
this.log('debug', 'Could not verify element (may not exist yet)', { _selector, error: String(error) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ export class SafeClickService {
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
const useForce = options?.force || attempt === maxRetries;
|
||||
await page.click(selector, { timeout, force: useForce });
|
||||
await page.click(_selector, { timeout, force: useForce });
|
||||
return;
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('BLOCKED')) {
|
||||
@@ -418,7 +418,7 @@ export class SafeClickService {
|
||||
this.log('debug', 'JS fallback click did not find element or failed', { selector });
|
||||
}
|
||||
} catch (e) {
|
||||
this.log('debug', 'JS fallback click error', { selector, error: String(e) });
|
||||
this.log('debug', 'JS fallback click error', { _selector, error: String(e) });
|
||||
}
|
||||
|
||||
this.log('error', 'Max retries reached, click still blocked', { selector });
|
||||
|
||||
@@ -70,7 +70,7 @@ export class MockBrowserAutomationAdapter implements IBrowserAutomation, IAutoma
|
||||
};
|
||||
}
|
||||
|
||||
async clickElement(selector: string): Promise<ClickResultDTO> {
|
||||
async clickElement(_selector: string): Promise<ClickResultDTO> {
|
||||
const delay = this.randomDelay(50, 300);
|
||||
await this.sleep(delay);
|
||||
return {
|
||||
@@ -79,7 +79,7 @@ export class MockBrowserAutomationAdapter implements IBrowserAutomation, IAutoma
|
||||
};
|
||||
}
|
||||
|
||||
async waitForElement(selector: string, maxWaitMs: number = 5000): Promise<WaitResultDTO> {
|
||||
async waitForElement(_selector: string, maxWaitMs: number = 5000): Promise<WaitResultDTO> {
|
||||
const delay = this.randomDelay(100, 1000);
|
||||
|
||||
await this.sleep(delay);
|
||||
|
||||
@@ -3,17 +3,17 @@ import type { LogContext } from '../../../application/ports/LoggerContext';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
|
||||
export class NoOpLogAdapter implements LoggerPort, Logger {
|
||||
debug(_message: string, _context?: LogContext): void {}
|
||||
debug(__message: string, __context?: LogContext): void {}
|
||||
|
||||
info(_message: string, _context?: LogContext): void {}
|
||||
info(__message: string, __context?: LogContext): void {}
|
||||
|
||||
warn(_message: string, _context?: LogContext): void {}
|
||||
warn(__message: string, __context?: LogContext): void {}
|
||||
|
||||
error(_message: string, _error?: Error, _context?: LogContext): void {}
|
||||
error(__message: string, __error?: Error, __context?: LogContext): void {}
|
||||
|
||||
fatal(_message: string, _error?: Error, _context?: LogContext): void {}
|
||||
fatal(__message: string, __error?: Error, __context?: LogContext): void {}
|
||||
|
||||
child(_context: LogContext): LoggerPort {
|
||||
child(__context: LogContext): LoggerPort {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AutomationSession } from '../../domain/entities/AutomationSession';
|
||||
import { SessionStateValue } from '../../domain/value-objects/SessionState';
|
||||
|
||||
import type { SessionRepositoryPort } from '../../application/ports/SessionRepositoryPort';
|
||||
|
||||
export class InMemorySessionRepository implements SessionRepositoryPort {
|
||||
|
||||
Reference in New Issue
Block a user