Add XCUITest authoring docs and reusable prompt template
This commit is contained in:
85
docs/XCUITest-Authoring.md
Normal file
85
docs/XCUITest-Authoring.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# XCUITest Authoring Guide
|
||||
|
||||
This document defines the required pattern for writing or modifying UI tests in this repository.
|
||||
|
||||
If a prompt says "create a UI test that does X", follow this guide exactly.
|
||||
|
||||
## Foundation Map
|
||||
|
||||
- Base class: `/Users/treyt/Desktop/code/Feels/Tests iOS/Helpers/BaseUITestCase.swift`
|
||||
- Wait + ID helpers: `/Users/treyt/Desktop/code/Feels/Tests iOS/Helpers/WaitHelpers.swift`
|
||||
- Screen objects: `/Users/treyt/Desktop/code/Feels/Tests iOS/Screens/`
|
||||
- Accessibility IDs source: `/Users/treyt/Desktop/code/Feels/Shared/AccessibilityIdentifiers.swift`
|
||||
- Test-mode launch and fixtures: `/Users/treyt/Desktop/code/Feels/Shared/UITestMode.swift`
|
||||
|
||||
## Non-Negotiable Rules
|
||||
|
||||
- Use `BaseUITestCase` for UI test suites.
|
||||
- Use `UITestID` / accessibility identifiers as primary selectors.
|
||||
- Use screen objects for navigation/actions/assertions.
|
||||
- Use wait helpers (`waitForExistence`, `waitForDisappearance`, `tapWhenReady`).
|
||||
- Do not use `sleep(...)`.
|
||||
- Do not rely on localized labels as the only selector.
|
||||
|
||||
## Deterministic Setup
|
||||
|
||||
Pick the right fixture by overriding `seedFixture` in the test class:
|
||||
|
||||
- `"empty"`: no entries
|
||||
- `"single_mood"`: one current-day mood
|
||||
- `"week_of_moods"`: seven days of entries
|
||||
|
||||
Override launch behavior when needed:
|
||||
|
||||
- `skipOnboarding` (default `true`)
|
||||
- `bypassSubscription` (default `true`)
|
||||
- `expireTrial` (default `false`)
|
||||
|
||||
## Authoring Workflow
|
||||
|
||||
1. Define or confirm accessibility IDs in app code.
|
||||
2. Mirror IDs in `UITestID` if needed.
|
||||
3. Add/extend a screen object in `Tests iOS/Screens/`.
|
||||
4. Create a suite in `Tests iOS/{Feature}Tests.swift` inheriting `BaseUITestCase`.
|
||||
5. Keep tests focused on one behavior per test method.
|
||||
6. Add screenshots at meaningful checkpoints for triage.
|
||||
7. Run targeted suite first, then broader run if needed.
|
||||
|
||||
## Command Pattern
|
||||
|
||||
Targeted suite:
|
||||
|
||||
```bash
|
||||
xcodebuild -project Feels.xcodeproj \
|
||||
-scheme "Feels (iOS)" \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
|
||||
-only-testing:"Tests iOS/<SuiteName>" \
|
||||
test
|
||||
```
|
||||
|
||||
Full iOS UI suite:
|
||||
|
||||
```bash
|
||||
xcodebuild -project Feels.xcodeproj \
|
||||
-scheme "Feels (iOS)" \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
|
||||
-only-testing:"Tests iOS" \
|
||||
test
|
||||
```
|
||||
|
||||
## Definition Of Done For New UI Tests
|
||||
|
||||
- New test compiles and passes in targeted run.
|
||||
- Selectors are identifier-first (not string-literal labels).
|
||||
- No `sleep(...)` usage.
|
||||
- Screen object methods are reused where applicable.
|
||||
- Any new test-only IDs are added in app code + test helper enums.
|
||||
|
||||
## Prompt Contract (For Agents)
|
||||
|
||||
When asked to "create a UI test that does X", the implementation should include:
|
||||
|
||||
- Test suite + test method(s) in `Tests iOS/`
|
||||
- Any required accessibility ID additions in app code
|
||||
- Any required screen object additions
|
||||
- Targeted test execution output summary
|
||||
34
docs/templates/XCUITestSuiteTemplate.swift
vendored
Normal file
34
docs/templates/XCUITestSuiteTemplate.swift
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// <Feature>Tests.swift
|
||||
// Tests iOS
|
||||
//
|
||||
// Replace placeholders and move into Tests iOS/ when creating a real suite.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class <Feature>Tests: BaseUITestCase {
|
||||
// Choose fixture: "empty", "single_mood", "week_of_moods"
|
||||
override var seedFixture: String? { "empty" }
|
||||
|
||||
// Override launch behavior only when needed for this feature.
|
||||
// override var skipOnboarding: Bool { false }
|
||||
// override var bypassSubscription: Bool { false }
|
||||
// override var expireTrial: Bool { true }
|
||||
|
||||
func test<Behavior>() {
|
||||
let tabBar = TabBarScreen(app: app)
|
||||
|
||||
// Navigate using screen objects.
|
||||
let day = tabBar.tapDay()
|
||||
|
||||
// Interact using identifier-backed elements and helpers.
|
||||
day.assertMoodHeaderVisible()
|
||||
|
||||
// Add an attachment for triage/debugging.
|
||||
captureScreenshot(name: "<feature_behavior_state>")
|
||||
|
||||
// Assert outcome.
|
||||
XCTAssertTrue(day.moodHeader.exists, "Expected day mood header to exist")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user