Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions Click2Minimize/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ struct Click2MinimizeApp: App {
}

class AppDelegate: NSObject, NSApplicationDelegate {
var didOpenBrowserForManualUpgrade = false
var urlOpener: (URL) -> Void = { NSWorkspace.shared.open($0) }
var eventTap: CFMachPort?
var mainWindow: NSWindow?
var cancellables = Set<AnyCancellable>()
Expand Down Expand Up @@ -426,25 +428,30 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}

private func fetchLatestDMG(releaseInfo: Release) {
func fetchLatestDMG(releaseInfo: Release, session: URLSession = .shared) {
let url = URL(string: "https://api.github.com/repos/hatimhtm/Click2Minimize/releases/latest")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
let task = session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("Error fetching release info: \(error?.localizedDescription ?? "Unknown error")")
return
}

var foundDMG = false
if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let assets = json["assets"] as? [[String: Any]] {
for asset in assets {
if let downloadURL = asset["browser_download_url"] as? String,
let name = asset["name"] as? String,
name.hasSuffix(".dmg") {
self.downloadDMG(from: downloadURL)
foundDMG = true
break
}
}
}
if !foundDMG {
self.openBrowserForManualUpgrade()
}
}
task.resume()
}
Expand Down Expand Up @@ -509,9 +516,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
task.resume()
}

private func openBrowserForManualUpgrade() {
func openBrowserForManualUpgrade() {
self.didOpenBrowserForManualUpgrade = true
if let url = URL(string: "https://github.com/hatimhtm/Click2Minimize/releases") {
NSWorkspace.shared.open(url)
urlOpener(url)
}
}

Expand Down
87 changes: 87 additions & 0 deletions Click2MinimizeTests/AppDelegateTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import XCTest
@testable import Click2Minimize

class MockURLProtocol: URLProtocol {
static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data?))?

override class func canInit(with request: URLRequest) -> Bool {
return true
}

override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}

override func startLoading() {
guard let handler = MockURLProtocol.requestHandler else {
fatalError("Handler is unavailable.")
}

do {
let (response, data) = try handler(request)
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
if let data = data {
client?.urlProtocol(self, didLoad: data)
}
client?.urlProtocolDidFinishLoading(self)
} catch {
client?.urlProtocol(self, didFailWithError: error)
}
}

override func stopLoading() {
}
}

final class AppDelegateTests: XCTestCase {

var appDelegate: AppDelegate!
var session: URLSession!

override func setUpWithError() throws {
try super.setUpWithError()
appDelegate = AppDelegate()

let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [MockURLProtocol.self]
session = URLSession(configuration: configuration)
}

override func tearDownWithError() throws {
appDelegate = nil
session = nil
MockURLProtocol.requestHandler = nil
try super.tearDownWithError()
}

func testFetchLatestDMG_JSONParsingError() throws {
// Arrange
let expectation = self.expectation(description: "Fetch Latest DMG")

// Mock invalid JSON response (missing "assets" key)
let jsonString = """
{
"tag_name": "v1.2.0"
}
"""
let responseData = jsonString.data(using: .utf8)!

MockURLProtocol.requestHandler = { request in
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
return (response, responseData)
}

// Override urlOpener to fulfill expectation and avoid actual opening
appDelegate.urlOpener = { url in
XCTAssertEqual(url.absoluteString, "https://github.com/hatimhtm/Click2Minimize/releases")
expectation.fulfill()
}

// Act
appDelegate.fetchLatestDMG(releaseInfo: AppDelegate.Release(tag_name: "v1.2.0"), session: session)

// Assert
waitForExpectations(timeout: 2.0)
XCTAssertTrue(appDelegate.didOpenBrowserForManualUpgrade)
}
}