test: add accessibility identifiers along the onboarding-to-residence-detail path
Scaffolding for the gitea#2 regression XCUITest. No user-visible change — pure metadata for UI automation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,9 @@ struct AccessibilityIdentifiers {
|
|||||||
static let addButton = "Residence.AddButton"
|
static let addButton = "Residence.AddButton"
|
||||||
static let residencesList = "Residence.List"
|
static let residencesList = "Residence.List"
|
||||||
static let residenceCard = "Residence.Card"
|
static let residenceCard = "Residence.Card"
|
||||||
|
/// Prefix for individual residence cells in the list. Suffix with the
|
||||||
|
/// residence id to address a specific cell (e.g. "Residence.Cell.42").
|
||||||
|
static let cellPrefix = "Residence.Cell"
|
||||||
static let emptyStateView = "Residence.EmptyState"
|
static let emptyStateView = "Residence.EmptyState"
|
||||||
static let emptyStateButton = "Residence.EmptyState.AddButton"
|
static let emptyStateButton = "Residence.EmptyState.AddButton"
|
||||||
|
|
||||||
@@ -87,7 +90,15 @@ struct AccessibilityIdentifiers {
|
|||||||
static let refreshButton = "Task.RefreshButton"
|
static let refreshButton = "Task.RefreshButton"
|
||||||
static let tasksList = "Task.List"
|
static let tasksList = "Task.List"
|
||||||
static let taskCard = "Task.Card"
|
static let taskCard = "Task.Card"
|
||||||
|
/// Prefix for individual task rows. Suffix with the task id to
|
||||||
|
/// address a specific row (e.g. "Task.Row.42"). Use `BEGINSWITH`
|
||||||
|
/// in tests to detect "any task row exists".
|
||||||
|
static let rowPrefix = "Task.Row"
|
||||||
static let emptyStateView = "Task.EmptyState"
|
static let emptyStateView = "Task.EmptyState"
|
||||||
|
/// Label rendered when a residence-detail tasks section has no tasks
|
||||||
|
/// in any kanban column. Asserted ABSENT after onboarding bulk-create
|
||||||
|
/// in the gitea#2 regression test.
|
||||||
|
static let noTasksLabel = "Task.NoTasksLabel"
|
||||||
static let kanbanView = "Task.KanbanView"
|
static let kanbanView = "Task.KanbanView"
|
||||||
static let overdueColumn = "Task.Column.Overdue"
|
static let overdueColumn = "Task.Column.Overdue"
|
||||||
static let upcomingColumn = "Task.Column.Upcoming"
|
static let upcomingColumn = "Task.Column.Upcoming"
|
||||||
@@ -229,8 +240,24 @@ struct AccessibilityIdentifiers {
|
|||||||
static let taskSelectionCounter = "Onboarding.TaskSelectionCounter"
|
static let taskSelectionCounter = "Onboarding.TaskSelectionCounter"
|
||||||
static let addPopularTasksButton = "Onboarding.AddPopularTasksButton"
|
static let addPopularTasksButton = "Onboarding.AddPopularTasksButton"
|
||||||
static let addTasksContinueButton = "Onboarding.AddTasksContinueButton"
|
static let addTasksContinueButton = "Onboarding.AddTasksContinueButton"
|
||||||
|
/// Submit/continue button at the bottom of the First-Task screen.
|
||||||
|
/// Triggers `POST /api/tasks/bulk/` for the selected templates.
|
||||||
|
static let submitTasksButton = "Onboarding.SubmitTasksButton"
|
||||||
|
/// Tab bar control above the task list. The "Browse All" segment is
|
||||||
|
/// addressed via `app.buttons["Browse All"]` from the segmented
|
||||||
|
/// picker once this identifier is set.
|
||||||
|
static let firstTaskTabBar = "Onboarding.FirstTaskTabBar"
|
||||||
|
/// Tab segment that shows the full template catalog.
|
||||||
|
/// Tap from a test by addressing the Picker's segment label
|
||||||
|
/// "Browse All" within the element identified above.
|
||||||
|
static let browseAllTab = "Onboarding.BrowseAllTab"
|
||||||
static let taskCategorySection = "Onboarding.TaskCategorySection"
|
static let taskCategorySection = "Onboarding.TaskCategorySection"
|
||||||
static let taskTemplateRow = "Onboarding.TaskTemplateRow"
|
static let taskTemplateRow = "Onboarding.TaskTemplateRow"
|
||||||
|
/// Prefix for individual template rows on the First-Task screen
|
||||||
|
/// (Browse All tab). Suffix with the backend template id —
|
||||||
|
/// e.g. `"Onboarding.TemplateRow.123"`. Tests use `BEGINSWITH` to
|
||||||
|
/// pick the first N rows deterministically without knowing ids.
|
||||||
|
static let templateRowPrefix = "Onboarding.TemplateRow"
|
||||||
|
|
||||||
// Subscription Screen
|
// Subscription Screen
|
||||||
static let subscriptionTitle = "Onboarding.SubscriptionTitle"
|
static let subscriptionTitle = "Onboarding.SubscriptionTitle"
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ struct OnboardingFirstTaskContent: View {
|
|||||||
|
|
||||||
OnboardingTaskTabBar(selectedTab: $selectedTab)
|
OnboardingTaskTabBar(selectedTab: $selectedTab)
|
||||||
.padding(.horizontal, OrganicSpacing.comfortable)
|
.padding(.horizontal, OrganicSpacing.comfortable)
|
||||||
|
.accessibilityIdentifier(AccessibilityIdentifiers.Onboarding.firstTaskTabBar)
|
||||||
|
|
||||||
switch selectedTab {
|
switch selectedTab {
|
||||||
case .forYou:
|
case .forYou:
|
||||||
@@ -384,6 +385,7 @@ struct OnboardingFirstTaskContent: View {
|
|||||||
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
|
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
|
||||||
.naturalShadow(selectedCount > 0 ? .medium : .subtle)
|
.naturalShadow(selectedCount > 0 ? .medium : .subtle)
|
||||||
}
|
}
|
||||||
|
.accessibilityIdentifier(AccessibilityIdentifiers.Onboarding.submitTasksButton)
|
||||||
.disabled(vm.isSubmitting)
|
.disabled(vm.isSubmitting)
|
||||||
.animation(.easeInOut(duration: 0.2), value: selectedCount)
|
.animation(.easeInOut(duration: 0.2), value: selectedCount)
|
||||||
}
|
}
|
||||||
@@ -653,6 +655,7 @@ private struct OnboardingSuggestionRow: View {
|
|||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
.accessibilityIdentifier("\(AccessibilityIdentifiers.Onboarding.templateRowPrefix).\(suggestion.template.id)")
|
||||||
.accessibilityLabel("\(suggestion.template.title), \(suggestion.template.frequencyDisplay), \(relevancePercent)% match")
|
.accessibilityLabel("\(suggestion.template.title), \(suggestion.template.frequencyDisplay), \(relevancePercent)% match")
|
||||||
.accessibilityValue(isSelected ? "selected" : "not selected")
|
.accessibilityValue(isSelected ? "selected" : "not selected")
|
||||||
}
|
}
|
||||||
@@ -798,6 +801,7 @@ private struct OnboardingTemplateRow: View {
|
|||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
.accessibilityIdentifier("\(AccessibilityIdentifiers.Onboarding.templateRowPrefix).\(template.id)")
|
||||||
.accessibilityLabel("\(template.title), \(template.frequencyLabel)")
|
.accessibilityLabel("\(template.title), \(template.frequencyLabel)")
|
||||||
.accessibilityValue(isSelected ? "selected" : "not selected")
|
.accessibilityValue(isSelected ? "selected" : "not selected")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ private struct ResidencesContent: View {
|
|||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
}
|
}
|
||||||
.buttonStyle(OrganicCardButtonStyle())
|
.buttonStyle(OrganicCardButtonStyle())
|
||||||
|
.accessibilityIdentifier("\(AccessibilityIdentifiers.Residence.cellPrefix).\(residence.id)")
|
||||||
.transition(.asymmetric(
|
.transition(.asymmetric(
|
||||||
insertion: .opacity.combined(with: .move(edge: .bottom)),
|
insertion: .opacity.combined(with: .move(edge: .bottom)),
|
||||||
removal: .opacity
|
removal: .opacity
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ struct DynamicTaskCard: View {
|
|||||||
.cornerRadius(12)
|
.cornerRadius(12)
|
||||||
.shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 2)
|
.shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 2)
|
||||||
.simultaneousGesture(TapGesture(), including: .subviews)
|
.simultaneousGesture(TapGesture(), including: .subviews)
|
||||||
|
.accessibilityIdentifier("\(AccessibilityIdentifiers.Task.rowPrefix).\(task.id)")
|
||||||
.sheet(isPresented: $showCompletionHistory) {
|
.sheet(isPresented: $showCompletionHistory) {
|
||||||
CompletionHistorySheet(
|
CompletionHistorySheet(
|
||||||
taskTitle: task.title,
|
taskTitle: task.title,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ struct EmptyTasksView: View {
|
|||||||
.background(Color.appBackgroundSecondary)
|
.background(Color.appBackgroundSecondary)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
|
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
|
||||||
.naturalShadow(.subtle)
|
.naturalShadow(.subtle)
|
||||||
|
.accessibilityElement(children: .combine)
|
||||||
|
.accessibilityIdentifier(AccessibilityIdentifiers.Task.noTasksLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user