106 lines
7.3 KiB
Markdown
106 lines
7.3 KiB
Markdown
# iRacing Selectors Update Plan
|
|
|
|
**Date:** 2025-11-27
|
|
**Based on:** HTML dumps from `html-dumps-optimized/iracing-hosted-sessions/` (01-18) vs [`IRacingSelectors.ts`](packages/infrastructure/adapters/automation/IRacingSelectors.ts).
|
|
**Goal:** Verify selectors against recent dumps, propose updates for stability (React/Chakra UI resilience), prioritize fixes.
|
|
|
|
## Clean Architecture Impact
|
|
Selectors adhere to Clean Arch by relying on stable attributes (text, aria-label, data-testid, IDs like #set-*) rather than volatile classes. Updates reinforce this: prefer `:has-text()`, `data-testid`, label proximity over class names. No cross-layer leaks; selectors are pure infrastructure adapters.
|
|
|
|
## Priority Summary
|
|
| Priority | Count | Examples |
|
|
|----------|-------|----------|
|
|
| **Critical** (broken) | 2 | `adminList` (no [data-list="admins"]), generic sliders (risky ID match) |
|
|
| **Recommended** (stability) | 8 | Time sliders (add label context), fields (add chakra-), unconfirmed fields (label-for/placeholder) |
|
|
| **Optional** (enhancements) | 5 | Add Car/Track buttons (dynamic count handling), BLOCKED_SELECTORS (chakra-button) |
|
|
| **Verified/Matches** | 70+ | Wizard nav/step IDs, most buttons/text |
|
|
|
|
**Total selectors needing updates: 15**
|
|
|
|
## Selector Verification Tables
|
|
|
|
### login
|
|
| Selector | Current Selector | Status | Evidence (Dump) | Proposed | Priority |
|
|
|----------|------------------|--------|-----------------|----------|----------|
|
|
| emailInput | `#username, input[name="username"], input[type="email"]` | Unconfirmed | No login dump | N/A | - |
|
|
| passwordInput | `#password, input[type="password"]` | Unconfirmed | No login dump | N/A | - |
|
|
| submitButton | `button[type="submit"], button:has-text("Sign In")` | Unconfirmed | No login dump | N/A | - |
|
|
|
|
### hostedRacing
|
|
| Selector | Current Selector | Status | Evidence (Dump) | Proposed | Priority |
|
|
|----------|------------------|--------|-----------------|----------|----------|
|
|
| createRaceButton | `button:has-text("Create a Race"), button[aria-label="Create a Race"]` | Matches | 01-hosted-racing.json: `bu.chakra-button:0 t:"Create a Race"` | N/A | Verified |
|
|
| hostedTab | `a:has-text("Hosted")` | Matches | 01: sidebar `a.c0:2 t:"Hosted"` | N/A | Verified |
|
|
| createRaceModal | `#modal-children-container, .modal-content` | Matches | 02: `#confirm-create-race-modal-modal-content` | N/A | Verified |
|
|
| newRaceButton | `a.btn:has-text("New Race")` | Matches | 02: `a.btn.btn-lg:1 t:"New Race"` | N/A | Verified |
|
|
| lastSettingsButton | `a.btn:has-text("Last Settings")` | Matches | 02: `a.btn.btn-lg:0 t:"Last Settings"` | N/A | Verified |
|
|
|
|
### wizard
|
|
#### Core
|
|
| Selector | Current Selector | Status | Evidence | Proposed | Priority |
|
|
|----------|------------------|--------|-----------|----------|----------|
|
|
| modal | `#create-race-modal-modal-content, .modal-content` | Matches | All dumps: `#create-race-modal-modal-content` | N/A | Verified |
|
|
| modalDialog | `.modal-dialog` | Matches | Dumps: `#create-race-modal-modal-dialog` | N/A | Verified |
|
|
| modalContent | `#create-race-modal-modal-content, .modal-content` | Matches | Dumps | N/A | Verified |
|
|
| modalTitle | `[data-testid="modal-title"], .modal-title` | Unconfirmed | No exact match | `[data-testid="modal-title"]` | Optional |
|
|
| nextButton | `.wizard-footer a.btn:last-child` | Matches | 03,05,07: `d.wizard-footer@4>d.pull-xs-left>a.btn.btn-sm:1` (dynamic text) | N/A | Verified |
|
|
| backButton | `.wizard-footer a.btn:first-child` | Matches | Dumps: first-child | N/A | Verified |
|
|
| confirmButton | `.modal-footer a.btn-success, button:has-text("Confirm")` | Unconfirmed | No final confirm dump | N/A | - |
|
|
| cancelButton | `.modal-footer a.btn-secondary:has-text("Back")` | Matches | Dumps: "Back" | N/A | Verified |
|
|
| closeButton | `[data-testid="button-close-modal"]` | Matches | Dumps: `data-testid=button-close-modal` | N/A | Verified |
|
|
|
|
#### sidebarLinks (all Matches - data-testid exact)
|
|
| Selector | Status | Evidence |
|
|
|----------|--------|----------|
|
|
| raceInformation | Matches | 03+: `data-testid=wizard-nav-set-session-information` |
|
|
| ... (all 11) | Matches | Exact data-testid in 03,05,07,08 |
|
|
|
|
#### stepContainers (all Matches - #set-* IDs)
|
|
| Selector | Status | Evidence |
|
|
|----------|--------|----------|
|
|
| raceInformation (#set-session-information) | Matches | 03 |
|
|
| admins (#set-admins) | Matches | 05 |
|
|
| timeLimit (#set-time-limit) | Matches | 07 |
|
|
| cars (#set-cars) | Matches | 08 |
|
|
| ... (all 11) | Matches | Dumps |
|
|
|
|
### fields (Recommended: Add chakra- for stability)
|
|
| Selector | Current | Status | Evidence | Proposed | Priority |
|
|
|----------|---------|--------|----------|----------|----------|
|
|
| textInput | `input.form-control, .chakra-input, ...` | Matches | Chakra inputs in dumps | `.chakra-input, input[placeholder], input[type="text"]` | Recommended |
|
|
| ... (similar for others) | Partial | Chakra dominant | Add chakra- prefixes | Recommended |
|
|
|
|
### steps (Key issues highlighted)
|
|
| Selector | Current | Status | Evidence (Dump) | Proposed | Priority |
|
|
|----------|---------|--------|-----------------|----------|----------|
|
|
| sessionName | `#set-session-information .card-block .form-group:first-of-type input.form-control, ...` | Unconfirmed | 03: form-groups, chakra-input | `label:has-text("Session Name") ~ input.chakra-input` | Recommended |
|
|
| password | Complex | Unconfirmed | 03 | `label:has-text("Password") ~ input[type="password"], input[placeholder*="Password"]` | Recommended |
|
|
| adminList | `[data-list="admins"]` | No Match | 05: no data-list; #set-admins card | `#set-admins table.table.table-striped, #set-admins .card-block table` | Critical |
|
|
| practice | `input[id*="time-limit-slider"]` | Matches but risky | 07: `time-limit-slider1764248520320` | `label:has-text("Practice") ~ div input[id*="time-limit-slider"]` | Recommended |
|
|
| qualify/race | Similar | Matches risky | 07 | Label proximity | Recommended |
|
|
| addCarButton | `a.btn:has-text("Add a Car")` | Matches | 08: `a.btn.btn-sm t:"Add a Car 16 Available"` | `a.btn:has-text("Add a Car")` (handles dynamic) | Verified |
|
|
| carList | `table.table.table-striped` | Matches | 08: many `table.table.table-striped` | `#set-cars table.table.table-striped` | Verified |
|
|
| ... (track similar) | Matches | 08+ | N/A | Verified |
|
|
|
|
### BLOCKED_SELECTORS (Optional: Chakra enhancements)
|
|
| Selector | Status | Proposed | Priority |
|
|
|----------|--------|----------|----------|
|
|
| checkout | Matches | Add `.chakra-button:has-text("Check Out")` | Optional |
|
|
| ... | Matches | Minor | Optional |
|
|
|
|
## BDD Scenarios for Verification
|
|
- GIVEN hosted page (01), THEN `hostedRacing.createRaceButton` finds 1 button.
|
|
- GIVEN #set-admins (05), THEN `steps.adminList` finds 1 table; `addAdminButton` finds 1.
|
|
- GIVEN time-limits (07), THEN `steps.practice` finds 1 slider near "Practice" label.
|
|
- GIVEN cars (08), THEN `carList` finds table; `addCarButton:has-text("Add a Car")` finds 1.
|
|
- GIVEN any step, THEN `wizard.nextButton:last-child` enabled, finds 1.
|
|
|
|
**Run via Playwright: `expect(page.locator(selector)).toHaveCount(1)` per scenario.**
|
|
|
|
## Docker E2E Impacts
|
|
No major changes; selectors stable. Minor fixture updates if sliders refined (update E2ETestBrowserLauncher.ts expectations). Test post-update.
|
|
|
|
## Implementation Roadmap (for Code mode)
|
|
1. Apply Critical/Recommended updates via apply_diff.
|
|
2. Verify with browser_action on local iRacing mock/fixture.
|
|
3. Add BDD tests in tests/. |