From c2d5aa5de31d15b09d45d98332f39e27a980e9c8 Mon Sep 17 00:00:00 2001 From: Austin Smith Date: Wed, 15 Jul 2026 19:33:50 -0700 Subject: [PATCH 1/2] add motion policy unit tests --- .../Shared/MotionPolicyTests.swift | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 ComputerSolitaireTests/Shared/MotionPolicyTests.swift diff --git a/ComputerSolitaireTests/Shared/MotionPolicyTests.swift b/ComputerSolitaireTests/Shared/MotionPolicyTests.swift new file mode 100644 index 0000000..a605d8f --- /dev/null +++ b/ComputerSolitaireTests/Shared/MotionPolicyTests.swift @@ -0,0 +1,73 @@ +import XCTest +@testable import Computer_Solitaire + +/// The motion policy is the single gate every gameplay animation and +/// completion delay routes through; these tests pin its contract — the speed +/// catalog's stored scales, the raw-value fallback that protects old saved +/// defaults, the Reduce Motion clamp, and the builders going nil (apply +/// without animating) whenever motion is off. +@MainActor +final class MotionPolicyTests: XCTestCase { + func testAnimationSpeedScales() { + XCTAssertEqual(AnimationSpeed.normal.scale, 1) + XCTAssertEqual(AnimationSpeed.fast.scale, 0.5) + XCTAssertEqual(AnimationSpeed.instant.scale, 0) + XCTAssertEqual(AnimationSpeed.defaultValue, .normal) + } + + func testFromRawValueRoundTripsEveryOption() { + for speed in AnimationSpeed.all { + XCTAssertEqual(AnimationSpeed.from(rawValue: speed.id), speed) + } + } + + func testFromRawValueFallsBackToDefaultOnUnknownID() { + XCTAssertEqual(AnimationSpeed.from(rawValue: "warp"), .normal) + XCTAssertEqual(AnimationSpeed.from(rawValue: ""), .normal) + } + + func testPolicyUsesSpeedScaleWithoutReduceMotion() { + for speed in AnimationSpeed.all { + let policy = MotionPolicy(speed: speed, reduceMotion: false) + XCTAssertEqual(policy.scale, speed.scale) + } + } + + func testReduceMotionClampsEverySpeedToInstant() { + for speed in AnimationSpeed.all { + let policy = MotionPolicy(speed: speed, reduceMotion: true) + XCTAssertEqual(policy.scale, 0) + XCTAssertTrue(policy.isInstant) + XCTAssertNil(policy.spring(response: 0.35, dampingFraction: 0.86)) + } + } + + func testIsInstantOnlyForZeroScale() { + XCTAssertFalse(MotionPolicy(speed: .normal, reduceMotion: false).isInstant) + XCTAssertFalse(MotionPolicy(speed: .fast, reduceMotion: false).isInstant) + XCTAssertTrue(MotionPolicy(speed: .instant, reduceMotion: false).isInstant) + } + + func testDurationScalesWithSpeed() { + XCTAssertEqual(MotionPolicy(speed: .normal, reduceMotion: false).duration(0.32), 0.32) + XCTAssertEqual(MotionPolicy(speed: .fast, reduceMotion: false).duration(0.32), 0.16) + XCTAssertEqual(MotionPolicy(speed: .instant, reduceMotion: false).duration(0.32), 0) + XCTAssertEqual(MotionPolicy(speed: .normal, reduceMotion: true).duration(0.32), 0) + } + + func testBuildersReturnAnimationsWhenMotionIsOn() { + for speed in [AnimationSpeed.normal, .fast] { + let policy = MotionPolicy(speed: speed, reduceMotion: false) + XCTAssertNotNil(policy.spring(response: 0.35, dampingFraction: 0.86)) + XCTAssertNotNil(policy.easeOut(0.2)) + XCTAssertNotNil(policy.easeInOut(0.32)) + } + } + + func testBuildersReturnNilWhenInstant() { + let policy = MotionPolicy(speed: .instant, reduceMotion: false) + XCTAssertNil(policy.spring(response: 0.35, dampingFraction: 0.86)) + XCTAssertNil(policy.easeOut(0.2)) + XCTAssertNil(policy.easeInOut(0.32)) + } +} From bdeeddb4869d95973c157cb7f7282fe0bedaa389 Mon Sep 17 00:00:00 2001 From: Austin Smith Date: Wed, 15 Jul 2026 19:33:50 -0700 Subject: [PATCH 2/2] fix macos general settings pane height --- ComputerSolitaire/Views/MacSettingsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ComputerSolitaire/Views/MacSettingsView.swift b/ComputerSolitaire/Views/MacSettingsView.swift index cccf35f..7ef16c8 100644 --- a/ComputerSolitaire/Views/MacSettingsView.swift +++ b/ComputerSolitaire/Views/MacSettingsView.swift @@ -10,7 +10,7 @@ import SwiftUI struct MacSettingsView: View { private enum PaneMetrics { static let width: CGFloat = 500 - static let generalHeight: CGFloat = 410 + static let generalHeight: CGFloat = 355 static let appearanceHeight: CGFloat = 560 static let rulesHeight: CGFloat = 500 static let aboutHeight: CGFloat = 400