Tester la rotation iOS sans perdre la saisie

Tester la rotation iOS sans perdre la saisie

La saisie fonctionne dans le formulaire d’édition en mode portrait, puis le brouillon disparaît au passage en paysage. Ou bien le bouton d’enregistrement est toujours présent dans l’arbre des vues, mais le clavier le masque. Une simple compilation sur un Mac cloud ne révèle pas ces problèmes : le test UI doit réellement suivre le parcours « saisir, pivoter, enregistrer, revenir en portrait ». Voici comment établir un contrôle reproductible pour une application iOS dotée d’un écran de modification de brouillon.

Définir ce qui doit rester inchangé après la rotation

Avant le test, attribuez des accessibilityIdentifier stables au champ d’édition, au bouton d’enregistrement et à l’indication du résultat : draftEditor, saveDraft et savedStatus. Ces identifiants font partie du code de l’application ; ne repérez pas les éléments à partir de libellés de boutons localisés. Le contrat du test est le suivant : la rotation ne doit pas effacer la saisie non enregistrée ; le bouton d’enregistrement doit être utilisable en paysage ; une confirmation doit apparaître après l’enregistrement et rester visible une fois revenu en portrait.

exists indique seulement que l’élément figure encore dans l’arbre d’accessibilité, pas que l’utilisateur peut l’atteindre. Un test de rotation doit aussi vérifier isHittable et effectuer un appui réel.

Vérifiez d’abord que l’application prend en charge les orientations à tester. Si le paysage est désactivé dans la configuration des orientations de l’appareil, le test ne doit pas interpréter l’impossibilité de pivoter comme un défaut de mise en page. Ne réutilisez pas non plus les mêmes attentes pour tablette et téléphone sans les vérifier : disposition en colonnes, taille du clavier et zones de sécurité peuvent différer.

Fixer le simulateur et l’environnement d’exécution

Dans une session graphique sur un Mac cloud XcodeOps, exécutez d’abord xcrun simctl list devices available. Choisissez un simulateur de téléphone associé à un environnement d’exécution installé, puis notez son UDID. En CI, ne sélectionnez pas le simulateur par son seul nom : des appareils de même nom peuvent appartenir à des environnements d’exécution différents. Transmettez l’UDID à la tâche dans la variable d’environnement SIMULATOR_UDID et vérifiez avant le démarrage qu’il figure toujours parmi les appareils disponibles.

Pour une même série de référence, fixez le chemin de sélection de Xcode, l’UDID du simulateur, les données de test de l’application et le point d’entrée du test. Conservez avec la tâche les sorties de xcodebuild -version et de xcrun simctl list devices available ; ne mêlez pas les résultats d’un autre simulateur à cette référence. Les tests UI nécessitent également une session graphique ouverte et utilisable : une connexion SSH fonctionnelle ne garantit pas que le processus de test pourra interagir avec la fenêtre du simulateur.

Écrire un test UI couvrant l’aller-retour complet

Ajoutez le test suivant à la cible de tests UI du projet. L’exemple suppose que l’écran d’édition est directement visible au lancement et que l’application fournit les trois identifiants indiqués plus haut. Si une navigation est nécessaire pour atteindre cet écran, effectuez-la explicitement avant la saisie, sans compter sur l’interface laissée par une exécution précédente.

import XCTest

final class RotationTests: XCTestCase {
    func testDraftSurvivesRotation() {
        let device = XCUIDevice.shared
        device.orientation = .portrait
        defer { device.orientation = .portrait }

        let app = XCUIApplication()
        app.launch()

        let editor = app.textViews["draftEditor"]
        XCTAssertTrue(editor.waitForExistence(timeout: 10))
        editor.tap()
        editor.typeText("Rotation check 42")

        device.orientation = .landscapeLeft
        XCTAssertTrue(
            NSPredicate(format: "value == %@", "Rotation check 42")
                .evaluate(with: editor)
        )

        let save = app.buttons["saveDraft"]
        XCTAssertTrue(save.waitForExistence(timeout: 10))
        XCTAssertTrue(save.isHittable)
        save.tap()

        let status = app.staticTexts["savedStatus"]
        XCTAssertTrue(status.waitForExistence(timeout: 10))

        device.orientation = .portrait
        XCTAssertTrue(status.waitForExistence(timeout: 10))
    }
}

Ici, defer vise à remettre autant que possible l’appareil en portrait pour le test suivant ; il ne remplace pas la définition d’un état initial dans chaque test. Si l’application reconstruit la vue d’édition lors de la rotation, une requête d’élément existante peut généralement être résolue de nouveau. En revanche, ne mettez pas en cache les coordonnées ou la frame obtenues à partir de cette requête. Pour un enregistrement asynchrone, attendre l’apparition d’une indication ne suffit pas : idéalement, l’application doit exposer un état de réussite explicite dont le test vérifie la valeur, plutôt que de traiter « enregistrement en cours » comme une opération terminée.

Exécuter en ligne de commande et conserver les preuves

Depuis le répertoire du projet, définissez le chemin du projet, un Scheme partagé et l’UDID relevé précédemment. La commande ci-dessous s’arrête immédiatement si une variable requise est vide et écrit les résultats du test dans un nouveau répertoire de résultats. Changez le nom de ce répertoire avant chaque nouvelle exécution.

: "${PROJECT:?Set the Xcode project path}"
: "${SCHEME:?Set a shared test scheme}"
: "${SIMULATOR_UDID:?Set an available simulator UDID}"

xcodebuild test \
  -project "$PROJECT" \
  -scheme "$SCHEME" \
  -destination "platform=iOS Simulator,id=$SIMULATOR_UDID" \
  -only-testing:AppUITests/RotationTests/testDraftSurvivesRotation \
  -resultBundlePath "$PWD/rotation-run.xcresult"

AppUITests doit correspondre au nom de la cible de tests UI du projet. En cas d’échec, conservez le fichier .xcresult et examinez l’étape concernée, les captures d’écran et les journaux du test pour déterminer si la saisie a été réinitialisée ou si le bouton était masqué ; ne vous limitez pas à la dernière ligne du terminal. Si un script doit lire les résultats, consultez d’abord xcrun xcresulttool help pour vérifier les sous-commandes disponibles dans la version actuelle de Xcode, puis choisissez la sortie structurée appropriée.

Diagnostiquer selon le symptôme, sans ajouter d’abord une attente

Symptôme À vérifier en priorité Piste de correction
Le brouillon est effacé après le passage en paysage La vue est-elle reconstruite à la rotation ? Qui détient l’état ? Placer l’état d’édition là où il survivra à la reconstruction de la vue
Le bouton existe, mais n’est pas utilisable Clavier, vues superposées, zone de sécurité et conteneur défilant Corriger les contraintes ou prévoir un moyen explicite de masquer le clavier
L’échec survient seulement en CI Session graphique, environnement d’exécution du simulateur et données de test Fixer la cible d’exécution et garantir un point de départ identique pour chaque tâche
La confirmation disparaît au retour en portrait S’agit-il seulement d’une animation temporaire ? Vérifier un résultat d’enregistrement persistant ou relire les données

N’ajoutez pas systématiquement un sleep de durée fixe dès qu’un test échoue en paysage. Si l’interface traverse réellement une transition, attendez qu’un élément atteigne un état vérifiable ; si le clavier masque définitivement le bouton, attendre davantage ne le rendra pas utilisable. Pour un problème limité à certaines dimensions d’écran, ajoutez ensuite un deuxième simulateur d’une autre taille, plutôt que de faire entrer tous les appareils dans un seul test. Stabilisez d’abord l’aller-retour sur une cible fixe, puis étendez la couverture : les échecs enregistrés pourront alors être comparés utilement.

Questions fréquentes

Le simulateur suffit-il pour tester la rotation ?

Il permet de vérifier l’état de l’interface et l’accès aux commandes. Confirmez les capteurs et l’usage réel sur un appareil.

Pourquoi un bouton présent reste-t-il impossible à toucher ?

Cherchez d’abord un clavier ou une vue qui le recouvre, puis vérifiez les contraintes de zone sûre et la recréation de la vue.

Passez à l’action sur un nœud physique dédié

Configurez un Mac cloud pour votre prochain projet

Choisissez votre Mac mini selon la mémoire ou la charge de compilation. Trois configurations sont disponibles à la location à la journée, à la semaine, au mois ou au trimestre. Vérifiez la disponibilité des nœuds en temps réel depuis la console.

Louer un Mac cloud