summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md1
-rw-r--r--macos/Tests/Update/UpdateStateTests.swift116
-rw-r--r--macos/Tests/Update/UpdateViewModelTests.swift97
3 files changed, 214 insertions, 0 deletions
diff --git a/AGENTS.md b/AGENTS.md
index afa0fd1f2..5a885923e 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -29,3 +29,4 @@ A file for [guiding coding agents](https://agents.md/).
- Do not use `xcodebuild`
- Use `zig build` to build the macOS app and any shared Zig code
+- Run Xcode tests using `zig build test`
diff --git a/macos/Tests/Update/UpdateStateTests.swift b/macos/Tests/Update/UpdateStateTests.swift
new file mode 100644
index 000000000..5a0832a5a
--- /dev/null
+++ b/macos/Tests/Update/UpdateStateTests.swift
@@ -0,0 +1,116 @@
+import Testing
+import Foundation
+import Sparkle
+@testable import Ghostty
+
+struct UpdateStateTests {
+ // MARK: - Equatable Tests
+
+ @Test func testIdleEquality() {
+ let state1: UpdateState = .idle
+ let state2: UpdateState = .idle
+ #expect(state1 == state2)
+ }
+
+ @Test func testCheckingEquality() {
+ let state1: UpdateState = .checking(.init(cancel: {}))
+ let state2: UpdateState = .checking(.init(cancel: {}))
+ #expect(state1 == state2)
+ }
+
+ @Test func testNotFoundEquality() {
+ let state1: UpdateState = .notFound
+ let state2: UpdateState = .notFound
+ #expect(state1 == state2)
+ }
+
+ @Test func testInstallingEquality() {
+ let state1: UpdateState = .installing
+ let state2: UpdateState = .installing
+ #expect(state1 == state2)
+ }
+
+ @Test func testPermissionRequestEquality() {
+ let request1 = SPUUpdatePermissionRequest(systemProfile: [])
+ let request2 = SPUUpdatePermissionRequest(systemProfile: [])
+ let state1: UpdateState = .permissionRequest(.init(request: request1, reply: { _ in }))
+ let state2: UpdateState = .permissionRequest(.init(request: request2, reply: { _ in }))
+ #expect(state1 == state2)
+ }
+
+ @Test func testReadyToInstallEquality() {
+ let state1: UpdateState = .readyToInstall(.init(reply: { _ in }))
+ let state2: UpdateState = .readyToInstall(.init(reply: { _ in }))
+ #expect(state1 == state2)
+ }
+
+ @Test func testDownloadingEqualityWithSameProgress() {
+ let state1: UpdateState = .downloading(.init(cancel: {}, expectedLength: 1000, progress: 500))
+ let state2: UpdateState = .downloading(.init(cancel: {}, expectedLength: 1000, progress: 500))
+ #expect(state1 == state2)
+ }
+
+ @Test func testDownloadingInequalityWithDifferentProgress() {
+ let state1: UpdateState = .downloading(.init(cancel: {}, expectedLength: 1000, progress: 500))
+ let state2: UpdateState = .downloading(.init(cancel: {}, expectedLength: 1000, progress: 600))
+ #expect(state1 != state2)
+ }
+
+ @Test func testDownloadingInequalityWithDifferentExpectedLength() {
+ let state1: UpdateState = .downloading(.init(cancel: {}, expectedLength: 1000, progress: 500))
+ let state2: UpdateState = .downloading(.init(cancel: {}, expectedLength: 2000, progress: 500))
+ #expect(state1 != state2)
+ }
+
+ @Test func testDownloadingEqualityWithNilExpectedLength() {
+ let state1: UpdateState = .downloading(.init(cancel: {}, expectedLength: nil, progress: 500))
+ let state2: UpdateState = .downloading(.init(cancel: {}, expectedLength: nil, progress: 500))
+ #expect(state1 == state2)
+ }
+
+ @Test func testExtractingEqualityWithSameProgress() {
+ let state1: UpdateState = .extracting(.init(progress: 0.5))
+ let state2: UpdateState = .extracting(.init(progress: 0.5))
+ #expect(state1 == state2)
+ }
+
+ @Test func testExtractingInequalityWithDifferentProgress() {
+ let state1: UpdateState = .extracting(.init(progress: 0.5))
+ let state2: UpdateState = .extracting(.init(progress: 0.6))
+ #expect(state1 != state2)
+ }
+
+ @Test func testErrorEqualityWithSameDescription() {
+ let error1 = NSError(domain: "Test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Error message"])
+ let error2 = NSError(domain: "Test", code: 2, userInfo: [NSLocalizedDescriptionKey: "Error message"])
+ let state1: UpdateState = .error(.init(error: error1, retry: {}, dismiss: {}))
+ let state2: UpdateState = .error(.init(error: error2, retry: {}, dismiss: {}))
+ #expect(state1 == state2)
+ }
+
+ @Test func testErrorInequalityWithDifferentDescription() {
+ let error1 = NSError(domain: "Test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Error 1"])
+ let error2 = NSError(domain: "Test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Error 2"])
+ let state1: UpdateState = .error(.init(error: error1, retry: {}, dismiss: {}))
+ let state2: UpdateState = .error(.init(error: error2, retry: {}, dismiss: {}))
+ #expect(state1 != state2)
+ }
+
+ @Test func testDifferentStatesAreNotEqual() {
+ let state1: UpdateState = .idle
+ let state2: UpdateState = .checking(.init(cancel: {}))
+ #expect(state1 != state2)
+ }
+
+ // MARK: - isIdle Tests
+
+ @Test func testIsIdleTrue() {
+ let state: UpdateState = .idle
+ #expect(state.isIdle == true)
+ }
+
+ @Test func testIsIdleFalse() {
+ let state: UpdateState = .checking(.init(cancel: {}))
+ #expect(state.isIdle == false)
+ }
+}
diff --git a/macos/Tests/Update/UpdateViewModelTests.swift b/macos/Tests/Update/UpdateViewModelTests.swift
new file mode 100644
index 000000000..dd88cbe83
--- /dev/null
+++ b/macos/Tests/Update/UpdateViewModelTests.swift
@@ -0,0 +1,97 @@
+import Testing
+import Foundation
+import SwiftUI
+import Sparkle
+@testable import Ghostty
+
+struct UpdateViewModelTests {
+ // MARK: - Text Formatting Tests
+
+ @Test func testIdleText() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .idle
+ #expect(viewModel.text == "")
+ }
+
+ @Test func testPermissionRequestText() {
+ let viewModel = UpdateViewModel()
+ let request = SPUUpdatePermissionRequest(systemProfile: [])
+ viewModel.state = .permissionRequest(.init(request: request, reply: { _ in }))
+ #expect(viewModel.text == "Enable Automatic Updates?")
+ }
+
+ @Test func testCheckingText() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .checking(.init(cancel: {}))
+ #expect(viewModel.text == "Checking for Updates…")
+ }
+
+ @Test func testDownloadingTextWithKnownLength() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .downloading(.init(cancel: {}, expectedLength: 1000, progress: 500))
+ #expect(viewModel.text == "Downloading: 50%")
+ }
+
+ @Test func testDownloadingTextWithUnknownLength() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .downloading(.init(cancel: {}, expectedLength: nil, progress: 500))
+ #expect(viewModel.text == "Downloading…")
+ }
+
+ @Test func testDownloadingTextWithZeroExpectedLength() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .downloading(.init(cancel: {}, expectedLength: 0, progress: 500))
+ #expect(viewModel.text == "Downloading…")
+ }
+
+ @Test func testExtractingText() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .extracting(.init(progress: 0.75))
+ #expect(viewModel.text == "Preparing: 75%")
+ }
+
+ @Test func testReadyToInstallText() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .readyToInstall(.init(reply: { _ in }))
+ #expect(viewModel.text == "Install Update")
+ }
+
+ @Test func testInstallingText() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .installing
+ #expect(viewModel.text == "Installing…")
+ }
+
+ @Test func testNotFoundText() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .notFound
+ #expect(viewModel.text == "No Updates Available")
+ }
+
+ @Test func testErrorText() {
+ let viewModel = UpdateViewModel()
+ let error = NSError(domain: "Test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Network error"])
+ viewModel.state = .error(.init(error: error, retry: {}, dismiss: {}))
+ #expect(viewModel.text == "Network error")
+ }
+
+ // MARK: - Max Width Text Tests
+
+ @Test func testMaxWidthTextForDownloading() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .downloading(.init(cancel: {}, expectedLength: 1000, progress: 50))
+ #expect(viewModel.maxWidthText == "Downloading: 100%")
+ }
+
+ @Test func testMaxWidthTextForExtracting() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .extracting(.init(progress: 0.5))
+ #expect(viewModel.maxWidthText == "Preparing: 100%")
+ }
+
+ @Test func testMaxWidthTextForNonProgressState() {
+ let viewModel = UpdateViewModel()
+ viewModel.state = .checking(.init(cancel: {}))
+ #expect(viewModel.maxWidthText == viewModel.text)
+ }
+}