Improve tests.

Signed-off-by: Ross Goldberg <484615+rgoldberg@users.noreply.github.com>
This commit is contained in:
Ross Goldberg 2024-10-01 22:16:06 -04:00
parent f8d7a36a4c
commit dccac33abb
No known key found for this signature in database
20 changed files with 116 additions and 142 deletions

View file

@ -10,6 +10,7 @@
# Disabled rules # Disabled rules
--disable blankLinesAroundMark --disable blankLinesAroundMark
--disable consecutiveSpaces --disable consecutiveSpaces
--disable hoistAwait
--disable hoistPatternLet --disable hoistPatternLet
--disable hoistTry --disable hoistTry
--disable indent --disable indent

View file

@ -18,12 +18,12 @@ public class AccountSpec: QuickSpec {
Mas.initialize() Mas.initialize()
} }
// account command disabled since macOS 12 Monterey https://github.com/mas-cli/mas#%EF%B8%8F-known-issues // account command disabled since macOS 12 Monterey https://github.com/mas-cli/mas#%EF%B8%8F-known-issues
xdescribe("Account command") { describe("Account command") {
xit("displays active account") { it("displays active account") {
expect { expect {
try Mas.Account.parse([]).run() try Mas.Account.parse([]).run()
} }
.toNot(throwError()) .to(throwError(MASError.notSupported))
} }
} }
} }

View file

@ -13,11 +13,6 @@ import Quick
public class HomeSpec: QuickSpec { public class HomeSpec: QuickSpec {
override public static func spec() { override public static func spec() {
let result = SearchResult(
trackId: 1111,
trackViewUrl: "mas preview url",
version: "0.0"
)
let storeSearch = StoreSearchMock() let storeSearch = StoreSearchMock()
let openCommand = OpenSystemCommandMock() let openCommand = OpenSystemCommandMock()
@ -41,13 +36,18 @@ public class HomeSpec: QuickSpec {
.to(throwError(MASError.noSearchResultsFound)) .to(throwError(MASError.noSearchResultsFound))
} }
it("opens app on MAS Preview") { it("opens app on MAS Preview") {
storeSearch.apps[result.trackId] = result let mockResult = SearchResult(
trackId: 1111,
trackViewUrl: "mas preview url",
version: "0.0"
)
storeSearch.apps[mockResult.trackId] = mockResult
expect { expect {
try Mas.Home.parse([String(result.trackId)]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Home.parse([String(mockResult.trackId)])
.run(storeSearch: storeSearch, openCommand: openCommand)
return openCommand.arguments
} }
.toNot(throwError()) == [mockResult.trackViewUrl]
expect(openCommand.arguments).toNot(beNil())
expect(openCommand.arguments!.first!) == result.trackViewUrl
} }
} }
} }

View file

@ -13,27 +13,7 @@ import Quick
public class InfoSpec: QuickSpec { public class InfoSpec: QuickSpec {
override public static func spec() { override public static func spec() {
let result = SearchResult(
currentVersionReleaseDate: "2019-01-07T18:53:13Z",
fileSizeBytes: "1024",
minimumOsVersion: "10.14",
price: 2.0,
sellerName: "Awesome Dev",
trackId: 1111,
trackName: "Awesome App",
trackViewUrl: "https://awesome.app",
version: "1.0"
)
let storeSearch = StoreSearchMock() let storeSearch = StoreSearchMock()
let expectedOutput = """
Awesome App 1.0 [2.0]
By: Awesome Dev
Released: 2019-01-07
Minimum OS: 10.14
Size: 1 KB
From: https://awesome.app
"""
beforeSuite { beforeSuite {
Mas.initialize() Mas.initialize()
@ -55,13 +35,32 @@ public class InfoSpec: QuickSpec {
.to(throwError(MASError.noSearchResultsFound)) .to(throwError(MASError.noSearchResultsFound))
} }
it("displays app details") { it("displays app details") {
storeSearch.apps[result.trackId] = result let mockResult = SearchResult(
currentVersionReleaseDate: "2019-01-07T18:53:13Z",
fileSizeBytes: "1024",
minimumOsVersion: "10.14",
price: 2.0,
sellerName: "Awesome Dev",
trackId: 1111,
trackName: "Awesome App",
trackViewUrl: "https://awesome.app",
version: "1.0"
)
storeSearch.apps[mockResult.trackId] = mockResult
let output = OutputListener() let output = OutputListener()
expect { expect {
try Mas.Info.parse([String(result.trackId)]).run(storeSearch: storeSearch) try Mas.Info.parse([String(mockResult.trackId)]).run(storeSearch: storeSearch)
} }
.toNot(throwError()) .toNot(throwError())
expect(output.contents) == expectedOutput expect(output.contents) == """
Awesome App 1.0 [2.0]
By: Awesome Dev
Released: 2019-01-07
Minimum OS: 10.14
Size: 1 KB
From: https://awesome.app
"""
} }
} }
} }

View file

@ -19,7 +19,7 @@ public class LuckySpec: QuickSpec {
beforeSuite { beforeSuite {
Mas.initialize() Mas.initialize()
} }
describe("lucky command") { xdescribe("lucky command") {
xit("installs the first app matching a search") { xit("installs the first app matching a search") {
expect { expect {
try Mas.Lucky.parse(["Slack"]).run(appLibrary: AppLibraryMock(), storeSearch: storeSearch) try Mas.Lucky.parse(["Slack"]).run(appLibrary: AppLibraryMock(), storeSearch: storeSearch)

View file

@ -14,11 +14,6 @@ import Quick
public class OpenSpec: QuickSpec { public class OpenSpec: QuickSpec {
override public static func spec() { override public static func spec() {
let result = SearchResult(
trackId: 1111,
trackViewUrl: "fakescheme://some/url",
version: "0.0"
)
let storeSearch = StoreSearchMock() let storeSearch = StoreSearchMock()
let openCommand = OpenSystemCommandMock() let openCommand = OpenSystemCommandMock()
@ -42,26 +37,25 @@ public class OpenSpec: QuickSpec {
.to(throwError(MASError.noSearchResultsFound)) .to(throwError(MASError.noSearchResultsFound))
} }
it("opens app in MAS") { it("opens app in MAS") {
storeSearch.apps[result.trackId] = result let mockResult = SearchResult(
trackId: 1111,
trackViewUrl: "fakescheme://some/url",
version: "0.0"
)
storeSearch.apps[mockResult.trackId] = mockResult
expect { expect {
try Mas.Open.parse([result.trackId.description]) try Mas.Open.parse([mockResult.trackId.description])
.run(storeSearch: storeSearch, openCommand: openCommand) .run(storeSearch: storeSearch, openCommand: openCommand)
return openCommand.arguments
} }
.toNot(throwError()) == ["macappstore://some/url"]
expect(openCommand.arguments).toNot(beNil())
let url = URL(string: openCommand.arguments!.first!)
expect(url).toNot(beNil())
expect(url?.scheme) == "macappstore"
} }
it("just opens MAS if no app specified") { it("just opens MAS if no app specified") {
expect { expect {
try Mas.Open.parse([]).run(storeSearch: storeSearch, openCommand: openCommand) try Mas.Open.parse([]).run(storeSearch: storeSearch, openCommand: openCommand)
return openCommand.arguments
} }
.toNot(throwError()) == ["macappstore://"]
expect(openCommand.arguments).toNot(beNil())
let url = URL(string: openCommand.arguments!.first!)
expect(url).toNot(beNil())
expect(url) == URL(string: "macappstore://")
} }
} }
} }

View file

@ -19,8 +19,7 @@ public class OutdatedSpec: QuickSpec {
describe("outdated command") { describe("outdated command") {
it("displays apps with pending updates") { it("displays apps with pending updates") {
expect { expect {
try Mas.Outdated.parse(["--verbose"]) try Mas.Outdated.parse([]).run(appLibrary: AppLibraryMock(), storeSearch: StoreSearchMock())
.run(appLibrary: AppLibraryMock(), storeSearch: StoreSearchMock())
} }
.toNot(throwError()) .toNot(throwError())
} }

View file

@ -13,12 +13,6 @@ import Quick
public class SearchSpec: QuickSpec { public class SearchSpec: QuickSpec {
override public static func spec() { override public static func spec() {
let result = SearchResult(
trackId: 1111,
trackName: "slack",
trackViewUrl: "mas preview url",
version: "0.0"
)
let storeSearch = StoreSearchMock() let storeSearch = StoreSearchMock()
beforeSuite { beforeSuite {
@ -29,7 +23,13 @@ public class SearchSpec: QuickSpec {
storeSearch.reset() storeSearch.reset()
} }
it("can find slack") { it("can find slack") {
storeSearch.apps[result.trackId] = result let mockResult = SearchResult(
trackId: 1111,
trackName: "slack",
trackViewUrl: "mas preview url",
version: "0.0"
)
storeSearch.apps[mockResult.trackId] = mockResult
expect { expect {
try Mas.Search.parse(["slack"]).run(storeSearch: storeSearch) try Mas.Search.parse(["slack"]).run(storeSearch: storeSearch)
} }

View file

@ -18,12 +18,12 @@ public class SignInSpec: QuickSpec {
Mas.initialize() Mas.initialize()
} }
// account command disabled since macOS 10.13 High Sierra https://github.com/mas-cli/mas#%EF%B8%8F-known-issues // account command disabled since macOS 10.13 High Sierra https://github.com/mas-cli/mas#%EF%B8%8F-known-issues
xdescribe("signin command") { describe("signin command") {
xit("signs in") { it("signs in") {
expect { expect {
try Mas.SignIn.parse(["", ""]).run() try Mas.SignIn.parse(["", ""]).run()
} }
.toNot(throwError()) .to(throwError(MASError.notSupported))
} }
} }
} }

View file

@ -13,11 +13,6 @@ import Quick
public class VendorSpec: QuickSpec { public class VendorSpec: QuickSpec {
override public static func spec() { override public static func spec() {
let result = SearchResult(
trackId: 1111,
trackViewUrl: "https://awesome.app",
version: "0.0"
)
let storeSearch = StoreSearchMock() let storeSearch = StoreSearchMock()
let openCommand = OpenSystemCommandMock() let openCommand = OpenSystemCommandMock()
@ -41,14 +36,19 @@ public class VendorSpec: QuickSpec {
.to(throwError(MASError.noSearchResultsFound)) .to(throwError(MASError.noSearchResultsFound))
} }
it("opens vendor app page in browser") { it("opens vendor app page in browser") {
storeSearch.apps[result.trackId] = result let mockResult = SearchResult(
sellerUrl: "https://awesome.app",
trackId: 1111,
trackViewUrl: "https://apps.apple.com/us/app/awesome/id1111?mt=12&uo=4",
version: "0.0"
)
storeSearch.apps[mockResult.trackId] = mockResult
expect { expect {
try Mas.Vendor.parse([String(result.trackId)]) try Mas.Vendor.parse([String(mockResult.trackId)])
.run(storeSearch: storeSearch, openCommand: openCommand) .run(storeSearch: storeSearch, openCommand: openCommand)
return openCommand.arguments
} }
.toNot(throwError()) == [mockResult.sellerUrl]
expect(openCommand.arguments).toNot(beNil())
expect(openCommand.arguments!.first!) == result.sellerUrl
} }
} }
} }

View file

@ -20,12 +20,11 @@ public class MasAppLibrarySpec: QuickSpec {
} }
describe("mas app library") { describe("mas app library") {
it("contains all installed apps") { it("contains all installed apps") {
expect(library.installedApps.count) == apps.count expect(library.installedApps).to(haveCount(apps.count))
expect(library.installedApps.first!.appName) == myApp.appName expect(library.installedApps.first!.appName) == myApp.appName
} }
it("can locate an app by bundle id") { it("can locate an app by bundle id") {
let app = library.installedApp(forBundleId: "com.example")! expect(library.installedApp(forBundleId: "com.example")!.bundleIdentifier) == myApp.bundleIdentifier
expect(app.bundleIdentifier) == myApp.bundleIdentifier
} }
} }
} }

View file

@ -19,18 +19,16 @@ public class MasStoreSearchSpec: QuickSpec {
describe("url string") { describe("url string") {
it("contains the app name") { it("contains the app name") {
let appName = "myapp" let appName = "myapp"
let urlString = MasStoreSearch().searchURL(for: appName, inCountry: "US")?.absoluteString expect {
expect(urlString) == """ MasStoreSearch().searchURL(for: appName, inCountry: "US")?.absoluteString
https://itunes.apple.com/search?media=software&entity=macSoftware&term=\(appName)&country=US }
""" == "https://itunes.apple.com/search?media=software&entity=macSoftware&term=\(appName)&country=US"
} }
it("contains the encoded app name") { it("contains the encoded app name") {
let appName = "My App" expect {
let appNameEncoded = "My%20App" MasStoreSearch().searchURL(for: "My App", inCountry: "US")?.absoluteString
let urlString = MasStoreSearch().searchURL(for: appName, inCountry: "US")?.absoluteString }
expect(urlString) == """ == "https://itunes.apple.com/search?media=software&entity=macSoftware&term=My%20App&country=US"
https://itunes.apple.com/search?media=software&entity=macSoftware&term=\(appNameEncoded)&country=US
"""
} }
} }
describe("store") { describe("store") {
@ -39,16 +37,10 @@ public class MasStoreSearchSpec: QuickSpec {
let networkSession = NetworkSessionMockFromFile(responseFile: "search/slack.json") let networkSession = NetworkSessionMockFromFile(responseFile: "search/slack.json")
let storeSearch = MasStoreSearch(networkManager: NetworkManager(session: networkSession)) let storeSearch = MasStoreSearch(networkManager: NetworkManager(session: networkSession))
var results: [SearchResult] expect {
do { try storeSearch.search(for: "slack").wait()
results = try storeSearch.search(for: "slack").wait()
expect(results.count) == 39
} catch {
let maserror = error as! MASError
if case .jsonParsing(let nserror) = maserror {
fail("\(maserror) \(nserror!)")
}
} }
.to(haveCount(39))
} }
} }
@ -58,9 +50,9 @@ public class MasStoreSearchSpec: QuickSpec {
let networkSession = NetworkSessionMockFromFile(responseFile: "lookup/slack.json") let networkSession = NetworkSessionMockFromFile(responseFile: "lookup/slack.json")
let storeSearch = MasStoreSearch(networkManager: NetworkManager(session: networkSession)) let storeSearch = MasStoreSearch(networkManager: NetworkManager(session: networkSession))
var lookup: SearchResult? var result: SearchResult?
do { do {
lookup = try storeSearch.lookup(appID: appID).wait() result = try storeSearch.lookup(appID: appID).wait()
} catch { } catch {
let maserror = error as! MASError let maserror = error as! MASError
if case .jsonParsing(let nserror) = maserror { if case .jsonParsing(let nserror) = maserror {
@ -68,7 +60,9 @@ public class MasStoreSearchSpec: QuickSpec {
} }
} }
guard let result = lookup else { fatalError("lookup result was nil") } guard let result else {
fatalError("lookup result was nil")
}
expect(result.trackId) == appID expect(result.trackId) == appID
expect(result.bundleId) == "com.tinyspeck.slackmacgap" expect(result.bundleId) == "com.tinyspeck.slackmacgap"

View file

@ -14,9 +14,7 @@ class StoreSearchMock: StoreSearch {
var apps: [AppID: SearchResult] = [:] var apps: [AppID: SearchResult] = [:]
func search(for appName: String) -> Promise<[SearchResult]> { func search(for appName: String) -> Promise<[SearchResult]> {
let filtered = apps.filter { $1.trackName.contains(appName) } .value(apps.filter { $1.trackName.contains(appName) }.map { $1 })
let results = filtered.map { $1 }
return .value(results)
} }
func lookup(appID: AppID) -> Promise<SearchResult?> { func lookup(appID: AppID) -> Promise<SearchResult?> {

View file

@ -13,7 +13,7 @@ import Foundation
class OpenSystemCommandMock: ExternalCommand { class OpenSystemCommandMock: ExternalCommand {
// Stub out protocol logic // Stub out protocol logic
var succeeded = true var succeeded = true
var arguments: [String]? var arguments: [String] = []
// unused // unused
var binaryPath = "/dev/null" var binaryPath = "/dev/null"

View file

@ -19,12 +19,10 @@ public class OpenSystemCommandSpec: QuickSpec {
describe("open system command") { describe("open system command") {
context("binary path") { context("binary path") {
it("defaults to the macOS open command") { it("defaults to the macOS open command") {
let cmd = OpenSystemCommand() expect(OpenSystemCommand().binaryPath) == "/usr/bin/open"
expect(cmd.binaryPath) == "/usr/bin/open"
} }
it("can be overridden") { it("can be overridden") {
let cmd = OpenSystemCommand(binaryPath: "/dev/null") expect(OpenSystemCommand(binaryPath: "/dev/null").binaryPath) == "/dev/null"
expect(cmd.binaryPath) == "/dev/null"
} }
} }
} }

View file

@ -25,8 +25,7 @@ public class AppListsFormatterSpec: QuickSpec {
products = [] products = []
} }
it("formats nothing as empty string") { it("formats nothing as empty string") {
let output = format(products) expect(format(products)) == ""
expect(output) == ""
} }
it("can format a single product") { it("can format a single product") {
let product = SoftwareProductMock( let product = SoftwareProductMock(
@ -36,8 +35,7 @@ public class AppListsFormatterSpec: QuickSpec {
bundleVersion: "19.2.1", bundleVersion: "19.2.1",
itemIdentifier: 12345 itemIdentifier: 12345
) )
let output = format([product]) expect(format([product])) == "12345 Awesome App (19.2.1)"
expect(output) == "12345 Awesome App (19.2.1)"
} }
it("can format two products") { it("can format two products") {
products = [ products = [
@ -56,8 +54,8 @@ public class AppListsFormatterSpec: QuickSpec {
itemIdentifier: 67890 itemIdentifier: 67890
), ),
] ]
let output = format(products) expect(format(products))
expect(output) == "12345 Awesome App (19.2.1)\n67890 Even Better App (1.2.0)" == "12345 Awesome App (19.2.1)\n67890 Even Better App (1.2.0)"
} }
} }
} }

View file

@ -25,8 +25,7 @@ public class SearchResultsFormatterSpec: QuickSpec {
results = [] results = []
} }
it("formats nothing as empty string") { it("formats nothing as empty string") {
let output = format(results, false) expect(format(results, false)) == ""
expect(output) == ""
} }
it("can format a single result") { it("can format a single result") {
let result = SearchResult( let result = SearchResult(
@ -35,8 +34,7 @@ public class SearchResultsFormatterSpec: QuickSpec {
trackName: "Awesome App", trackName: "Awesome App",
version: "19.2.1" version: "19.2.1"
) )
let output = format([result], false) expect(format([result], false)) == " 12345 Awesome App (19.2.1)"
expect(output) == " 12345 Awesome App (19.2.1)"
} }
it("can format a single result with price") { it("can format a single result with price") {
let result = SearchResult( let result = SearchResult(
@ -45,8 +43,7 @@ public class SearchResultsFormatterSpec: QuickSpec {
trackName: "Awesome App", trackName: "Awesome App",
version: "19.2.1" version: "19.2.1"
) )
let output = format([result], true) expect(format([result], true)) == " 12345 Awesome App $ 9.87 (19.2.1)"
expect(output) == " 12345 Awesome App $ 9.87 (19.2.1)"
} }
it("can format a two results") { it("can format a two results") {
results = [ results = [
@ -63,8 +60,8 @@ public class SearchResultsFormatterSpec: QuickSpec {
version: "1.2.0" version: "1.2.0"
), ),
] ]
let output = format(results, false) expect(format(results, false))
expect(output) == " 12345 Awesome App (19.2.1)\n 67890 Even Better App (1.2.0)" == " 12345 Awesome App (19.2.1)\n 67890 Even Better App (1.2.0)"
} }
it("can format a two results with prices") { it("can format a two results with prices") {
results = [ results = [
@ -81,8 +78,7 @@ public class SearchResultsFormatterSpec: QuickSpec {
version: "1.2.0" version: "1.2.0"
), ),
] ]
let output = format(results, true) expect(format(results, true))
expect(output)
== " 12345 Awesome App $ 9.87 (19.2.1)\n 67890 Even Better App $ 0.01 (1.2.0)" == " 12345 Awesome App $ 9.87 (19.2.1)\n 67890 Even Better App $ 0.01 (1.2.0)"
} }
} }

View file

@ -19,18 +19,16 @@ public class SearchResultListSpec: QuickSpec {
} }
describe("search result list") { describe("search result list") {
it("can parse bbedit") { it("can parse bbedit") {
let data = Data(from: "search/bbedit.json") expect(
let decoder = JSONDecoder() try JSONDecoder().decode(SearchResultList.self, from: Data(from: "search/bbedit.json")).resultCount
let results = try decoder.decode(SearchResultList.self, from: data) )
== 1
expect(results.resultCount) == 1
} }
it("can parse things") { it("can parse things") {
let data = Data(from: "search/things.json") expect(
let decoder = JSONDecoder() try JSONDecoder().decode(SearchResultList.self, from: Data(from: "search/things.json")).resultCount
let results = try decoder.decode(SearchResultList.self, from: data) )
== 50
expect(results.resultCount) == 50
} }
} }
} }

View file

@ -19,11 +19,12 @@ public class SearchResultSpec: QuickSpec {
} }
describe("search result") { describe("search result") {
it("can parse things") { it("can parse things") {
let data = Data(from: "search/things-that-go-bump.json") expect(
let decoder = JSONDecoder() try JSONDecoder()
let result = try decoder.decode(SearchResult.self, from: data) .decode(SearchResult.self, from: Data(from: "search/things-that-go-bump.json"))
.bundleId
expect(result.bundleId) == "uikitformac.com.tinybop.thingamabops" )
== "uikitformac.com.tinybop.thingamabops"
} }
} }
} }

View file

@ -31,8 +31,7 @@ class NetworkSessionMockFromFile: NetworkSessionMock {
else { fatalError("Unable to load file \(responseFile)") } else { fatalError("Unable to load file \(responseFile)") }
do { do {
let data = try Data(contentsOf: fileURL, options: .mappedIfSafe) return .value(try Data(contentsOf: fileURL, options: .mappedIfSafe))
return .value(data)
} catch { } catch {
print("Error opening file: \(error)") print("Error opening file: \(error)")
return Promise(error: error) return Promise(error: error)