// // AccessibilityUITests.swift // PlantGuideUITests // // Tests for VoiceOver labels, Dynamic Type, and accessibility. // import XCTest final class AccessibilityUITests: BaseUITestCase { // MARK: - Tab Bar Labels @MainActor func testTabBarAccessibilityLabels() throws { launchClean() let tabs = TabBarScreen(app: app) tabs.assertAllTabsExist() for label in tabs.allTabLabels { let tab = tabs.tabBar.buttons[label] XCTAssertFalse(tab.label.isEmpty, "Tab '\(label)' label should not be empty") } } // MARK: - Camera Accessibility @MainActor func testCameraCaptureButtonAccessibility() throws { // Camera is default tab — no need to tap it launchClean() let camera = CameraScreen(app: app) if camera.captureButton.waitForExistence(timeout: 5) { XCTAssertFalse(camera.captureButton.label.isEmpty, "Capture button should have an accessibility label") } // If no capture button (permission not granted), test passes } // MARK: - Collection Accessibility @MainActor func testSearchFieldAccessibility() throws { launchClean() let collection = TabBarScreen(app: app).tapCollection() XCTAssertTrue(collection.waitForLoad()) // Search field may need swipe down to reveal var found = app.searchFields.firstMatch.waitForExistence(timeout: 3) if !found { collection.navigationBar.swipeDown() found = app.searchFields.firstMatch.waitForExistence(timeout: 3) } XCTAssertTrue(found || collection.navigationBar.exists, "Search field should be accessible or collection should be displayed") } // MARK: - Navigation Titles @MainActor func testNavigationTitlesAccessibility() throws { launchClean() let collection = TabBarScreen(app: app).tapCollection() XCTAssertTrue(collection.waitForLoad(), "Collection title should be accessible") let today = TabBarScreen(app: app).tapToday() XCTAssertTrue(today.waitForLoad(), "Today view should be accessible") let settings = TabBarScreen(app: app).tapSettings() XCTAssertTrue(settings.waitForLoad(), "Settings title should be accessible") } // MARK: - Dynamic Type @MainActor func testAppWithExtraLargeDynamicType() throws { app.launchArguments += [LaunchConfigKey.uiTesting, LaunchConfigKey.skipOnboarding] app.launchEnvironment[LaunchConfigKey.isUITesting] = "YES" app.launchEnvironment["UIPreferredContentSizeCategoryName"] = "UICTContentSizeCategoryAccessibilityExtraExtraExtraLarge" app.launch() XCTAssertTrue(app.waitForLaunch(), "App should launch with extra large text") let tabs = TabBarScreen(app: app) let collection = tabs.tapCollection() XCTAssertTrue(collection.waitForLoad(), "Collection should load with large text") let today = tabs.tapToday() XCTAssertTrue(today.waitForLoad(), "Today should load with large text") let settings = tabs.tapSettings() XCTAssertTrue(settings.waitForLoad(), "Settings should load with large text") } // MARK: - Empty States @MainActor func testEmptyStatesAccessibility() throws { launchClean() let collection = TabBarScreen(app: app).tapCollection() XCTAssertTrue(collection.waitForLoad()) // Empty state should be accessible let emptyByID = collection.emptyStateView.waitForExistence(timeout: 5) let emptyByText = app.staticTexts.matching( NSPredicate(format: "label CONTAINS[c] 'no plants' OR label CONTAINS[c] 'empty' OR label CONTAINS[c] 'add' OR label CONTAINS[c] 'identify'") ).firstMatch.waitForExistence(timeout: 3) XCTAssertTrue(emptyByID || emptyByText || collection.navigationBar.exists, "Empty state should be accessible") } // MARK: - Interactive Elements @MainActor func testInteractiveElementsAreAccessible() throws { launchClean() // Collection nav bar let collection = TabBarScreen(app: app).tapCollection() XCTAssertTrue(collection.waitForLoad(), "Collection should load") // Settings view let settings = TabBarScreen(app: app).tapSettings() XCTAssertTrue(settings.waitForLoad(), "Settings should be accessible") // Camera view (navigate back to camera) TabBarScreen(app: app).tapCamera() let camera = CameraScreen(app: app) XCTAssertTrue(camera.hasValidState(timeout: 10), "Camera should have accessible content") } }