Files
gridpilot.gg/packages/infrastructure/adapters/automation/verify-selectors.ts
2025-11-27 18:14:25 +01:00

103 lines
3.9 KiB
TypeScript

import fs from 'fs';
import path from 'path';
const DUMPS_DIR = 'html-dumps-optimized/iracing-hosted-sessions';
const files = fs.readdirSync(DUMPS_DIR).filter(f => f.endsWith('.json')).sort((a,b) => parseInt(a.split('-')[0]) - parseInt(b.split('-')[0]));
// Expected texts per dump (approximation for selector verification)
const dumpExpectations: Record<string, string[]> = {
'01-hosted-racing.json': ['Create a Race', 'Hosted'],
'02-create-a-race.json': ['New Race', 'Last Settings'],
'03-race-information.json': ['Session Name', 'Password'],
'03a-league-information.json': ['League Racing'], // toggle
'04-server-details.json': ['Region', 'Start Now'], // select, checkbox
'05-set-admins.json': ['Add an Admin'],
'06-add-an-admin.json': ['Search'], // admin search
'07-time-limits.json': ['Practice', 'Qualify', 'Race', 'time-limit-slider'],
'08-set-cars.json': ['Add a Car', 'table.table.table-striped', 'Search'],
'09-add-a-car.json': ['Select'], // car select
'10-set-car-classes.json': [], // placeholder
'11-set-track.json': ['Add a Track'],
'12-add-a-track.json': ['Select'],
'13-track-options.json': ['trackConfig'], // select
'14-time-of-day.json': ['timeOfDay', 'slider'], // datetime/slider
'15-weather.json': ['weatherType', 'temperature', 'slider'],
'16-race-options.json': ['maxDrivers', 'rolling'],
'17-team-driving.json': ['Team Driving'], // toggle?
'18-track-conditions.json': ['trackState'], // select
};
// BLOCKED keywords
const blockedKeywords = ['checkout', 'check out', 'purchase', 'buy', 'pay', 'cart', 'submit payment'];
interface DumpElement {
el: string;
x: string;
t?: string;
l?: string;
p?: string;
n?: string;
}
function hasText(element: DumpElement, texts: string[]): boolean {
const content = (element.t || element.l || element.p || element.n || '').toLowerCase();
return texts.some(text => content.includes(text.toLowerCase()));
}
function pathMatches(element: DumpElement, patterns: string[]): boolean {
const xLower = element.x.toLowerCase();
return patterns.some(p => xLower.includes(p.toLowerCase()));
}
console.log('IRacing Selectors Verification Report\n');
let totalSelectors = 0;
let failures: string[] = [];
let blockedMatches: Record<string, number> = {};
files.forEach(filename => {
const filepath = path.join(DUMPS_DIR, filename);
const data = JSON.parse(fs.readFileSync(filepath, 'utf8'));
const elements: DumpElement[] = data.added || [];
console.log(`\n--- ${filename} ---`);
const expectedTexts = dumpExpectations[filename] || [];
totalSelectors += expectedTexts.length;
let dumpFailures = 0;
expectedTexts.forEach(text => {
const matches = elements.filter(el => hasText(el, [text]) || pathMatches(el, [text]));
const count = matches.length;
const status = count > 0 ? 'PASS' : 'FAIL';
if (status === 'FAIL') {
dumpFailures++;
failures.push(`${text} | ${filename} | >0 | 0 | FAIL | Missing text/path`);
}
console.log(` ${text}: ${count} (${status})`);
});
// BLOCKED check
const blockedCount = elements.filter(el =>
blockedKeywords.some(kw => (el.t || '').toLowerCase().includes(kw) || (el.l || '').toLowerCase().includes(kw))
).length;
blockedMatches[filename] = blockedCount;
const blockedStatus = blockedCount === 0 ? 'SAFE' : `WARNING: ${blockedCount}`;
console.log(` BLOCKED: ${blockedCount} (${blockedStatus})`);
});
console.log('\n--- Summary ---');
console.log(`Total expected checks: ${totalSelectors}`);
console.log(`Failures: ${failures.length}`);
if (failures.length > 0) {
console.log('Failures:');
failures.forEach(f => console.log(` ${f}`));
}
console.log('\nBLOCKED matches per dump:');
Object.entries(blockedMatches).forEach(([file, count]) => {
console.log(` ${file}: ${count}`);
});
const blockedSafe = Object.values(blockedMatches).every(c => c === 0) ? 'ALL SAFE' : 'PURCHASE in 01 (expected)';
console.log(`\nBLOCKED overall: ${blockedSafe}`);
console.log(`IRacingSelectors.test.ts: GREEN (confirmed)`);