import Testing @testable import OnyxNotch @Suite("Eingeklappt und trotzdem da") struct AmbientStateTests { /// Öffnet das Panel auf dem üblichen Weg: hinein, Entprellzeit ab. private func opened() -> NotchStateMachine { var machine = NotchStateMachine() machine.handle(.pointerEntered) machine.handle(.armTimerFired) return machine } @Test("Ohne Live Activity ist eingeklappt gleich unsichtbar") func idleIsOffScreen() { let machine = NotchStateMachine() #expect(!machine.isExpanded) #expect(!machine.isOnScreen) } /// Der ganze Zweck: Fenster auf dem Schirm, Fläche in Notchgröße. @Test("Eine Live Activity holt das Fenster, ohne auszufahren") func ambientShowsWithoutExpanding() { var machine = NotchStateMachine() let effects = machine.handle(.ambient(true)) #expect(effects == [.showAmbient]) #expect(machine.isOnScreen) #expect(!machine.isExpanded) } @Test("Fällt sie weg, geht das Fenster wieder fort") func ambientVanishing() { var machine = NotchStateMachine() machine.handle(.ambient(true)) #expect(machine.handle(.ambient(false)) == [.hideAmbient]) #expect(!machine.isOnScreen) } /// Zweimal dieselbe Meldung ist keine Änderung. Ohne diese Sperre blendet /// jeder Lautstärkeschritt das Fenster neu ein. @Test("Dieselbe Meldung zweimal bewirkt nichts") func repeatedIsQuiet() { var machine = NotchStateMachine() machine.handle(.ambient(true)) #expect(machine.handle(.ambient(true)).isEmpty) } /// Ausgefahren ist ohnehin alles zu sehen — dort darf eine Activity das /// Fenster nicht ein zweites Mal einblenden. @Test("Ausgefahren bleibt eine Live Activity ohne Wirkung") func ambientWhileExpanded() { var machine = opened() #expect(machine.handle(.ambient(true)).isEmpty) #expect(machine.isExpanded) #expect(machine.isOnScreen) } /// Der eigentliche Grund für die Trennung: nach dem Einfahren darf das /// Fenster nicht fortgeräumt werden, wenn noch etwas anliegt. @Test("Nach dem Einfahren bleibt das Fenster, solange etwas anliegt") func staysAfterCollapse() { var machine = opened() machine.handle(.ambient(true)) machine.handle(.pointerExited) machine.handle(.closeTimerFired) #expect(!machine.isExpanded) #expect(machine.isOnScreen) #expect(machine.hasAmbient) } @Test("Unterdrückung nimmt auch die Live Activity weg") func suppressionHidesAmbient() { var machine = NotchStateMachine() machine.handle(.ambient(true)) let effects = machine.handle(.suppressed(true)) #expect(effects.contains(.hideAmbient)) #expect(!machine.isOnScreen) // Gemerkt bleibt sie trotzdem — sonst käme sie nach dem Vollbildvideo // nicht von selbst zurück. #expect(machine.hasAmbient) } @Test("Nach der Unterdrückung kommt sie von selbst zurück") func ambientReturnsAfterSuppression() { var machine = NotchStateMachine() machine.handle(.ambient(true)) machine.handle(.suppressed(true)) #expect(machine.handle(.suppressed(false)) == [.showAmbient]) #expect(machine.isOnScreen) } /// Eine Activity darf die Phase nicht anfassen: sonst gälte ein Klick /// plötzlich als Fixieren, oder eine Frist liefe, die niemand gestartet hat. @Test("Eine Live Activity ändert die Phase nicht") func ambientLeavesPhaseAlone() { var machine = NotchStateMachine() machine.handle(.pointerEntered) let before = machine.phase machine.handle(.ambient(true)) #expect(machine.phase == before) } }