Skip to content

Commit 7762c13

Browse files
committed
Add accessibility IDs to calendar legend
Expose accessibility identifiers for CalendarLegendView and its LegendItem labels to improve UI test reliability and accessibility. Refactor LegendItem to accept a labelIdentifier and mark the view as an accessibility element; convert labelKey init to LocalizedStringKey. Update LocalizationUITests by adding assertLegendPeriodLabel helper and using it in the language switch test, with additional tab bar existence checks and more robust lookup logic.
1 parent b829663 commit 7762c13

2 files changed

Lines changed: 92 additions & 12 deletions

File tree

CycleOne/Views/Calendar/CalendarLegendView.swift

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,73 @@ import SwiftUI
77

88
struct CalendarLegendView: View {
99
@EnvironmentObject private var themeManager: ThemeManager
10+
1011
var body: some View {
1112
VStack(alignment: .leading, spacing: 10) {
1213
Text("calendar.legend.title")
1314
.font(.caption)
1415
.fontWeight(.semibold)
1516
.foregroundColor(.secondary)
17+
.accessibilityIdentifier("CalendarLegend_TitleLabel")
1618

1719
HStack(spacing: 0) {
18-
LegendItem(color: .systemPink, labelKey: "calendar.legend.period", isCustomView: true)
20+
LegendItem(
21+
color: .systemPink,
22+
labelKey: "calendar.legend.period",
23+
isCustomView: true,
24+
labelIdentifier: "CalendarLegend_PeriodLabel"
25+
)
1926
Spacer()
20-
LegendItem(color: .systemGray, labelKey: "calendar.legend.predicted")
27+
LegendItem(
28+
color: .systemGray,
29+
labelKey: "calendar.legend.predicted",
30+
labelIdentifier: "CalendarLegend_PredictedLabel"
31+
)
2132
Spacer()
22-
LegendItem(color: .systemTeal, labelKey: "calendar.legend.ovulation")
33+
LegendItem(
34+
color: .systemTeal,
35+
labelKey: "calendar.legend.ovulation",
36+
labelIdentifier: "CalendarLegend_OvulationLabel"
37+
)
2338
Spacer()
24-
LegendItem(color: .systemTeal.withAlphaComponent(0.3), labelKey: "calendar.legend.fertile")
39+
LegendItem(
40+
color: .systemTeal.withAlphaComponent(0.3),
41+
labelKey: "calendar.legend.fertile",
42+
labelIdentifier: "CalendarLegend_FertileLabel"
43+
)
2544
Spacer()
26-
LegendItem(color: .secondaryLabel, labelKey: "calendar.legend.logged")
45+
LegendItem(
46+
color: .secondaryLabel,
47+
labelKey: "calendar.legend.logged",
48+
labelIdentifier: "CalendarLegend_LoggedLabel"
49+
)
2750
}
2851
}
2952
.padding(14)
3053
.background(
3154
RoundedRectangle(cornerRadius: 14)
3255
.fill(Color(.secondarySystemBackground).opacity(0.6))
3356
)
57+
.accessibilityIdentifier("CalendarLegendView")
3458
}
3559
}
3660

3761
struct LegendItem: View {
3862
let color: UIColor
3963
let labelKey: LocalizedStringKey
4064
var isCustomView: Bool = false
65+
let labelIdentifier: String
4166

4267
init(
4368
color: UIColor,
4469
labelKey: String,
45-
isCustomView: Bool = false
70+
isCustomView: Bool = false,
71+
labelIdentifier: String
4672
) {
4773
self.color = color
4874
self.labelKey = LocalizedStringKey(labelKey)
4975
self.isCustomView = isCustomView
76+
self.labelIdentifier = labelIdentifier
5077
}
5178

5279
var body: some View {
@@ -58,6 +85,8 @@ struct LegendItem: View {
5885
.font(.caption2)
5986
.foregroundColor(.primary)
6087
}
88+
.accessibilityElement(children: .combine)
89+
.accessibilityIdentifier(labelIdentifier)
6190
}
6291
}
6392

CycleOneUITests/Tests/LocalizationUITests.swift

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,59 @@ final class LocalizationUITests: XCTestCase {
6363
return settingsList
6464
}
6565

66+
@MainActor
67+
private func assertLegendPeriodLabel(
68+
in app: XCUIApplication,
69+
expected: String,
70+
timeout: TimeInterval = 15
71+
) {
72+
UITestAppHarness.openTab(at: 0, in: app)
73+
74+
let legendRoot = UITestAppHarness.element(
75+
withIdentifier: "CalendarLegendView",
76+
in: app
77+
)
78+
XCTAssertTrue(legendRoot.waitForExistence(timeout: timeout))
79+
80+
let periodLabel = UITestAppHarness.element(
81+
withIdentifier: "CalendarLegend_PeriodLabel",
82+
in: app
83+
)
84+
85+
let deadline = Date().addingTimeInterval(timeout)
86+
var foundLocalizedLabel = false
87+
88+
while Date() < deadline {
89+
if app.staticTexts[expected].exists {
90+
foundLocalizedLabel = true
91+
break
92+
}
93+
94+
if periodLabel.exists, periodLabel.label.contains(expected) {
95+
foundLocalizedLabel = true
96+
break
97+
}
98+
99+
if legendRoot.label.contains(expected) {
100+
foundLocalizedLabel = true
101+
break
102+
}
103+
104+
let scrollView = app.scrollViews.firstMatch
105+
if scrollView.exists {
106+
scrollView.swipeUp()
107+
scrollView.swipeDown()
108+
}
109+
_ = app.staticTexts[expected].waitForExistence(timeout: 0.2)
110+
}
111+
112+
XCTAssertTrue(
113+
foundLocalizedLabel,
114+
"Expected legend period label '\(expected)' not found. Legend root label: '\(legendRoot.label)'"
115+
)
116+
XCTAssertFalse(app.staticTexts["calendar.legend.period"].exists)
117+
}
118+
66119
@MainActor
67120
func testSettingsLanguageSwitchToJapanese_updatesVisibleStrings() {
68121
let app = UITestAppHarness.launch(
@@ -101,17 +154,15 @@ final class LocalizationUITests: XCTestCase {
101154

102155
let settingsList = openSettingsList(in: app)
103156
selectLanguage(in: app, settingsList: settingsList, candidates: ["Japanese", "日本語"])
157+
XCTAssertTrue(app.tabBars.buttons["設定"].waitForExistence(timeout: 12))
104158

105-
UITestAppHarness.openTab(at: 0, in: app)
106-
XCTAssertTrue(app.staticTexts["生理"].waitForExistence(timeout: 10))
107-
XCTAssertFalse(app.staticTexts["calendar.legend.period"].exists)
159+
assertLegendPeriodLabel(in: app, expected: "生理")
108160

109161
let settingsListJapanese = openSettingsList(in: app)
110162
selectLanguage(in: app, settingsList: settingsListJapanese, candidates: ["English", "英語"])
163+
XCTAssertTrue(app.tabBars.buttons["Settings"].waitForExistence(timeout: 12))
111164

112-
UITestAppHarness.openTab(at: 0, in: app)
113-
XCTAssertTrue(app.staticTexts["Period"].waitForExistence(timeout: 10))
114-
XCTAssertFalse(app.staticTexts["calendar.legend.period"].exists)
165+
assertLegendPeriodLabel(in: app, expected: "Period")
115166
}
116167

117168
@MainActor

0 commit comments

Comments
 (0)