1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 03:50:08 +02:00

Test improvements

This commit is contained in:
2023-03-03 16:49:17 +01:00
parent 063a729d67
commit ed1d7f8aed
9 changed files with 196 additions and 32 deletions

View File

@ -161,6 +161,7 @@
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; }; C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; }; C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; }; C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */; };
C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; }; C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; };
C450C8C628C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; }; C450C8C628C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; };
C450C8C728C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; }; C450C8C728C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; };
@ -860,6 +861,7 @@
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; }; C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; };
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; }; C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; };
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; }; C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; };
C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCheckTest.swift; sourceTree = "<group>"; };
C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; }; C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; };
C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = "<group>"; }; C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = "<group>"; };
C451AFF52969E40F0078E617 /* HelpButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpButton.swift; sourceTree = "<group>"; }; C451AFF52969E40F0078E617 /* HelpButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpButton.swift; sourceTree = "<group>"; };
@ -1480,6 +1482,7 @@
children = ( children = (
C471E7BE28F9B90F0021E251 /* StartupTest.swift */, C471E7BE28F9B90F0021E251 /* StartupTest.swift */,
C469E702294CFDF700A82AB2 /* DomainsListTest.swift */, C469E702294CFDF700A82AB2 /* DomainsListTest.swift */,
C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */,
C4181F1028FAF9330042EA28 /* UITestCase.swift */, C4181F1028FAF9330042EA28 /* UITestCase.swift */,
); );
path = ui; path = ui;
@ -2603,6 +2606,7 @@
C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */, C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */,
C471E82828F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */, C471E82828F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */,
C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */, C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */,
C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */,
C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */, C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */,
C471E80028F9BAD10021E251 /* Xdebug.swift in Sources */, C471E80028F9BAD10021E251 /* Xdebug.swift in Sources */,
C471E7F528F9BAC80021E251 /* PhpEnv.swift in Sources */, C471E7F528F9BAC80021E251 /* PhpEnv.swift in Sources */,

View File

@ -95,7 +95,7 @@
</CommandLineArgument> </CommandLineArgument>
<CommandLineArgument <CommandLineArgument
argument = "--configuration:~/.phpmon_fconf_working.json" argument = "--configuration:~/.phpmon_fconf_working.json"
isEnabled = "NO"> isEnabled = "YES">
</CommandLineArgument> </CommandLineArgument>
<CommandLineArgument <CommandLineArgument
argument = "--configuration:~/.phpmon_fconf_working_no_valet.json" argument = "--configuration:~/.phpmon_fconf_working_no_valet.json"

View File

@ -12,7 +12,7 @@ class Homebrew {
struct Formulae { struct Formulae {
static var php: HomebrewFormula { static var php: HomebrewFormula {
if PhpEnv.shared.homebrewPackage == nil { if PhpEnv.shared.homebrewPackage == nil {
fatalError("You must either load the HomebrewPackage object or call `fake` on the Homebrew class.") return HomebrewFormula("php", elevated: true)
} }
guard let install = PhpEnv.phpInstall else { guard let install = PhpEnv.phpInstall else {

View File

@ -13,6 +13,7 @@ public struct TestableConfiguration: Codable {
var filesystem: [String: FakeFile] var filesystem: [String: FakeFile]
var shellOutput: [String: BatchFakeShellOutput] var shellOutput: [String: BatchFakeShellOutput]
var commandOutput: [String: String] var commandOutput: [String: String]
var preferenceOverrides: [PreferenceName: Bool]
func apply() { func apply() {
Log.separator() Log.separator()
@ -24,6 +25,10 @@ public struct TestableConfiguration: Codable {
ActiveFileSystem.useTestable(filesystem) ActiveFileSystem.useTestable(filesystem)
Log.info("Applying fake commands...") Log.info("Applying fake commands...")
ActiveCommand.useTestable(commandOutput) ActiveCommand.useTestable(commandOutput)
Log.info("Applying temporary preference overrides...")
preferenceOverrides.forEach { (key: PreferenceName, value: Any?) in
Preferences.shared.cachedPreferences[key] = value
}
if Valet.shared.installed { if Valet.shared.installed {
Log.info("Applying fake scanner...") Log.info("Applying fake scanner...")

View File

@ -9,7 +9,7 @@
/** /**
These are the keys used for every preference in the app. These are the keys used for every preference in the app.
*/ */
enum PreferenceName: String { enum PreferenceName: String, Codable {
// FIRST-TIME LAUNCH // FIRST-TIME LAUNCH
case wasLaunchedBefore = "launched_before" case wasLaunchedBefore = "launched_before"

View File

@ -22,6 +22,8 @@ class TestableConfigurations {
: .fake(.binary), : .fake(.binary),
"/opt/homebrew/bin/php" "/opt/homebrew/bin/php"
: .fake(.binary), : .fake(.binary),
"/opt/homebrew/bin/php-config"
: .fake(.binary),
"/opt/homebrew/bin/valet" "/opt/homebrew/bin/valet"
: .fake(.binary), : .fake(.binary),
"/opt/homebrew/opt/php" "/opt/homebrew/opt/php"
@ -139,7 +141,21 @@ class TestableConfigurations {
"/opt/homebrew/bin/brew services info --all --json" "/opt/homebrew/bin/brew services info --all --json"
: .instant(ShellStrings.shared.brewServicesAsUser), : .instant(ShellStrings.shared.brewServicesAsUser),
"curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'" "curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'"
: .instant("version '5.6.2_976'"), : .delayed(0.5, """
cask 'phpmon-dev' do
depends_on formula: 'gnu-sed'
version '\(App.shortVersion)_\(App.bundleVersion)'
sha256 '1cb147bd1b1fbd52971d90dff577465b644aee7c878f15ede57f46e8f217067a'
url 'https://github.com/nicoverbruggen/phpmon/releases/download/v\(App.shortVersion)/phpmon-dev.zip'
appcast 'https://github.com/nicoverbruggen/phpmon/releases.atom'
name 'PHP Monitor DEV'
homepage 'https://phpmon.app'
app 'PHP Monitor DEV.app', target: "PHP Monitor DEV.app"
end
"""),
"/opt/homebrew/bin/brew unlink php" "/opt/homebrew/bin/brew unlink php"
: .delayed(0.2, "OK"), : .delayed(0.2, "OK"),
"/opt/homebrew/bin/brew unlink php@8.2" "/opt/homebrew/bin/brew unlink php@8.2"
@ -170,7 +186,8 @@ class TestableConfigurations {
: """ : """
/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini, /opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini,
""" """
] ],
preferenceOverrides: [:]
) )
} }
@ -180,27 +197,29 @@ class TestableConfigurations {
architecture: "arm64", architecture: "arm64",
filesystem: [ filesystem: [
"/usr/local/bin/" "/usr/local/bin/"
: .fake(.directory, readOnly: true), : .fake(.directory, readOnly: true),
"/usr/local/bin/composer" "/usr/local/bin/composer"
: .fake(.binary), : .fake(.binary),
"/opt/homebrew/bin/brew" "/opt/homebrew/bin/brew"
: .fake(.binary), : .fake(.binary),
"/opt/homebrew/bin/php" "/opt/homebrew/bin/php"
: .fake(.binary), : .fake(.binary),
"/opt/homebrew/bin/php-config"
: .fake(.binary),
"/opt/homebrew/opt/php" "/opt/homebrew/opt/php"
: .fake(.symlink, "/opt/homebrew/Cellar/php/8.2.0"), : .fake(.symlink, "/opt/homebrew/Cellar/php/8.2.0"),
"/opt/homebrew/opt/php@8.2/bin/php" "/opt/homebrew/opt/php@8.2/bin/php"
: .fake(.symlink, "/opt/homebrew/Cellar/php/8.2.0/bin/php"), : .fake(.symlink, "/opt/homebrew/Cellar/php/8.2.0/bin/php"),
"/opt/homebrew/Cellar/php/8.2.0/bin/php" "/opt/homebrew/Cellar/php/8.2.0/bin/php"
: .fake(.binary), : .fake(.binary),
"/opt/homebrew/Cellar/php/8.2.0/bin/php-config" "/opt/homebrew/Cellar/php/8.2.0/bin/php-config"
: .fake(.binary), : .fake(.binary),
"/opt/homebrew/etc/php/8.2/php-fpm.d/www.conf" "/opt/homebrew/etc/php/8.2/php-fpm.d/www.conf"
: .fake(.text), : .fake(.text),
"/opt/homebrew/etc/php/8.2/php.ini" "/opt/homebrew/etc/php/8.2/php.ini"
: .fake(.text), : .fake(.text),
"/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini" "/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini"
: .fake(.text) : .fake(.text)
], ],
shellOutput: [ shellOutput: [
"sysctl -n sysctl.proc_translated" "sysctl -n sysctl.proc_translated"
@ -306,6 +325,9 @@ class TestableConfigurations {
: """ : """
/opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini, /opt/homebrew/etc/php/8.2/conf.d/php-memory-limits.ini,
""" """
],
preferenceOverrides: [
: // TODO: Add default preferences that are relevant for tests
] ]
) )
} }

View File

@ -16,26 +16,14 @@ final class DomainsListTest: UITestCase {
override func tearDownWithError() throws {} override func tearDownWithError() throws {}
private func openMenu() -> XCPMApplication {
let app = XCPMApplication()
app.withConfiguration(TestableConfigurations.working)
app.launch()
// Note: If this fails here, make sure the menu bar item can be displayed
// If you use Bartender or something like this, this item may be hidden and tests will fail
app.statusItems.firstMatch.click()
return app
}
final func test_can_always_open_domains_list() throws { final func test_can_always_open_domains_list() throws {
let app = openMenu() let app = launch(openMenu: true)
app.menuItems["mi_domain_list".localized].click() app.menuItems["mi_domain_list".localized].click()
} }
final func test_can_filter_domains_list() throws { final func test_can_filter_domains_list() throws {
let app = openMenu() let app = launch(openMenu: true)
app.menuItems["mi_domain_list".localized].click() app.menuItems["mi_domain_list".localized].click()
@ -58,7 +46,7 @@ final class DomainsListTest: UITestCase {
} }
final func test_can_tap_add_domain_button() throws { final func test_can_tap_add_domain_button() throws {
let app = openMenu() let app = launch(openMenu: true)
app.menuItems["mi_domain_list".localized].click() app.menuItems["mi_domain_list".localized].click()

View File

@ -10,9 +10,33 @@ import XCTest
class UITestCase: XCTestCase { class UITestCase: XCTestCase {
/** Launches the app and opens the menu. */
public func launch(
openMenu: Bool = false,
with configuration: TestableConfiguration? = nil
) -> XCPMApplication {
let app = XCPMApplication()
let config = configuration ?? TestableConfigurations.working
app.withConfiguration(config)
app.launch()
// Note: If this fails here, make sure the menu bar item can be displayed
// If you use Bartender or something like this, this item may be hidden and tests will fail
if openMenu {
app.statusItems.firstMatch.click()
}
return app
}
/** Checks if a single element exists. */ /** Checks if a single element exists. */
public func assertExists(_ element: XCUIElement, _ timeout: TimeInterval = 0.05) { public func assertExists(_ element: XCUIElement, _ timeout: TimeInterval = 0.05) {
XCTAssert(element.waitForExistence(timeout: timeout)) XCTAssertTrue(element.waitForExistence(timeout: timeout))
}
/** Checks if a single element fails to exist. */
public func assertNotExists(_ element: XCUIElement, _ timeout: TimeInterval = 0.05) {
XCTAssertFalse(element.waitForExistence(timeout: timeout))
} }
/** Checks if all elements exist. */ /** Checks if all elements exist. */

View File

@ -0,0 +1,121 @@
//
// UpdateCheckTest.swift
// UI Tests
//
// Created by Nico Verbruggen on 13/03/2023.
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import XCTest
final class UpdateCheckTest: UITestCase {
override func setUpWithError() throws {
continueAfterFailure = false
}
override func tearDownWithError() throws {}
final func test_can_check_for_updates_with_no_new_update() throws {
let app = launch(openMenu: true)
app.menuItems["mi_check_for_updates".localized].click()
assertExists(app.staticTexts["updater.alerts.is_latest_version.title".localized], 1.0)
assertExists(app.buttons["generic.ok".localized])
}
final func test_will_prompt_at_launch_new_version_available() throws {
var configuration = TestableConfigurations.working
// Ensure automatic check is enabled
configuration.preferenceOverrides[.automaticBackgroundUpdateCheck] = true
// Ensure an update is available
configuration.shellOutput[
"curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'"
] = .delayed(0.5, """
cask 'phpmon-dev' do
depends_on formula: 'gnu-sed'
version '99.0.0_9999'
sha256 '1cb147bd1b1fbd52971d90dff577465b644aee7c878f15ede57f46e8f217067a'
url 'https://github.com/nicoverbruggen/phpmon/releases/download/v99.0/phpmon-dev.zip'
appcast 'https://github.com/nicoverbruggen/phpmon/releases.atom'
name 'PHP Monitor DEV'
homepage 'https://phpmon.app'
app 'PHP Monitor DEV.app', target: "PHP Monitor DEV.app"
end
""")
let app = launch(openMenu: false, with: configuration)
// Expect to see the content of the appropriate alert box
assertExists(app.staticTexts["updater.alerts.newer_version_available.title".localized("99.0.0 (9999)")], 2)
assertExists(app.buttons["updater.alerts.buttons.install".localized])
assertExists(app.buttons["updater.alerts.buttons.dismiss".localized])
}
final func test_will_require_manual_search_for_update() throws {
var configuration = TestableConfigurations.working
// Ensure automatic check is disabled
configuration.preferenceOverrides[.automaticBackgroundUpdateCheck] = false
// Ensure an update is available
configuration.shellOutput[
"curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'"
] = .delayed(0.5, """
cask 'phpmon-dev' do
depends_on formula: 'gnu-sed'
version '99.0.0_9999'
sha256 '1cb147bd1b1fbd52971d90dff577465b644aee7c878f15ede57f46e8f217067a'
url 'https://github.com/nicoverbruggen/phpmon/releases/download/v99.0/phpmon-dev.zip'
appcast 'https://github.com/nicoverbruggen/phpmon/releases.atom'
name 'PHP Monitor DEV'
homepage 'https://phpmon.app'
app 'PHP Monitor DEV.app', target: "PHP Monitor DEV.app"
end
""")
// Wait for the menu to open and search for updates
let app = launch(openMenu: false, with: configuration)
// The check should not happen if the preference is disabled
assertNotExists(app.staticTexts["updater.alerts.newer_version_available.title".localized("99.0.0 (9999)")], 2)
// Open the menu and check manually
app.statusItems.firstMatch.click()
app.menuItems["mi_check_for_updates".localized].click()
// Expect to see the content of the appropriate alert box
assertExists(app.staticTexts["updater.alerts.newer_version_available.title".localized("99.0.0 (9999)")], 2)
assertExists(app.buttons["updater.alerts.buttons.install".localized])
assertExists(app.buttons["updater.alerts.buttons.dismiss".localized])
}
final func test_could_not_parse_version() throws {
var configuration = TestableConfigurations.working
// Ensure automatic check is disabled
configuration.preferenceOverrides[.automaticBackgroundUpdateCheck] = false
// Ensure an update is available
configuration.shellOutput[
"curl -s --max-time 10 '\(Constants.Urls.DevBuildCaskFile.absoluteString)'"
] = .delayed(0.5, "404 PAGE NOT FOUND")
// Wait for the menu to open and search for updates
let app = launch(openMenu: true, with: configuration)
app.menuItems["mi_check_for_updates".localized].click()
// Expect to see the content of the appropriate alert box
assertExists(app.staticTexts["updater.alerts.cannot_check_for_update.title".localized], 2)
assertExists(app.buttons["generic.ok".localized])
assertExists(app.buttons["updater.alerts.buttons.releases_on_github".localized])
}
}