Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QAE-459] Add accessibility identifiers #653

Merged
merged 5 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LoopKit/GlucoseRangeSchedule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import HealthKit
import LoopAlgorithm

public struct DoubleRange {
public struct DoubleRange: Sendable {
public let minValue: Double
public let maxValue: Double

Expand Down
8 changes: 4 additions & 4 deletions LoopKit/TemporaryScheduleOverride.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ import Foundation
import HealthKit
import LoopAlgorithm

public struct TemporaryScheduleOverride: Hashable {
public enum Context: Hashable {
public struct TemporaryScheduleOverride: Hashable, Sendable {
public enum Context: Hashable, Sendable {
case preMeal
case legacyWorkout
case preset(TemporaryScheduleOverridePreset)
case custom
}

public enum EnactTrigger: Hashable {
public enum EnactTrigger: Hashable, Sendable {
case local
case remote(String)
}

public enum Duration: Hashable, Comparable {
public enum Duration: Hashable, Comparable, Sendable {
case finite(TimeInterval)
case indefinite

Expand Down
7 changes: 4 additions & 3 deletions LoopKit/TemporaryScheduleOverrideHistory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation
import SwiftData

public enum End: Equatable, Hashable, Codable {
public enum End: Equatable, Hashable, Codable, Sendable {
case natural
case early(Date)
case deleted // Ended before started
Expand Down Expand Up @@ -134,9 +134,10 @@ public final class TemporaryScheduleOverrideHistory {
}

public func activeOverride(at date: Date) -> TemporaryScheduleOverride? {
recentEvents.first { event in
let active = recentEvents.filter({$0.override.actualEnd != .deleted}).first { event in
event.override.isActive(at: date)
}?.override
}
return active?.override
}

private var lastUndeletedEvent: OverrideEvent? {
Expand Down
2 changes: 1 addition & 1 deletion LoopKit/TemporaryScheduleOverridePreset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation


public struct TemporaryScheduleOverridePreset: Hashable {
public struct TemporaryScheduleOverridePreset: Hashable, Sendable {
public let id: UUID
public var symbol: String
public var name: String
Expand Down
2 changes: 1 addition & 1 deletion LoopKit/TemporaryScheduleOverrideSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import HealthKit
import LoopAlgorithm

public struct TemporaryScheduleOverrideSettings: Hashable {
public struct TemporaryScheduleOverrideSettings: Hashable, Sendable {
private var targetRangeInMgdl: DoubleRange?
public var insulinNeedsScaleFactor: Double?

Expand Down
25 changes: 1 addition & 24 deletions LoopKit/TherapySettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,30 +67,6 @@ public struct TherapySettings: Equatable {
self.basalRateSchedule = basalRateSchedule
self.defaultRapidActingModel = defaultRapidActingModel
}

public var preMealGuardrail: Guardrail<LoopQuantity> {
if let scheduleRange = glucoseTargetRangeSchedule?.scheduleRange() {
return Guardrail.correctionRangeOverride(
for: .preMeal,
correctionRangeScheduleRange: scheduleRange,
suspendThreshold: suspendThreshold
)
} else {
return Guardrail.correctionRange
}
}

public var legacyWorkoutPresetGuardrail: Guardrail<LoopQuantity> {
if let scheduleRange = glucoseTargetRangeSchedule?.scheduleRange() {
return Guardrail.correctionRangeOverride(
for: .workout,
correctionRangeScheduleRange: scheduleRange,
suspendThreshold: suspendThreshold
)
} else {
return Guardrail.correctionRange
}
}
}

extension TherapySettings: Codable {
Expand Down Expand Up @@ -174,6 +150,7 @@ extension TherapySettings {

return (basalRate, carbRatio, isf)
}

}

extension TherapySettings {
Expand Down
4 changes: 2 additions & 2 deletions LoopKitUI/OnboardingUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ public protocol ServiceProvider: AnyObject {
var availableServices: [ServiceDescriptor] { get }
}

public protocol TherapySettingsProvider {
public protocol OnboardingTherapySettingsProvider {
var onboardingTherapySettings: TherapySettings { get }
}

public protocol SupportProvider: AnyObject {
var availableSupports: [SupportUI] { get }
}

public protocol OnboardingProvider: NotificationAuthorizationProvider, HealthStoreAuthorizationProvider, BluetoothProvider, CGMManagerProvider, PumpManagerProvider, StatefulPluggableProvider, ServiceProvider, TherapySettingsProvider, SupportProvider, PluginHost {
public protocol OnboardingProvider: NotificationAuthorizationProvider, HealthStoreAuthorizationProvider, BluetoothProvider, CGMManagerProvider, PumpManagerProvider, StatefulPluggableProvider, ServiceProvider, OnboardingTherapySettingsProvider, SupportProvider, PluginHost {
var allowDebugFeatures: Bool { get } // NOTE: DEBUG FEATURES - DEBUG AND TEST ONLY
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class DateAndDurationTableViewController: UITableViewController {
public func onSave(_ completion: @escaping (InputMode) -> Void) {
let saveBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(save))
navigationItem.rightBarButtonItem = saveBarButtonItem
navigationItem.rightBarButtonItem?.accessibilityIdentifier = "button_Save"
self.completion = completion
}

Expand Down
3 changes: 1 addition & 2 deletions LoopKitUI/Views/ChartTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ public final class ChartTableViewCell: UITableViewCell {
public var doesNavigate: Bool = true {
didSet {
rightArrowHint?.isHidden = !doesNavigate
rightArrowHint?.accessibilityIdentifier =
"image_navigateToGlucoseDetails_\(doesNavigate)"
}
}

Expand Down Expand Up @@ -70,6 +68,7 @@ public final class ChartTableViewCell: UITableViewCell {
public func setTitleLabelText(label: String?) {
titleLabel?.text = label
titleLabel?.font = .systemFont(ofSize: 17, weight: .semibold)
titleLabel?.accessibilityIdentifier = "chartTitleText_\(label!.replacing(" ", with: ""))"
}

public func setTitleLabelText(label: NSAttributedString?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ final class IssueAlertTableViewController: UITableViewController {
tableView.register(TextButtonTableViewCell.self, forCellReuseIdentifier: TextButtonTableViewCell.className)

let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped(_:)))
button.accessibilityIdentifier = "button_done"
navigationItem.setRightBarButton(button, animated: false)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
switch ModelRow(rawValue: indexPath.row)! {
case .constant:
cell.textLabel?.text = "Constant"
cell.accessibilityIdentifier = "cell_Constant"
if case .constant(let glucose) = cgmManager.dataSource.model {
cell.detailTextLabel?.text = quantityFormatter.string(from: glucose)
cell.accessoryType = .checkmark
Expand All @@ -232,6 +233,7 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
}
case .sineCurve:
cell.textLabel?.text = "Sine Curve"
cell.accessibilityIdentifier = "cell_SineCurve"
if case .sineCurve(parameters: (baseGlucose: let baseGlucose, amplitude: let amplitude, period: _, referenceDate: _)) = cgmManager.dataSource.model {
if let baseGlucoseText = quantityFormatter.numberFormatter.string(from: baseGlucose.doubleValue(for: glucoseUnit)),
let amplitudeText = quantityFormatter.string(from: amplitude) {
Expand Down Expand Up @@ -263,6 +265,7 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
}
case .frequency:
cell.textLabel?.text = "Measurement Frequency"
cell.accessibilityIdentifier = "cell_MeasurementFrequency"
cell.detailTextLabel?.text = cgmManager.dataSource.dataPointFrequency.localizedDescription
cell.accessoryType = .disclosureIndicator
}
Expand All @@ -277,22 +280,27 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
cell.onToggle = { [weak self] isOn in
self?.cgmManager.mockSensorState.glucoseAlertingEnabled = isOn
}
cell.switch?.accessibilityIdentifier = "switch_GlucoseValueAlerting"
cell.selectionStyle = .none
return cell
case .cgmLowerLimit:
cell.textLabel?.text = "CGM Lower Limit"
cell.detailTextLabel?.text = quantityFormatter.string(from: cgmManager.mockSensorState.cgmLowerLimit)
case .urgentLowGlucoseThreshold:
cell.textLabel?.text = "Urgent Low Glucose Threshold"
cell.accessibilityIdentifier = "cell_UrgentLowGlucoseThreshold"
cell.detailTextLabel?.text = quantityFormatter.string(from: cgmManager.mockSensorState.urgentLowGlucoseThreshold)
case .lowGlucoseThreshold:
cell.textLabel?.text = "Low Glucose Threshold"
cell.accessibilityIdentifier = "cell_LowGlucoseThreshold"
cell.detailTextLabel?.text = quantityFormatter.string(from: cgmManager.mockSensorState.lowGlucoseThreshold)
case .highGlucoseThreshold:
cell.textLabel?.text = "High Glucose Threshold"
cell.accessibilityIdentifier = "cell_HighGlucoseThreshold"
cell.detailTextLabel?.text = quantityFormatter.string(from: cgmManager.mockSensorState.highGlucoseThreshold)
case .cgmUpperLimit:
cell.textLabel?.text = "CGM Upper Limit"
cell.accessibilityIdentifier = "cell_CgmUpperLimit"
cell.detailTextLabel?.text = quantityFormatter.string(from: cgmManager.mockSensorState.cgmUpperLimit)
}
cell.accessoryType = .disclosureIndicator
Expand All @@ -302,6 +310,7 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
switch EffectsRow(rawValue: indexPath.row)! {
case .noise:
cell.textLabel?.text = "Glucose Noise"
cell.accessibilityIdentifier = "cell_GlucoseNoise"
if let maximumDeltaMagnitude = cgmManager.dataSource.effects.glucoseNoise {
cell.detailTextLabel?.text = quantityFormatter.string(from: maximumDeltaMagnitude)
} else {
Expand Down Expand Up @@ -363,9 +372,11 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
switch HistoryRow(rawValue: indexPath.row)! {
case .trend:
cell.textLabel?.text = "Trend"
cell.accessibilityIdentifier = "cell_Trend"
cell.detailTextLabel?.text = cgmManager.mockSensorState.trendType?.symbol
case .backfill:
cell.textLabel?.text = "Backfill Glucose"
cell.accessibilityIdentifier = "cell_BackfillGlucose"
}
cell.accessoryType = .disclosureIndicator
return cell
Expand All @@ -374,6 +385,7 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
switch AlertsRow(rawValue: indexPath.row)! {
case .issueAlert:
cell.textLabel?.text = "Issue Alerts"
cell.accessibilityIdentifier = "cell_IssueAlerts"
cell.accessoryType = .disclosureIndicator
}
return cell
Expand All @@ -382,20 +394,23 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
switch LifecycleProgressRow(rawValue: indexPath.row)! {
case .percentComplete:
cell.textLabel?.text = "Percent Completed"
cell.accessibilityIdentifier = "cell_PercentCompleted"
if let percentCompleted = cgmManager.mockSensorState.cgmLifecycleProgress?.percentComplete {
cell.detailTextLabel?.text = "\(Int(round(percentCompleted * 100)))%"
} else {
cell.detailTextLabel?.text = SettingsTableViewCell.NoValueString
}
case .warningThreshold:
cell.textLabel?.text = "Warning Threshold"
cell.accessibilityIdentifier = "cell_WarningThreshold"
if let warningThreshold = cgmManager.mockSensorState.progressWarningThresholdPercentValue {
cell.detailTextLabel?.text = "\(Int(round(warningThreshold * 100)))%"
} else {
cell.detailTextLabel?.text = SettingsTableViewCell.NoValueString
}
case .criticalThreshold:
cell.textLabel?.text = "Critical Threshold"
cell.accessibilityIdentifier = "cell_CriticalThreshold"
if let criticalThreshold = cgmManager.mockSensorState.progressCriticalThresholdPercentValue {
cell.detailTextLabel?.text = "\(Int(round(criticalThreshold * 100)))%"
} else {
Expand Down Expand Up @@ -577,6 +592,8 @@ final class MockCGMManagerSettingsViewController: UITableViewController {
vc.glucoseTrend = cgmManager.mockSensorState.trendType
vc.title = "Glucose Trend"
vc.glucoseTrendDelegate = self
vc.glucoseTrend?.image?.accessibilityIdentifier =
"cell_\(vc.glucoseTrend?.localizedDescription.replacingOccurrences(of: " ", with: "") ?? "")"
show(vc, sender: sender)
case .backfill:
let vc = DateAndDurationTableViewController()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,19 @@ final class SineCurveParametersTableViewController: UITableViewController {
switch Row(rawValue: indexPath.row)! {
case .baseGlucose:
cell.textLabel?.text = "Base Glucose"
cell.accessibilityIdentifier = "cell_BaseGlucose"
cell.detailTextLabel?.text = baseGlucose.map(formatGlucose) ?? SettingsTableViewCell.NoValueString
case .amplitude:
cell.textLabel?.text = "Amplitude"
cell.accessibilityIdentifier = "cell_Amplitude"
cell.detailTextLabel?.text = amplitude.map(formatGlucose) ?? SettingsTableViewCell.NoValueString
case .period:
cell.textLabel?.text = "Period"
cell.accessibilityIdentifier = "cell_Period"
cell.detailTextLabel?.text = period.flatMap(durationFormatter.string(from:)) ?? SettingsTableViewCell.NoValueString
case .referenceDate:
cell.textLabel?.text = "Reference Date"
cell.accessibilityIdentifier = "cell_ReferenceDate"
cell.detailTextLabel?.text = referenceDate.map(dateFormatter.string(from:)) ?? SettingsTableViewCell.NoValueString
}

Expand Down
1 change: 1 addition & 0 deletions MockKitUI/Views/MockCGMManagerSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ struct MockCGMManagerSettingsView: View {
.aspectRatio(contentMode: ContentMode.fit)
.frame(maxHeight: 70)
.frame(width: 70)
.accessibilityIdentifier("image_CgmSimulator")
}
}

Expand Down