// // FavoriteFlowUITests.swift // FocaleUITests // // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // // Regression test for the heart-button crash: the PHPhotoLibrary change // block must stay isolation-free (Swift 6) or tapping the favorite // button kills the app on PhotoKit's queue. // import XCTest final class FavoriteFlowUITests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false } @MainActor func testHeartToggleTogglesAndDoesNotCrash() throws { let app = XCUIApplication() app.launch() // The test runner reinstalls the app, which resets the photo // permission: walk through onboarding + the system dialog if shown. let allowButton = app.buttons["Autoriser l'accès à mes photos"] if allowButton.waitForExistence(timeout: 5) { allowButton.tap() let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let fullAccess = springboard.buttons.matching(NSPredicate( format: "label CONTAINS[c] 'toutes' OR label CONTAINS[c] 'complet' OR label CONTAINS[c] 'Allow'" )).firstMatch if fullAccess.waitForExistence(timeout: 8) { fullAccess.tap() } } let libraryTab = app.buttons["Bibliothèque"].firstMatch XCTAssertTrue(libraryTab.waitForExistence(timeout: 20), "Library tab should exist") libraryTab.tap() let thumbnail = app.buttons.matching(identifier: "photo-thumbnail").firstMatch XCTAssertTrue(thumbnail.waitForExistence(timeout: 15), "At least one photo thumbnail") thumbnail.tap() let addFavorite = app.buttons["Ajouter aux favoris"] let removeFavorite = app.buttons["Retirer des favoris"] XCTAssertTrue( addFavorite.waitForExistence(timeout: 10) || removeFavorite.exists, "Favorite button should be visible in the viewer toolbar" ) let startedAsFavorite = removeFavorite.exists (startedAsFavorite ? removeFavorite : addFavorite).tap() // Optimistic UI: the label flips immediately, and the app survives // the PhotoKit write that crashed build 3. let flipped = startedAsFavorite ? addFavorite : removeFavorite XCTAssertTrue(flipped.waitForExistence(timeout: 5), "Heart state should flip after tap") // Give the PhotoKit change block time to run (the old crash fired here). Thread.sleep(forTimeInterval: 2) XCTAssertEqual(app.state, .runningForeground, "App must survive the favorite write") // Toggle back so the test leaves the library as it found it. flipped.tap() let restored = startedAsFavorite ? removeFavorite : addFavorite XCTAssertTrue(restored.waitForExistence(timeout: 5), "Heart state should flip back") Thread.sleep(forTimeInterval: 2) XCTAssertEqual(app.state, .runningForeground) } }