Add XCUITest authoring docs and templates

This commit is contained in:
treyt
2026-02-18 13:24:46 -06:00
parent 20ac1a7e59
commit 7e54ff2ef2
6 changed files with 252 additions and 1 deletions

27
AGENTS.md Normal file
View File

@@ -0,0 +1,27 @@
# SportsTime Agent Notes
This file is for AI/code agents working in this repo.
## UI Test Docs
- Start with `XCUITest-Authoring.md`.
- Use `XCUITestSuiteTemplate.swift` when creating new suites.
- Use `uiTestPrompt.md` when asking an agent to add/fix UI tests.
## UI Test Ground Rules
- Keep new UI tests in `SportsTimeUITests/Tests/`.
- Reuse page objects in `SportsTimeUITests/Framework/Screens.swift`.
- Reuse shared setup and helpers from `SportsTimeUITests/Framework/BaseUITestCase.swift`.
- Reuse high-level flows from `TestFlows` before adding duplicate test steps.
- Prefer robust waits (`waitForExistenceOrFail`, `waitUntilHittable`) over sleeps.
- Use existing accessibility identifiers (`wizard.*`, `tripOptions.*`, `tripDetail.*`, etc.).
- Capture screenshots for key checkpoints in longer end-to-end tests.
## Before Merging UI Test Changes
- Run the touched test class.
- Run full UI suite:
- `xcodebuild test-without-building -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -parallel-testing-enabled NO -only-testing:SportsTimeUITests`
- Run full scheme verification if behavior touched shared flows:
- `xcodebuild test-without-building -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -parallel-testing-enabled NO`

View File

@@ -17,6 +17,9 @@ xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platfo
# Run a single test
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -only-testing:SportsTimeTests/TripPlanningEngineTests/planningMode_dateRange test
# Run UI tests only
xcodebuild test-without-building -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -parallel-testing-enabled NO -only-testing:SportsTimeUITests
# Data scraping (Python)
cd Scripts && pip install -r requirements.txt
python scrape_schedules.py --sport all --season 2026
@@ -298,7 +301,10 @@ View → modelContext.delete(model) → modelContext.save() → reload data →
- **Test directory**: `SportsTimeTests/` — mirrors source structure
- **File naming**: `{ClassName}Tests.swift` or `{Feature}Tests.swift`
- **Helper files**: `SportsTimeTests/Helpers/MockServices.swift`, `SportsTimeTests/Helpers/TestFixtures.swift`
- **UI tests**: `SportsTimeUITests/` (template only, not actively used)
- **UI tests**: `SportsTimeUITests/` is active and uses XCTest + page-object patterns
- **UI authoring guide**: `XCUITest-Authoring.md`
- **UI suite template**: `XCUITestSuiteTemplate.swift`
- **UI request template**: `uiTestPrompt.md`
### Existing Test Suites

View File

@@ -70,6 +70,7 @@ SportsTime/
│ ├── Export/ # PDF generation
│ └── Resources/ # Bundled JSON data
├── SportsTimeTests/ # Unit tests
├── SportsTimeUITests/ # UI test suites + screen objects
├── Scripts/ # Python data pipeline
│ └── sportstime_parser/ # Schedule scraping & CloudKit upload
├── data/ # Local data files
@@ -124,6 +125,14 @@ xcodebuild -project SportsTime.xcodeproj \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
-only-testing:SportsTimeTests/EdgeCaseTests \
test
# UI test target only
xcodebuild test-without-building \
-project SportsTime.xcodeproj \
-scheme SportsTime \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
-parallel-testing-enabled NO \
-only-testing:SportsTimeUITests
```
### Data Pipeline
@@ -151,7 +160,11 @@ python -m sportstime_parser upload --sport all
## Documentation
- `CLAUDE.md` - Development guidelines and architecture details
- `AGENTS.md` - Agent-specific execution notes for this repo
- `ARCHITECTURE.md` - Detailed system architecture
- `XCUITest-Authoring.md` - How existing UI tests are structured and how to add new ones
- `XCUITestSuiteTemplate.swift` - Starter suite template for new UI test files
- `uiTestPrompt.md` - Reusable prompt template for requesting UI test work
- `docs/TEST_PLAN.md` - Test suite documentation
- `docs/MARKET_RESEARCH.md` - Competitive analysis

108
XCUITest-Authoring.md Normal file
View File

@@ -0,0 +1,108 @@
# XCUITest Authoring Guide
This guide documents the current SportsTime UI test foundation and how to add new tests safely.
## Current Foundation
## Layout
- Test target: `SportsTimeUITests`
- Base setup/helpers: `SportsTimeUITests/Framework/BaseUITestCase.swift`
- Page objects + shared flows: `SportsTimeUITests/Framework/Screens.swift`
- Suite files: `SportsTimeUITests/Tests/*.swift`
- Legacy mixed tests: `SportsTimeUITests/SportsTimeUITests.swift` and `SportsTimeUITests/SportsTimeUITestsLaunchTests.swift`
## Existing Suite Coverage
- `AppLaunchTests`
- `HomeTests`
- `TabNavigationTests`
- `ScheduleTests`
- `TripWizardFlowTests`
- `TripOptionsTests`
- `TripSavingTests`
- `ProgressTests`
- `SettingsTests`
- `AccessibilityTests`
- `StabilityTests`
## Key Conventions
- Inherit from `BaseUITestCase`.
- Use `@MainActor` test methods.
- Prefer page-object actions over direct element taps.
- Prefer existing high-level flows (`TestFlows.planDateRangeTrip`, `TestFlows.planAndSelectFirstTrip`) for shared setup.
- Use deterministic selectors with accessibility identifiers.
- Use `waitForExistenceOrFail` and `waitUntilHittable` instead of arbitrary sleeps.
## Adding a New UI Test
1. Pick the closest existing suite in `SportsTimeUITests/Tests/`.
2. If none fits, create a new suite using `XCUITestSuiteTemplate.swift`.
3. Add/extend page-object methods in `Screens.swift` before writing raw element code.
4. Reuse `TestFlows` when setup overlaps existing end-to-end flows.
5. Keep assertion messages explicit and behavior-focused.
6. Capture screenshot(s) for key milestone state in longer flows.
7. Run targeted tests, then full UI tests.
## When to Edit `Screens.swift`
Edit page objects when:
- A new screen element needs a stable selector.
- The interaction is reused across multiple tests.
- The flow can be standardized (especially wizard planning flow).
Do not add one-off test-only branching logic unless it removes a real flake.
## Running Tests
## Fast Loop (single test)
```bash
xcodebuild test-without-building \
-project SportsTime.xcodeproj \
-scheme SportsTime \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
-parallel-testing-enabled NO \
-only-testing:SportsTimeUITests/TripWizardFlowTests/testF026_DateRangeSelection
```
## Per Class
```bash
xcodebuild test-without-building \
-project SportsTime.xcodeproj \
-scheme SportsTime \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
-parallel-testing-enabled NO \
-only-testing:SportsTimeUITests/TripOptionsTests
```
## Full UI Suite
```bash
xcodebuild test-without-building \
-project SportsTime.xcodeproj \
-scheme SportsTime \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
-parallel-testing-enabled NO \
-only-testing:SportsTimeUITests
```
## Full Scheme Validation (unit + UI)
```bash
xcodebuild test-without-building \
-project SportsTime.xcodeproj \
-scheme SportsTime \
-destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' \
-parallel-testing-enabled NO
```
## Flake Prevention Notes
- Keep simulator orientation consistent (portrait baseline from `BaseUITestCase`).
- For wizard date selection, navigate by month/year first and use day-cell fallback when specific IDs are unavailable.
- For cross-season planning tests, prefer deterministic fallback sports if selected sport has no viable schedule for current test data.
- Increase waits only where planning computation is the actual bottleneck.

View File

@@ -0,0 +1,48 @@
//
// __FeatureName__Tests.swift
// SportsTimeUITests
//
// Copy this file into SportsTimeUITests/Tests and rename placeholders.
//
import XCTest
final class __FeatureName__Tests: BaseUITestCase {
// MARK: - Helpers
@MainActor
private func openHome() -> HomeScreen {
let home = HomeScreen(app: app)
home.waitForLoad()
return home
}
@MainActor
private func openWizard() -> TripWizardScreen {
let home = openHome()
home.tapStartPlanning()
let wizard = TripWizardScreen(app: app)
wizard.waitForLoad()
return wizard
}
// MARK: - Tests
/// Replace with test case ID and behavior, e.g. F-200.
@MainActor
func testF___BehaviorName() {
let wizard = openWizard()
wizard.selectDateRangeMode()
// Add scenario actions
// wizard.selectSport("mlb")
// wizard.selectRegion("central")
// Add assertions
XCTAssertTrue(wizard.planTripButton.exists, "Plan button should be visible")
captureScreenshot(named: "F___-BehaviorName")
}
}

49
uiTestPrompt.md Normal file
View File

@@ -0,0 +1,49 @@
# UI Test Prompt Template
Use this prompt when asking an agent to add or modify SportsTime UI tests.
---
You are updating SportsTime UI tests.
## Goal
- [Describe the behavior to test or fix.]
## Scope
- Update/add tests under `SportsTimeUITests/Tests/`.
- Reuse page objects in `SportsTimeUITests/Framework/Screens.swift`.
- Reuse shared setup in `SportsTimeUITests/Framework/BaseUITestCase.swift`.
- Reuse existing `TestFlows` where possible.
## Required Changes
- [List test suites to modify.]
- [List new test names.]
- [List selectors/page-object methods to add if needed.]
## Constraints
- Do not add raw sleeps unless strictly necessary.
- Prefer `waitForExistenceOrFail` and `waitUntilHittable`.
- Keep tests deterministic with current local test data.
- Keep existing naming style (`testF###_...`, `testP###_...`, etc.).
## Validation
Run these before finishing:
1. Targeted class or test:
- `xcodebuild test-without-building -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -parallel-testing-enabled NO -only-testing:SportsTimeUITests/<SuiteOrTest>`
2. Full UI suite:
- `xcodebuild test-without-building -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -parallel-testing-enabled NO -only-testing:SportsTimeUITests`
3. If requested, full scheme:
- `xcodebuild test-without-building -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' -parallel-testing-enabled NO`
## Output Format
- Summarize files changed.
- Summarize root causes fixed.
- Include exact commands run and pass/fail outcomes.
- Call out any remaining flaky behavior or follow-up work.