From ed1d7f8aed4a925699ec2eb96ec3d0344e9ccddd Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Fri, 3 Mar 2023 16:49:17 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Test=20improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 + .../xcschemes/PHP Monitor DEV.xcscheme | 2 +- phpmon/Common/Core/Homebrew.swift | 2 +- .../Testables/TestableConfiguration.swift | 5 + .../Domain/Preferences/PreferenceName.swift | 2 +- tests/Shared/TestableConfigurations.swift | 48 +++++-- tests/ui/DomainsListTest.swift | 18 +-- tests/ui/UITestCase.swift | 26 +++- tests/ui/UpdateCheckTest.swift | 121 ++++++++++++++++++ 9 files changed, 196 insertions(+), 32 deletions(-) create mode 100644 tests/ui/UpdateCheckTest.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 8b548c6..6f9cd36 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -161,6 +161,7 @@ C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.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 */; }; + C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */; }; C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; }; C450C8C628C919EC002A2B4B /* 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 = ""; }; C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = ""; }; C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = ""; }; + C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCheckTest.swift; sourceTree = ""; }; C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = ""; }; C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = ""; }; C451AFF52969E40F0078E617 /* HelpButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpButton.swift; sourceTree = ""; }; @@ -1480,6 +1482,7 @@ children = ( C471E7BE28F9B90F0021E251 /* StartupTest.swift */, C469E702294CFDF700A82AB2 /* DomainsListTest.swift */, + C44E985E29B23EBF0059F773 /* UpdateCheckTest.swift */, C4181F1028FAF9330042EA28 /* UITestCase.swift */, ); path = ui; @@ -2603,6 +2606,7 @@ C471E82C28F9BB340021E251 /* ValetListable.swift in Sources */, C471E82828F9BB310021E251 /* HomebrewDiagnostics.swift in Sources */, C471E81E28F9BB260021E251 /* BetterAlert.swift in Sources */, + C44E985F29B23EBF0059F773 /* UpdateCheckTest.swift in Sources */, C471E7D228F9BA630021E251 /* ActiveFileSystem.swift in Sources */, C471E80028F9BAD10021E251 /* Xdebug.swift in Sources */, C471E7F528F9BAC80021E251 /* PhpEnv.swift in Sources */, diff --git a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor DEV.xcscheme b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor DEV.xcscheme index 9f9fe5d..dd392ba 100644 --- a/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor DEV.xcscheme +++ b/PHP Monitor.xcodeproj/xcshareddata/xcschemes/PHP Monitor DEV.xcscheme @@ -95,7 +95,7 @@ + isEnabled = "YES"> 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 { - let app = openMenu() + let app = launch(openMenu: true) app.menuItems["mi_domain_list".localized].click() } final func test_can_filter_domains_list() throws { - let app = openMenu() + let app = launch(openMenu: true) app.menuItems["mi_domain_list".localized].click() @@ -58,7 +46,7 @@ final class DomainsListTest: UITestCase { } final func test_can_tap_add_domain_button() throws { - let app = openMenu() + let app = launch(openMenu: true) app.menuItems["mi_domain_list".localized].click() diff --git a/tests/ui/UITestCase.swift b/tests/ui/UITestCase.swift index e34f9b1..858b824 100644 --- a/tests/ui/UITestCase.swift +++ b/tests/ui/UITestCase.swift @@ -10,9 +10,33 @@ import XCTest 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. */ 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. */ diff --git a/tests/ui/UpdateCheckTest.swift b/tests/ui/UpdateCheckTest.swift new file mode 100644 index 0000000..aaa2c56 --- /dev/null +++ b/tests/ui/UpdateCheckTest.swift @@ -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]) + } +}