From fdf89c3badf4edb10ab7eb07d428295fe19d1a04 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:07:40 +0100 Subject: [PATCH 001/108] Add accessibiity identifiers for Find in Page controller elements --- DuckDuckGo/FindInPage/FindInPageViewController.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DuckDuckGo/FindInPage/FindInPageViewController.swift b/DuckDuckGo/FindInPage/FindInPageViewController.swift index 985c634777..539fc78a56 100644 --- a/DuckDuckGo/FindInPage/FindInPageViewController.swift +++ b/DuckDuckGo/FindInPage/FindInPageViewController.swift @@ -57,8 +57,11 @@ final class FindInPageViewController: NSViewController { updateFieldStates() closeButton.toolTip = UserText.findInPageCloseTooltip + closeButton.setAccessibilityIdentifier("FindInPageController.closeButton") nextButton.toolTip = UserText.findInPageNextTooltip + nextButton.setAccessibilityIdentifier("FindInPageController.nextButton") previousButton.toolTip = UserText.findInPagePreviousTooltip + previousButton.setAccessibilityIdentifier("FindInPageController.previousButton") } @IBAction func findInPageNext(_ sender: Any?) { From f4270bc626715bc513f87993fcc9a71586f05117 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:21:31 +0100 Subject: [PATCH 002/108] Add accessibilityIdentifier to NSMenuItemExtension.swift --- DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift b/DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift index bfa2e24e09..b3f0870252 100644 --- a/DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift +++ b/DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift @@ -104,6 +104,12 @@ extension NSMenuItem { return self } + @discardableResult + func withAccessibilityIdentifier(_ accessibilityIdentifier: String) -> NSMenuItem { + self.setAccessibilityIdentifier(accessibilityIdentifier) + return self + } + @discardableResult func withImage(_ image: NSImage?) -> NSMenuItem { self.image = image From 55e0e42bc87946cc806b08fe2bed8c320d17b387 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:31:22 +0100 Subject: [PATCH 003/108] Set accessibilityIdentifier for address bar text field --- DuckDuckGo/NavigationBar/View/AddressBarViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DuckDuckGo/NavigationBar/View/AddressBarViewController.swift b/DuckDuckGo/NavigationBar/View/AddressBarViewController.swift index 4caad386b5..256138f56e 100644 --- a/DuckDuckGo/NavigationBar/View/AddressBarViewController.swift +++ b/DuckDuckGo/NavigationBar/View/AddressBarViewController.swift @@ -99,6 +99,7 @@ final class AddressBarViewController: NSViewController { view.layer?.masksToBounds = false addressBarTextField.placeholderString = UserText.addressBarPlaceholder + addressBarTextField.setAccessibilityIdentifier("AddressBarViewController.addressBarTextField") updateView() // only activate active text field leading constraint on its appearance to avoid constraint conflicts From 114109520f9e40f6ddccd52f2497c02b72456474 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:32:06 +0100 Subject: [PATCH 004/108] Add accessibilityIdentifier for Find in Page menu item --- DuckDuckGo/Menus/MainMenu.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index d6d7ac227a..9668b094b0 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -207,7 +207,7 @@ import SubscriptionUI NSMenuItem.separator() NSMenuItem(title: UserText.mainMenuEditFind) { - NSMenuItem(title: UserText.findInPageMenuItem, action: #selector(MainViewController.findInPage), keyEquivalent: "f") + NSMenuItem(title: UserText.findInPageMenuItem, action: #selector(MainViewController.findInPage), keyEquivalent: "f").withAccessibilityIdentifier("MainViewController.findInPage") NSMenuItem(title: UserText.mainMenuEditFindFindNext, action: #selector(MainViewController.findInPageNext), keyEquivalent: "g") NSMenuItem(title: UserText.mainMenuEditFindFindPrevious, action: #selector(MainViewController.findInPagePrevious), keyEquivalent: "G") NSMenuItem.separator() From 1b74f49686aa84ddb99e0a8d4de01ca87fe8a09c Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:32:36 +0100 Subject: [PATCH 005/108] Add FindInPageTests.swift to project --- DuckDuckGo.xcodeproj/project.pbxproj | 4 + UITests/FindInPageTests.swift | 139 +++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 UITests/FindInPageTests.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 90fc764ac7..fe264cb144 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3112,6 +3112,7 @@ EAC80DE0271F6C0100BBF02D /* fb-sdk.js in Resources */ = {isa = PBXBuildFile; fileRef = EAC80DDF271F6C0100BBF02D /* fb-sdk.js */; }; EAE42800275D47FA00DAC26B /* ClickToLoadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE427FF275D47FA00DAC26B /* ClickToLoadModel.swift */; }; EAFAD6CA2728BD1200F9DF00 /* clickToLoad.js in Resources */ = {isa = PBXBuildFile; fileRef = EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */; }; + EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */; }; EE0629722B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; EE0629732B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; EE0629742B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; @@ -4492,6 +4493,7 @@ EAC80DDF271F6C0100BBF02D /* fb-sdk.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "fb-sdk.js"; sourceTree = ""; }; EAE427FF275D47FA00DAC26B /* ClickToLoadModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClickToLoadModel.swift; sourceTree = ""; }; EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = clickToLoad.js; sourceTree = ""; }; + EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManagerExtension.swift; sourceTree = ""; }; EE339227291BDEFD009F62C1 /* JSAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSAlertController.swift; sourceTree = ""; }; EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationsHostingViewController.swift; sourceTree = ""; }; @@ -6220,6 +6222,7 @@ isa = PBXGroup; children = ( 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, + EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, ); path = UITests; sourceTree = ""; @@ -11674,6 +11677,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift new file mode 100644 index 0000000000..7dbf520def --- /dev/null +++ b/UITests/FindInPageTests.swift @@ -0,0 +1,139 @@ +// +// FindInPageTests.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +extension FindInPageTests { + func loremIpsumFileURL() -> URL { + let loremIpsumFileName = "lorem_ipsum.html" + let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) + return loremIpsumHTMLFileURL + } + + func saveLocalHTML() { + let loremIpsumHTML = """ + Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maximus ligula, et tincidunt sapien. Suspendisse posuere diam maximus, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet molestie aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, molestie accumsan eros.

+ +

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

+ +

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lore + m vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

+ """ + let loremIpsumData = Data(loremIpsumHTML.utf8) + + do { + try loremIpsumData.write(to: loremIpsumFileURL(), options: [.atomic, .completeFileProtection]) + } catch { + print(error.localizedDescription) + } + } + + func removeLocalHTML() { + do { + try FileManager.default.removeItem(at: loremIpsumFileURL()) + } catch { + print(error.localizedDescription) + } + } +} + +class FindInPageTests: XCTestCase { + let app = XCUIApplication() + override class func setUp() { + // This is the setUp() class method. + // XCTest calls it before calling the first test method. + // Set up any overall initial state here. + } + + override func setUp() async throws { + // This is the setUp() async instance method. + // XCTest calls it before each test method. + // Perform any asynchronous setup in this method. + } + + override func setUpWithError() throws { + continueAfterFailure = false + + app.launch() + // This is the setUpWithError() instance method. + // XCTest calls it before each test method. + // Set up any synchronous per-test state that might throw errors here. + } + + override func setUp() { + // This is the setUp() instance method. + // XCTest calls it before each test method. + // Set up any synchronous per-test state here. + } + + override class func tearDown() { + // This is the tearDown() class method. + // XCTest calls it after the last test method completes. + // Perform any overall cleanup here. + } + + override func tearDown() { + // This is the tearDown() instance method. + // XCTest calls it after each test method. + // Perform any synchronous per-test cleanup here. + } + + override func tearDownWithError() throws { + // This is the tearDownWithError() instance method. + // XCTest calls it after each test method. + // Perform any synchronous per-test cleanup that might throw errors here. + } + + override func tearDown() async throws { + // This is the tearDown() async instance method. + // XCTest calls it after each test method. + // Perform any asynchronous per-test cleanup here. + } + + func test_findInPage_canBeOpenedWithKeyCommand() throws { + saveLocalHTML() + let searchOrEnterAddressTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: 1), "Address bar text field does not exist when it is expected to exist") + searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: 1), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + + app.typeKey("f", modifierFlags: .command) + let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] + + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: 1), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + removeLocalHTML() + } + + func test_findInPage_canBeOpenedWithMenuItem() throws { + saveLocalHTML() + let searchOrEnterAddressTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: 1), "Address bar text field does not exist when it is expected to exist") + searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] + let findInPageMenuBarItem = app.menuItems["MainViewController.findInPage"] + XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: 1), "Couldn't find Find in Page menu bar item in a reasonable timeframe.") + + findInPageMenuBarItem.click() + let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] + + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: 1), "After invoking find in page via the menu items Edit->Find->Find in Page, the elements of the find in page interface should exist.") + removeLocalHTML() + } +} From 8566be2f42a9f1e41ffdd27776055518a0b9999b Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:34:14 +0100 Subject: [PATCH 006/108] Add more accessibility identifiers to menu items and fix one incorrect one --- DuckDuckGo/Menus/MainMenu.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index 9668b094b0..16aa6aa6a8 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -43,7 +43,7 @@ import SubscriptionUI // MARK: DuckDuckGo let servicesMenu = NSMenu(title: UserText.mainMenuAppServices) - let preferencesMenuItem = NSMenuItem(title: UserText.mainMenuAppPreferences, action: #selector(AppDelegate.openPreferences), keyEquivalent: ",") + let preferencesMenuItem = NSMenuItem(title: UserText.mainMenuAppPreferences, action: #selector(AppDelegate.openPreferences), keyEquivalent: ",").withAccessibilityIdentifier("MainMenu.preferencesMenuItem") // MARK: File let newWindowMenuItem = NSMenuItem(title: UserText.newWindowMenuItem, action: #selector(AppDelegate.newWindow), keyEquivalent: "n") @@ -207,12 +207,12 @@ import SubscriptionUI NSMenuItem.separator() NSMenuItem(title: UserText.mainMenuEditFind) { - NSMenuItem(title: UserText.findInPageMenuItem, action: #selector(MainViewController.findInPage), keyEquivalent: "f").withAccessibilityIdentifier("MainViewController.findInPage") + NSMenuItem(title: UserText.findInPageMenuItem, action: #selector(MainViewController.findInPage), keyEquivalent: "f").withAccessibilityIdentifier("MainMenu.findInPage") NSMenuItem(title: UserText.mainMenuEditFindFindNext, action: #selector(MainViewController.findInPageNext), keyEquivalent: "g") NSMenuItem(title: UserText.mainMenuEditFindFindPrevious, action: #selector(MainViewController.findInPagePrevious), keyEquivalent: "G") NSMenuItem.separator() - NSMenuItem(title: UserText.mainMenuEditFindHideFind, action: #selector(MainViewController.findInPageDone), keyEquivalent: "F") + NSMenuItem(title: UserText.mainMenuEditFindHideFind, action: #selector(MainViewController.findInPageDone), keyEquivalent: "F").withAccessibilityIdentifier("MainMenu.findInPageDone") } NSMenuItem(title: UserText.mainMenuEditSpellingandGrammar) { From c7163ea887d1df19ca0ad1e25abf4fcb1afa8d89 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:35:05 +0100 Subject: [PATCH 007/108] Add accessibilityIdentifier to more options find in page --- DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift index 24ecc77e86..41cc63e3ec 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift @@ -475,6 +475,7 @@ final class MoreOptionsMenu: NSMenu { addItem(withTitle: UserText.findInPageMenuItem, action: #selector(findInPage(_:)), keyEquivalent: "f") .targetting(self) .withImage(.findSearch) + .withAccessibilityIdentifier("MoreOptionsMenu.findInPage") addItem(withTitle: UserText.shareMenuItem, action: nil, keyEquivalent: "") .targetting(self) From 6c98b35d1ea5d819f56801f7796e88c713363f84 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:35:50 +0100 Subject: [PATCH 008/108] Change in-use accessibilityIdentifier for optionsButton so it isn't an English phrase and update in test --- .../NavigationBar/View/NavigationBarViewController.swift | 2 +- SyncE2EUITests/CriticalPathsTests.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift index aa050d3a98..37552f70f2 100644 --- a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift +++ b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift @@ -185,7 +185,7 @@ final class NavigationBarViewController: NSViewController { passwordManagementButton.sendAction(on: .leftMouseDown) optionsButton.toolTip = UserText.applicationMenuTooltip - optionsButton.setAccessibilityIdentifier("Options Button") + optionsButton.setAccessibilityIdentifier("NavigationBarViewController.optionsButton") networkProtectionButton.toolTip = UserText.networkProtectionButtonTooltip diff --git a/SyncE2EUITests/CriticalPathsTests.swift b/SyncE2EUITests/CriticalPathsTests.swift index fd1c305f05..c59174b4fe 100644 --- a/SyncE2EUITests/CriticalPathsTests.swift +++ b/SyncE2EUITests/CriticalPathsTests.swift @@ -296,7 +296,7 @@ final class CriticalPathsTests: XCTestCase { private func addLogin() { let bookmarksWindow = app.windows["Bookmarks"] - bookmarksWindow.buttons["Options Button"].click() + bookmarksWindow.buttons["NavigationBarViewController.optionsButton"].click() bookmarksWindow.menuItems["Autofill"].click() bookmarksWindow.popovers.buttons["add item"].click() bookmarksWindow.popovers.menuItems["createNewLogin"].click() @@ -361,7 +361,7 @@ final class CriticalPathsTests: XCTestCase { private func checkLogins() { let bookmarksWindow = app.windows["Bookmarks"] - bookmarksWindow.buttons["Options Button"].click() + bookmarksWindow.buttons["NavigationBarViewController.optionsButton"].click() bookmarksWindow.menuItems["Autofill"].click() let elementsQuery = bookmarksWindow.popovers.scrollViews.otherElements elementsQuery.buttons["Da, Dax Login, daxthetest"].click() From d1e2190255bcc8e45907c384f253349a6d307546 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:38:14 +0100 Subject: [PATCH 009/108] Find in page tests --- UITests/FindInPageTests.swift | 148 ++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 60 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 7dbf520def..45449d5d4f 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -28,7 +28,7 @@ extension FindInPageTests { func saveLocalHTML() { let loremIpsumHTML = """ - Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maximus ligula, et tincidunt sapien. Suspendisse posuere diam maximus, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet molestie aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, molestie accumsan eros.

+ Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maximus ligula, et tincidunt sapien. Suspendisse posuere diam maximus, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

@@ -55,85 +55,113 @@ extension FindInPageTests { class FindInPageTests: XCTestCase { let app = XCUIApplication() - override class func setUp() { - // This is the setUp() class method. - // XCTest calls it before calling the first test method. - // Set up any overall initial state here. - } - - override func setUp() async throws { - // This is the setUp() async instance method. - // XCTest calls it before each test method. - // Perform any asynchronous setup in this method. - } + let timeout = 0.3 + let searchOrEnterAddressTextField = XCUIApplication().windows.textFields["AddressBarViewController.addressBarTextField"] + let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] + let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] override func setUpWithError() throws { continueAfterFailure = false - + saveLocalHTML() app.launch() - // This is the setUpWithError() instance method. - // XCTest calls it before each test method. - // Set up any synchronous per-test state that might throw errors here. } - override func setUp() { - // This is the setUp() instance method. - // XCTest calls it before each test method. - // Set up any synchronous per-test state here. + override func tearDownWithError() throws { + removeLocalHTML() } - override class func tearDown() { - // This is the tearDown() class method. - // XCTest calls it after the last test method completes. - // Perform any overall cleanup here. + func test_findInPage_canBeOpenedWithKeyCommand() throws { + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") + searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + + app.typeKey("f", modifierFlags: .command) + + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") } - override func tearDown() { - // This is the tearDown() instance method. - // XCTest calls it after each test method. - // Perform any synchronous per-test cleanup here. + func test_findInPage_canBeOpenedWithMenuBarItem() throws { + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") + searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] + XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find Find in Page menu bar item in a reasonable timeframe.") + + findInPageMenuBarItem.click() + + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page via the menu items Edit->Find->Find in Page, the elements of the find in page interface should exist.") } - override func tearDownWithError() throws { - // This is the tearDownWithError() instance method. - // XCTest calls it after each test method. - // Perform any synchronous per-test cleanup that might throw errors here. + func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") + searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + let optionsButton = XCUIApplication().windows.buttons["NavigationBarViewController.optionsButton"] + XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") + optionsButton.click() + + let findInPageMoreOptionsMenuBarItem = app.menuItems["MoreOptionsMenu.findInPage"] + XCTAssertTrue(findInPageMoreOptionsMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find more options find in page menu item in a reasonable timeframe.") + findInPageMoreOptionsMenuBarItem.click() + + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page via the more options find in Page menu item, the elements of the find in page interface should exist.") } - override func tearDown() async throws { - // This is the tearDown() async instance method. - // XCTest calls it after each test method. - // Perform any asynchronous per-test cleanup here. + func test_findInPage_canBeClosedWithEscape() throws { + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") + searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + + app.typeKey(.escape, modifierFlags: []) + + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing find in page with escape, the elements of the find in page interface should no longer exist.") } - func test_findInPage_canBeOpenedWithKeyCommand() throws { - saveLocalHTML() - let searchOrEnterAddressTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: 1), "Address bar text field does not exist when it is expected to exist") + func test_findInPage_canBeClosedWithShiftCommandF() throws { + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") - let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: 1), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") - + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) - let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] - - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: 1), "After invoking find in page with command-f, the elements of the find in page interface should exist.") - removeLocalHTML() + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + + app.typeKey("f", modifierFlags: [.command, .shift]) + + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing find in page with escape, the elements of the find in page interface should no longer exist.") } - func test_findInPage_canBeOpenedWithMenuItem() throws { - saveLocalHTML() - let searchOrEnterAddressTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: 1), "Address bar text field does not exist when it is expected to exist") + func test_findInPage_canBeClosedWithHideFindMenuItem() throws { + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") - let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] - let findInPageMenuBarItem = app.menuItems["MainViewController.findInPage"] - XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: 1), "Couldn't find Find in Page menu bar item in a reasonable timeframe.") - - findInPageMenuBarItem.click() - let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] - - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: 1), "After invoking find in page via the menu items Edit->Find->Find in Page, the elements of the find in page interface should exist.") - removeLocalHTML() + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + + let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] + XCTAssertTrue(findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find find in page done main menu item in a reasonable timeframe.") + findInPageDoneMenuBarItem.click() + + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing find in page with escape, the elements of the find in page interface should no longer exist.") + } +} + +extension XCUIElement { + // https://stackoverflow.com/a/37447150/119717 + + /** + * Waits the specified amount of time for the element’s `exists` property to become `false`. + * + * - Parameter timeout: The amount of time to wait. + * - Returns: `false` if the timeout expires without the element coming out of existence. + */ + func waitForNonExistence(timeout: TimeInterval) -> Bool { + let timeStart = Date().timeIntervalSince1970 + + while Date().timeIntervalSince1970 <= (timeStart + timeout) { + if !exists { return true } + } + + return false } } From 1d36c5788702ca1d8aeb7174feed145130667dcb Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:18:37 +0100 Subject: [PATCH 010/108] Fix accessibilityIdentifiers for find in page text fields, add roles to disambiguate --- DuckDuckGo/FindInPage/FindInPageViewController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/FindInPage/FindInPageViewController.swift b/DuckDuckGo/FindInPage/FindInPageViewController.swift index 539fc78a56..7189f1da78 100644 --- a/DuckDuckGo/FindInPage/FindInPageViewController.swift +++ b/DuckDuckGo/FindInPage/FindInPageViewController.swift @@ -57,11 +57,16 @@ final class FindInPageViewController: NSViewController { updateFieldStates() closeButton.toolTip = UserText.findInPageCloseTooltip - closeButton.setAccessibilityIdentifier("FindInPageController.closeButton") nextButton.toolTip = UserText.findInPageNextTooltip - nextButton.setAccessibilityIdentifier("FindInPageController.nextButton") previousButton.toolTip = UserText.findInPagePreviousTooltip + + nextButton.setAccessibilityIdentifier("FindInPageController.nextButton") + closeButton.setAccessibilityIdentifier("FindInPageController.closeButton") previousButton.setAccessibilityIdentifier("FindInPageController.previousButton") + textField.setAccessibilityIdentifier("FindInPageController.textField") + textField.setAccessibilityRole(.textField) + statusField.setAccessibilityIdentifier("FindInPageController.statusField") + statusField.setAccessibilityRole(.textField) } @IBAction func findInPageNext(_ sender: Any?) { From 5d7ff4f3a657915ecc4a3fe5134db4aa4e57d8a0 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:41:29 +0100 Subject: [PATCH 011/108] Test for number of search hits in page, single window enforecement, and cleanup --- UITests/FindInPageTests.swift | 88 +++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 45449d5d4f..8a29e6bcab 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -18,41 +18,6 @@ import XCTest -extension FindInPageTests { - func loremIpsumFileURL() -> URL { - let loremIpsumFileName = "lorem_ipsum.html" - let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) - return loremIpsumHTMLFileURL - } - - func saveLocalHTML() { - let loremIpsumHTML = """ - Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maximus ligula, et tincidunt sapien. Suspendisse posuere diam maximus, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

- -

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

- -

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lore - m vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

- """ - let loremIpsumData = Data(loremIpsumHTML.utf8) - - do { - try loremIpsumData.write(to: loremIpsumFileURL(), options: [.atomic, .completeFileProtection]) - } catch { - print(error.localizedDescription) - } - } - - func removeLocalHTML() { - do { - try FileManager.default.removeItem(at: loremIpsumFileURL()) - } catch { - print(error.localizedDescription) - } - } -} - class FindInPageTests: XCTestCase { let app = XCUIApplication() let timeout = 0.3 @@ -64,6 +29,9 @@ class FindInPageTests: XCTestCase { continueAfterFailure = false saveLocalHTML() app.launch() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) + } override func tearDownWithError() throws { @@ -144,6 +112,56 @@ class FindInPageTests: XCTestCase { XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing find in page with escape, the elements of the find in page interface should no longer exist.") } + + func test_findInPage_showsCorrectNumberOfOccurences() throws { + XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") + searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find find in page statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the find in page status field when it was expected.") + let statusFieldTextContent = statusField.value as! String + + XCTAssertEqual(statusFieldTextContent, "1 of 6") // Note: this is not a localized test element, and it should have a localization strategy. + } +} + +extension FindInPageTests { + func loremIpsumFileURL() -> URL { + let loremIpsumFileName = "lorem_ipsum.html" + let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) + return loremIpsumHTMLFileURL + } + + func saveLocalHTML() { + let loremIpsumHTML = """ + Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maximus ligula, et tincidunt sapien. Suspendisse posuere diam maximus, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

+ +

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

+ +

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

+ """ + let loremIpsumData = Data(loremIpsumHTML.utf8) + + do { + try loremIpsumData.write(to: loremIpsumFileURL(), options: []) + } catch { + print(error.localizedDescription) + } + } + + func removeLocalHTML() { + do { + try FileManager.default.removeItem(at: loremIpsumFileURL()) + } catch { + print(error.localizedDescription) + } + } } extension XCUIElement { From 067ea21cbfc8eb3c0ba22b7fd8a86ae91bf7538a Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:50:00 +0100 Subject: [PATCH 012/108] Only deal with the HTML once in the test case run --- UITests/FindInPageTests.swift | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 8a29e6bcab..777d782369 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -25,22 +25,24 @@ class FindInPageTests: XCTestCase { let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] + override class func setUp() { + saveLocalHTML() + } + + override class func tearDown() { + removeLocalHTML() + } + override func setUpWithError() throws { continueAfterFailure = false - saveLocalHTML() app.launch() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window app.typeKey("n", modifierFlags: .command) - - } - - override func tearDownWithError() throws { - removeLocalHTML() } func test_findInPage_canBeOpenedWithKeyCommand() throws { XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) @@ -50,7 +52,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeOpenedWithMenuBarItem() throws { XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find Find in Page menu bar item in a reasonable timeframe.") @@ -62,7 +64,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") let optionsButton = XCUIApplication().windows.buttons["NavigationBarViewController.optionsButton"] XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") @@ -77,7 +79,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeClosedWithEscape() throws { XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") @@ -89,7 +91,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeClosedWithShiftCommandF() throws { XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") @@ -101,7 +103,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeClosedWithHideFindMenuItem() throws { XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") @@ -115,7 +117,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_showsCorrectNumberOfOccurences() throws { XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(loremIpsumFileURL().absoluteString)\r") + searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") @@ -131,14 +133,14 @@ class FindInPageTests: XCTestCase { } extension FindInPageTests { - func loremIpsumFileURL() -> URL { + class func loremIpsumFileURL() -> URL { let loremIpsumFileName = "lorem_ipsum.html" let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) return loremIpsumHTMLFileURL } - func saveLocalHTML() { + class func saveLocalHTML() { let loremIpsumHTML = """ Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maximus ligula, et tincidunt sapien. Suspendisse posuere diam maximus, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

@@ -155,7 +157,7 @@ extension FindInPageTests { } } - func removeLocalHTML() { + class func removeLocalHTML() { do { try FileManager.default.removeItem(at: loremIpsumFileURL()) } catch { From d5b4bf3bce05c3c2076334af43e4cba7fdfc79d0 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:14:02 +0100 Subject: [PATCH 013/108] Add waitForNonExistence XCUIElement extension to UI Tests Common --- UITests/Common/XCUIElementExtension.swift | 42 +++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 UITests/Common/XCUIElementExtension.swift diff --git a/UITests/Common/XCUIElementExtension.swift b/UITests/Common/XCUIElementExtension.swift new file mode 100644 index 0000000000..8558cd9a7e --- /dev/null +++ b/UITests/Common/XCUIElementExtension.swift @@ -0,0 +1,42 @@ +// +// XCUIElementExtension.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +extension XCUIElement { + + // https://stackoverflow.com/a/63089781/119717 + // Licensed under https://creativecommons.org/licenses/by-sa/4.0/ + // Credit: Adil Hussain + + /** + * Waits the specified amount of time for the element’s `exists` property to become `false`. + * + * - Parameter timeout: The amount of time to wait. + * - Returns: `false` if the timeout expires without the element coming out of existence. + */ + func waitForNonExistence(timeout: TimeInterval) -> Bool { + let timeStart = Date().timeIntervalSince1970 + + while Date().timeIntervalSince1970 <= (timeStart + timeout) { + if !exists { return true } + } + + return false + } +} From 2b71d4edd85b9b0db40cd3fee4d19619c845d3a0 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:14:27 +0100 Subject: [PATCH 014/108] Add NSImage cropping to NSImageExtensions.swift --- .../Common/Extensions/NSImageExtensions.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/DuckDuckGo/Common/Extensions/NSImageExtensions.swift b/DuckDuckGo/Common/Extensions/NSImageExtensions.swift index da11e9af3d..ad7e0bae65 100644 --- a/DuckDuckGo/Common/Extensions/NSImageExtensions.swift +++ b/DuckDuckGo/Common/Extensions/NSImageExtensions.swift @@ -59,6 +59,19 @@ extension NSImage { return image } + func cropped(to rect: CGRect) -> NSImage { + let image = NSImage(size: rect.size) + image.lockFocus() + + let targetRect = rect + let currentRect = NSRect(x: 0, y: 0, width: self.size.width, height: self.size.height) + + self.draw(in: targetRect, from: rect, operation: .copy, fraction: 1.0) + + image.unlockFocus() + return image + } + func ciImage(with size: NSSize?) -> CIImage { var rect = NSRect(origin: .zero, size: size ?? self.size) return CIImage(cgImage: self.cgImage(forProposedRect: &rect, context: nil, hints: nil)!) From 05991354c0cbeefbda7e075236b27e8a85a98715 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:15:37 +0100 Subject: [PATCH 015/108] Add extensions to UI Tests target --- DuckDuckGo.xcodeproj/project.pbxproj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 97bac2bb0f..18676da400 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3131,6 +3131,9 @@ EEA3EEB12B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; EEA3EEB32B24EC0600E8333A /* VPNLocationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */; }; EEAD7A7C2A1D3E20002A24E7 /* AppLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */; }; + EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */; }; + EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B139AFC26B60BD800894F82 /* NSImageExtensions.swift */; }; + EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC5E4E325D6BA9C007F5990 /* NSSizeExtension.swift */; }; EEC111E4294D06020086524F /* JSAlert.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEC111E3294D06020086524F /* JSAlert.storyboard */; }; EEC111E6294D06290086524F /* JSAlertViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC111E5294D06290086524F /* JSAlertViewModel.swift */; }; EEC4A65E2B277E8D00F7C0AA /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; @@ -4500,6 +4503,7 @@ EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNLocationViewModel.swift; sourceTree = ""; }; EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLauncher.swift; sourceTree = ""; }; + EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCUIElementExtension.swift; sourceTree = ""; }; EEC111E3294D06020086524F /* JSAlert.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = JSAlert.storyboard; sourceTree = ""; }; EEC111E5294D06290086524F /* JSAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSAlertViewModel.swift; sourceTree = ""; }; EEC4A6682B2C87D300F7C0AA /* VPNLocationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationView.swift; sourceTree = ""; }; @@ -6221,6 +6225,7 @@ 7B4CE8DB26F02108009134B1 /* UITests */ = { isa = PBXGroup; children = ( + EEBCE6802BA444FA00B9DF00 /* Common */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, ); @@ -8395,6 +8400,14 @@ path = JSAlert; sourceTree = ""; }; + EEBCE6802BA444FA00B9DF00 /* Common */ = { + isa = PBXGroup; + children = ( + EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */, + ); + path = Common; + sourceTree = ""; + }; EEC589D62A4F1B1F00BCD60C /* AppAndExtensionAndAgentTargets */ = { isa = PBXGroup; children = ( @@ -11677,8 +11690,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */, EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, + EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */, + EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 5e38ef922fd2cfb64c8f6e21f15a3b4fbc0fafc0 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:15:59 +0100 Subject: [PATCH 016/108] Clean up tests and language --- UITests/FindInPageTests.swift | 135 ++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 64 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 777d782369..793956e6dc 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -21,7 +21,7 @@ import XCTest class FindInPageTests: XCTestCase { let app = XCUIApplication() let timeout = 0.3 - let searchOrEnterAddressTextField = XCUIApplication().windows.textFields["AddressBarViewController.addressBarTextField"] + let addressBarTextField = XCUIApplication().windows.textFields["AddressBarViewController.addressBarTextField"] let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] @@ -41,108 +41,135 @@ class FindInPageTests: XCTestCase { } func test_findInPage_canBeOpenedWithKeyCommand() throws { - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") } func test_findInPage_canBeOpenedWithMenuBarItem() throws { - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] - XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find Find in Page menu bar item in a reasonable timeframe.") + XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe.") findInPageMenuBarItem.click() - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page via the menu items Edit->Find->Find in Page, the elements of the find in page interface should exist.") + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist.") } func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") - let optionsButton = XCUIApplication().windows.buttons["NavigationBarViewController.optionsButton"] + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") optionsButton.click() - let findInPageMoreOptionsMenuBarItem = app.menuItems["MoreOptionsMenu.findInPage"] - XCTAssertTrue(findInPageMoreOptionsMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find more options find in page menu item in a reasonable timeframe.") - findInPageMoreOptionsMenuBarItem.click() + let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] + XCTAssertTrue(findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe.") + findInPageMoreOptionsMenuItem.click() - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page via the more options find in Page menu item, the elements of the find in page interface should exist.") + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist.") } func test_findInPage_canBeClosedWithEscape() throws { - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") app.typeKey(.escape, modifierFlags: []) - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing find in page with escape, the elements of the find in page interface should no longer exist.") + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") } func test_findInPage_canBeClosedWithShiftCommandF() throws { - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") app.typeKey("f", modifierFlags: [.command, .shift]) - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing find in page with escape, the elements of the find in page interface should no longer exist.") + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") } func test_findInPage_canBeClosedWithHideFindMenuItem() throws { - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] - XCTAssertTrue(findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find find in page done main menu item in a reasonable timeframe.") + XCTAssertTrue(findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe.") findInPageDoneMenuBarItem.click() - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing find in page with escape, the elements of the find in page interface should no longer exist.") + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") } func test_findInPage_showsCorrectNumberOfOccurences() throws { - XCTAssertTrue(searchOrEnterAddressTextField.waitForExistence(timeout: timeout), "Address bar text field does not exist when it is expected to exist") - searchOrEnterAddressTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local lorem ipsum web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking find in page with command-f, the elements of the find in page interface should exist.") + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find find in page statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the find in page status field when it was expected.") + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") let statusFieldTextContent = statusField.value as! String XCTAssertEqual(statusFieldTextContent, "1 of 6") // Note: this is not a localized test element, and it should have a localization strategy. } + + func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") + let statusFieldTextContent = statusField.value as! String + + XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + + let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() + let croppedRect = NSRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: webViewWithSelectedWordsScreenshot.image.size.width, height: webViewWithSelectedWordsScreenshot.image.size.height-80)) + let croppedImage = webViewWithSelectedWordsScreenshot.image.cropped(to: croppedRect) + } } extension FindInPageTests { class func loremIpsumFileURL() -> URL { let loremIpsumFileName = "lorem_ipsum.html" - let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + XCTAssertNotNil(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, "It wasn't possible to obtain a local file URL for the sandbox Documents directory.") + let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) return loremIpsumHTMLFileURL } class func saveLocalHTML() { let loremIpsumHTML = """ - Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maximus ligula, et tincidunt sapien. Suspendisse posuere diam maximus, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

+ + Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

@@ -153,7 +180,7 @@ extension FindInPageTests { do { try loremIpsumData.write(to: loremIpsumFileURL(), options: []) } catch { - print(error.localizedDescription) + XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") } } @@ -161,27 +188,7 @@ extension FindInPageTests { do { try FileManager.default.removeItem(at: loremIpsumFileURL()) } catch { - print(error.localizedDescription) - } - } -} - -extension XCUIElement { - // https://stackoverflow.com/a/37447150/119717 - - /** - * Waits the specified amount of time for the element’s `exists` property to become `false`. - * - * - Parameter timeout: The amount of time to wait. - * - Returns: `false` if the timeout expires without the element coming out of existence. - */ - func waitForNonExistence(timeout: TimeInterval) -> Bool { - let timeStart = Date().timeIntervalSince1970 - - while Date().timeIntervalSince1970 <= (timeStart + timeout) { - if !exists { return true } + XCTFail("It wasn't possible to remove the required local HTML file for the tests: \(error.localizedDescription)") } - - return false } } From 956a6bed5635ed2e8d342da0920a150409d489ea Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:47:37 +0100 Subject: [PATCH 017/108] Adds test to detect selected find result highlight color --- .../Common/Extensions/NSImageExtensions.swift | 13 - UITests/FindInPageTests.swift | 431 ++++++++++-------- 2 files changed, 251 insertions(+), 193 deletions(-) diff --git a/DuckDuckGo/Common/Extensions/NSImageExtensions.swift b/DuckDuckGo/Common/Extensions/NSImageExtensions.swift index ad7e0bae65..da11e9af3d 100644 --- a/DuckDuckGo/Common/Extensions/NSImageExtensions.swift +++ b/DuckDuckGo/Common/Extensions/NSImageExtensions.swift @@ -59,19 +59,6 @@ extension NSImage { return image } - func cropped(to rect: CGRect) -> NSImage { - let image = NSImage(size: rect.size) - image.lockFocus() - - let targetRect = rect - let currentRect = NSRect(x: 0, y: 0, width: self.size.width, height: self.size.height) - - self.draw(in: targetRect, from: rect, operation: .copy, fraction: 1.0) - - image.unlockFocus() - return image - } - func ciImage(with size: NSSize?) -> CIImage { var rect = NSRect(origin: .zero, size: size ?? self.size) return CIImage(cgImage: self.cgImage(forProposedRect: &rect, context: nil, hints: nil)!) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 793956e6dc..7aaca122de 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -1,194 +1,265 @@ // -// FindInPageTests.swift +// FindInPageTests.swift // -// Copyright © 2024 DuckDuckGo. All rights reserved. +// Copyright © 2024 DuckDuckGo. All rights reserved. // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // import XCTest class FindInPageTests: XCTestCase { - let app = XCUIApplication() - let timeout = 0.3 - let addressBarTextField = XCUIApplication().windows.textFields["AddressBarViewController.addressBarTextField"] - let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] - let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] - - override class func setUp() { - saveLocalHTML() - } - - override class func tearDown() { - removeLocalHTML() - } - - override func setUpWithError() throws { - continueAfterFailure = false - app.launch() - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window - app.typeKey("n", modifierFlags: .command) - } - - func test_findInPage_canBeOpenedWithKeyCommand() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - - app.typeKey("f", modifierFlags: .command) - - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - } - - func test_findInPage_canBeOpenedWithMenuBarItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] - XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe.") - - findInPageMenuBarItem.click() - - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist.") - } - - func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] - XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") - optionsButton.click() - - let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] - XCTAssertTrue(findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe.") - findInPageMoreOptionsMenuItem.click() - - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist.") - } - - func test_findInPage_canBeClosedWithEscape() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - - app.typeKey(.escape, modifierFlags: []) - - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") - } - - func test_findInPage_canBeClosedWithShiftCommandF() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - - app.typeKey("f", modifierFlags: [.command, .shift]) - - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") - } - - func test_findInPage_canBeClosedWithHideFindMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - - let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] - XCTAssertTrue(findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe.") - findInPageDoneMenuBarItem.click() - - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") - } - - func test_findInPage_showsCorrectNumberOfOccurences() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") - let statusFieldTextContent = statusField.value as! String - - XCTAssertEqual(statusFieldTextContent, "1 of 6") // Note: this is not a localized test element, and it should have a localization strategy. - } - - func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") - let statusFieldTextContent = statusField.value as! String - - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. - - let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() - let croppedRect = NSRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: webViewWithSelectedWordsScreenshot.image.size.width, height: webViewWithSelectedWordsScreenshot.image.size.height-80)) - let croppedImage = webViewWithSelectedWordsScreenshot.image.cropped(to: croppedRect) - } + let app = XCUIApplication() + let timeout = 0.3 + let addressBarTextField = XCUIApplication().windows.textFields["AddressBarViewController.addressBarTextField"] + let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] + let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] + + override class func setUp() { + saveLocalHTML() + } + + override class func tearDown() { + removeLocalHTML() + } + + override func setUpWithError() throws { + continueAfterFailure = false + app.launch() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) + } + + func test_findInPage_canBeOpenedWithKeyCommand() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + + app.typeKey("f", modifierFlags: .command) + + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + } + + func test_findInPage_canBeOpenedWithMenuBarItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] + XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe.") + + findInPageMenuBarItem.click() + + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist.") + } + + func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] + XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") + optionsButton.click() + + let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] + XCTAssertTrue(findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe.") + findInPageMoreOptionsMenuItem.click() + + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist.") + } + + func test_findInPage_canBeClosedWithEscape() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + + app.typeKey(.escape, modifierFlags: []) + + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") + } + + func test_findInPage_canBeClosedWithShiftCommandF() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + + app.typeKey("f", modifierFlags: [.command, .shift]) + + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") + } + + func test_findInPage_canBeClosedWithHideFindMenuItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + + let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] + XCTAssertTrue(findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe.") + findInPageDoneMenuBarItem.click() + + XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") + } + + func test_findInPage_showsCorrectNumberOfOccurences() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") + let statusFieldTextContent = statusField.value as! String + + XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + } + + func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") + let statusFieldTextContent = statusField.value as! String + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Test cannot continue because there was an unexpected number of matches for a \"Find in Page\" operation.") // Note: this is not a localized test element, and it should have a localization strategy. + + let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot().image.withNoAlphaChannel() // Screenshot of Find in Page state + XCTAssertNotNil(webViewWithSelectedWordsScreenshot, "It wasn't possible to remove the alpha channel of the screenshot image when it was expected.") + let pixelData = webViewWithSelectedWordsScreenshot!.pixels().pixelArray // Pixels of the screenshot we will check for the highlight color + let colorSpace = webViewWithSelectedWordsScreenshot!.pixels().colorSpace // We have to check in the same colorspace of the screenshot + let findHighlightColor: NSColor = .findHighlightColor // This is the color the user should see on a selected find hit + XCTAssertNotNil(findHighlightColor.usingColorSpace(colorSpace), "It wasn't possible to get the local colorspace for the UI Tests when this is expected.") + let findHighlightColorWithColorSpace = findHighlightColor.usingColorSpace(colorSpace)! // And this is the same color converted to the screenshot's colorspace, so we can compare + let findHighlightRed = UInt8(findHighlightColorWithColorSpace.redComponent * 255.999999) // .findHighlightColor color components in 0-255 values in this colorspace + let findHighlightGreen = UInt8(findHighlightColorWithColorSpace.greenComponent * 255.999999) + let findHighlightBlue = UInt8(findHighlightColorWithColorSpace.blueComponent * 255.999999) + let pixelSet = Set(pixelData) // A set of the pixels + var matchingPixels: [Pixel: Int] = [:] + if pixelSet.contains(Pixel(red: findHighlightRed, green: findHighlightGreen, blue: findHighlightBlue, alpha: 255)) { // If there is a match + pixelData.forEach { matchingPixels[$0, default: 0] += 1 } // See if it is a number of matches we believe is a highlighted area of the screenshot + } else { + XCTFail("The \"Find in Page\" selected highlight color was not detected in the page where hits are expected. If this is happening in a test where you can see the highlight on the screenshot, it is possible that the color tolerances have to be loosened for the color equality test..") + } + let expectedNumberOfMatchingPixels = 150 + XCTAssertGreaterThan(matchingPixels.count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(matchingPixels.count) matching pixels.") + } } extension FindInPageTests { - class func loremIpsumFileURL() -> URL { - let loremIpsumFileName = "lorem_ipsum.html" - XCTAssertNotNil(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, "It wasn't possible to obtain a local file URL for the sandbox Documents directory.") - let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! - let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) - return loremIpsumHTMLFileURL - } - - class func saveLocalHTML() { - let loremIpsumHTML = """ - - Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

- -

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

- -

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

- """ - let loremIpsumData = Data(loremIpsumHTML.utf8) - - do { - try loremIpsumData.write(to: loremIpsumFileURL(), options: []) - } catch { - XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") - } - } - - class func removeLocalHTML() { - do { - try FileManager.default.removeItem(at: loremIpsumFileURL()) - } catch { - XCTFail("It wasn't possible to remove the required local HTML file for the tests: \(error.localizedDescription)") - } - } + class func loremIpsumFileURL() -> URL { + let loremIpsumFileName = "lorem_ipsum.html" + XCTAssertNotNil(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, "It wasn't possible to obtain a local file URL for the sandbox Documents directory.") + let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) + return loremIpsumHTMLFileURL + } + + class func saveLocalHTML() { + let loremIpsumHTML = """ + + Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

+ +

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

+ +

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

+ """ + let loremIpsumData = Data(loremIpsumHTML.utf8) + + do { + try loremIpsumData.write(to: loremIpsumFileURL(), options: []) + } catch { + XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") + } + } + + class func removeLocalHTML() { + do { + try FileManager.default.removeItem(at: loremIpsumFileURL()) + } catch { + XCTFail("It wasn't possible to remove the required local HTML file for the tests: \(error.localizedDescription)") + } + } +} + +extension NSImage { + func pixels() -> (pixelArray: [Pixel], colorSpace: NSColorSpace) { + let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) + let bitmap = NSBitmapImageRep(cgImage: cgImage!) + let colorSpace = bitmap.colorSpace + var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! + var red, green, blue, alpha: UInt8 + var pixels: [Pixel] = [] + + for _ in 0.. NSImage? { + guard let cgImageWithPossibleAlpha = cgImage(forProposedRect: nil, context: nil, hints: nil), + let colorSpace = cgImageWithPossibleAlpha.colorSpace, + let context = CGContext(data: nil, width: cgImageWithPossibleAlpha.width, + height: cgImageWithPossibleAlpha.height, + bitsPerComponent: cgImageWithPossibleAlpha.bitsPerComponent, + bytesPerRow: cgImageWithPossibleAlpha.bytesPerRow, + space: colorSpace, + bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue) // Remove alpha + else { + return nil + } + + context.draw(cgImageWithPossibleAlpha, in: CGRect(x: 0, y: 0, width: context.width, height: context.height)) + + guard let cgImageWithoutAlpha = context.makeImage() else { + return nil + } + + return NSImage(cgImage: cgImageWithoutAlpha, size: .zero) + } +} + +struct Pixel: Hashable { + var red: UInt8 + var green: UInt8 + var blue: UInt8 + var alpha: UInt8 } From cf26a1a3d429683a08cefd0f90d7006e0c1fbe44 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:53:16 +0100 Subject: [PATCH 018/108] Add two more accessibility identifiers to menus --- DuckDuckGo/Menus/MainMenu.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index 83cad26501..8d1b6b78a6 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -208,8 +208,8 @@ import SubscriptionUI NSMenuItem(title: UserText.mainMenuEditFind) { NSMenuItem(title: UserText.findInPageMenuItem, action: #selector(MainViewController.findInPage), keyEquivalent: "f").withAccessibilityIdentifier("MainMenu.findInPage") - NSMenuItem(title: UserText.mainMenuEditFindFindNext, action: #selector(MainViewController.findInPageNext), keyEquivalent: "g") - NSMenuItem(title: UserText.mainMenuEditFindFindPrevious, action: #selector(MainViewController.findInPagePrevious), keyEquivalent: "G") + NSMenuItem(title: UserText.mainMenuEditFindFindNext, action: #selector(MainViewController.findInPageNext), keyEquivalent: "g").withAccessibilityIdentifier("MainMenu.findNext") + NSMenuItem(title: UserText.mainMenuEditFindFindPrevious, action: #selector(MainViewController.findInPagePrevious), keyEquivalent: "G").withAccessibilityIdentifier("MainMenu.findPrevious") NSMenuItem.separator() NSMenuItem(title: UserText.mainMenuEditFindHideFind, action: #selector(MainViewController.findInPageDone), keyEquivalent: "F").withAccessibilityIdentifier("MainMenu.findInPageDone") From bca6b11d9d108607023a8c203806c473f0ea8863 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:53:54 +0100 Subject: [PATCH 019/108] Tests of find next, generalize screenshot color checking --- UITests/FindInPageTests.swift | 138 +++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 18 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 7aaca122de..72c77c8398 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -131,6 +131,102 @@ class FindInPageTests: XCTestCase { XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. } + func test_findInPage_findNextGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") + let statusFieldTextContent = statusField.value as! String + + XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + + let findInPageScreenshot = loremIpsumWebView.screenshot() + + let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] + XCTAssertTrue(findNextMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe.") + + findNextMenuBarItem.click() + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + let findNextScreenshot = loremIpsumWebView.screenshot() + + XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different + let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) + let expectedNumberOfMatchingPixels = 150 + XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") + } + + func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") + let statusFieldTextContent = statusField.value as! String + + XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + + let findInPageScreenshot = loremIpsumWebView.screenshot() + + let findInPageNextButton = XCUIApplication().windows.buttons["FindInPageController.nextButton"] + XCTAssertTrue(findInPageNextButton.waitForExistence(timeout: timeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe.") + + findInPageNextButton.click() + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + let findNextScreenshot = loremIpsumWebView.screenshot() + + XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different + let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) + let expectedNumberOfMatchingPixels = 150 + XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") + } + + func test_findInPage_commandGGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") + let statusFieldTextContent = statusField.value as! String + + XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + + let findInPageScreenshot = loremIpsumWebView.screenshot() + + app.typeKey("g", modifierFlags: [.command]) + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + let findNextScreenshot = loremIpsumWebView.screenshot() + + XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different + let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) + let expectedNumberOfMatchingPixels = 150 + XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") + } + func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") @@ -144,25 +240,10 @@ class FindInPageTests: XCTestCase { let statusFieldTextContent = statusField.value as! String XCTAssertEqual(statusFieldTextContent, "1 of 4", "Test cannot continue because there was an unexpected number of matches for a \"Find in Page\" operation.") // Note: this is not a localized test element, and it should have a localization strategy. - let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot().image.withNoAlphaChannel() // Screenshot of Find in Page state - XCTAssertNotNil(webViewWithSelectedWordsScreenshot, "It wasn't possible to remove the alpha channel of the screenshot image when it was expected.") - let pixelData = webViewWithSelectedWordsScreenshot!.pixels().pixelArray // Pixels of the screenshot we will check for the highlight color - let colorSpace = webViewWithSelectedWordsScreenshot!.pixels().colorSpace // We have to check in the same colorspace of the screenshot - let findHighlightColor: NSColor = .findHighlightColor // This is the color the user should see on a selected find hit - XCTAssertNotNil(findHighlightColor.usingColorSpace(colorSpace), "It wasn't possible to get the local colorspace for the UI Tests when this is expected.") - let findHighlightColorWithColorSpace = findHighlightColor.usingColorSpace(colorSpace)! // And this is the same color converted to the screenshot's colorspace, so we can compare - let findHighlightRed = UInt8(findHighlightColorWithColorSpace.redComponent * 255.999999) // .findHighlightColor color components in 0-255 values in this colorspace - let findHighlightGreen = UInt8(findHighlightColorWithColorSpace.greenComponent * 255.999999) - let findHighlightBlue = UInt8(findHighlightColorWithColorSpace.blueComponent * 255.999999) - let pixelSet = Set(pixelData) // A set of the pixels - var matchingPixels: [Pixel: Int] = [:] - if pixelSet.contains(Pixel(red: findHighlightRed, green: findHighlightGreen, blue: findHighlightBlue, alpha: 255)) { // If there is a match - pixelData.forEach { matchingPixels[$0, default: 0] += 1 } // See if it is a number of matches we believe is a highlighted area of the screenshot - } else { - XCTFail("The \"Find in Page\" selected highlight color was not detected in the page where hits are expected. If this is happening in a test where you can see the highlight on the screenshot, it is possible that the color tolerances have to be loosened for the color equality test..") - } + let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() + let count = webViewWithSelectedWordsScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) let expectedNumberOfMatchingPixels = 150 - XCTAssertGreaterThan(matchingPixels.count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(matchingPixels.count) matching pixels.") + XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") } } @@ -208,6 +289,27 @@ extension FindInPageTests { } extension NSImage { + + func numberOfMatchingPixels(of colorToMatch: NSColor) -> Int { + let imageNoAlpha = self.withNoAlphaChannel() // We remove the alpha, since this is for screenshot comparisons + XCTAssertNotNil(imageNoAlpha, "It wasn't possible to remove the alpha channel of the image when it was expected.") + let pixelData = imageNoAlpha!.pixels().pixelArray // Pixels of the image we will check for the requested color + let colorSpace = imageNoAlpha!.pixels().colorSpace // We have to check in the same colorspace of the image + XCTAssertNotNil(colorToMatch.usingColorSpace(colorSpace), "It wasn't possible to get the local colorspace for the UI Tests when this is expected.") + let colorToMatchWithColorSpace = colorToMatch.usingColorSpace(colorSpace)! // And this is the same color converted to the image's colorspace, so we can compare + let colorToMatchRed = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace + let colorToMatchGreen = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) + let colorToMatchBlue = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) + let pixelSet = Set(pixelData) // A set of the pixels so we do our first check for existence of the color without enumerating a very large array + var matchingPixels: [Pixel: Int] = [:] + if pixelSet.contains(Pixel(red: colorToMatchRed, green: colorToMatchGreen, blue: colorToMatchBlue, alpha: 255)) { // If there is a match in the set + pixelData.forEach { matchingPixels[$0, default: 0] += 1 } // Get all the matches in the array + } else { + return 0 + } + return matchingPixels.count + } + func pixels() -> (pixelArray: [Pixel], colorSpace: NSColorSpace) { let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) let bitmap = NSBitmapImageRep(cgImage: cgImage!) From d25a6fb771cdc61dae571248fb5f09e60d09d60c Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:00:11 +0100 Subject: [PATCH 020/108] Test cleanup --- UITests/FindInPageTests.swift | 57 ++++++++++++++--------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 72c77c8398..715bfd56c6 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -115,7 +115,7 @@ class FindInPageTests: XCTestCase { XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") } - func test_findInPage_showsCorrectNumberOfOccurences() throws { + func test_findInPage_showsCorrectNumberOfOccurrences() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") @@ -131,23 +131,38 @@ class FindInPageTests: XCTestCase { XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. } - func test_findInPage_findNextGoesToNextOccurrence() throws { + func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") let statusFieldTextContent = statusField.value as! String + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Test cannot continue because there was an unexpected number of matches for a \"Find in Page\" operation.") // Note: this is not a localized test element, and it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() + let count = webViewWithSelectedWordsScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) + let expectedNumberOfMatchingPixels = 150 + XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") + } + func test_findInPage_findNextGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") + let statusFieldTextContent = statusField.value as! String + XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. let findInPageScreenshot = loremIpsumWebView.screenshot() - let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] XCTAssertTrue(findNextMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe.") @@ -155,9 +170,9 @@ class FindInPageTests: XCTestCase { let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. let findNextScreenshot = loremIpsumWebView.screenshot() - XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) let expectedNumberOfMatchingPixels = 150 @@ -170,17 +185,13 @@ class FindInPageTests: XCTestCase { XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") let statusFieldTextContent = statusField.value as! String - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. - let findInPageScreenshot = loremIpsumWebView.screenshot() - let findInPageNextButton = XCUIApplication().windows.buttons["FindInPageController.nextButton"] XCTAssertTrue(findInPageNextButton.waitForExistence(timeout: timeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe.") @@ -188,9 +199,9 @@ class FindInPageTests: XCTestCase { let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. let findNextScreenshot = loremIpsumWebView.screenshot() - XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) let expectedNumberOfMatchingPixels = 150 @@ -203,48 +214,26 @@ class FindInPageTests: XCTestCase { XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") app.typeKey("f", modifierFlags: .command) XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") let statusFieldTextContent = statusField.value as! String - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. - let findInPageScreenshot = loremIpsumWebView.screenshot() app.typeKey("g", modifierFlags: [.command]) let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. let findNextScreenshot = loremIpsumWebView.screenshot() - XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) let expectedNumberOfMatchingPixels = 150 XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") } - - func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") - let statusFieldTextContent = statusField.value as! String - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Test cannot continue because there was an unexpected number of matches for a \"Find in Page\" operation.") // Note: this is not a localized test element, and it should have a localization strategy. - - let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() - let count = webViewWithSelectedWordsScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) - let expectedNumberOfMatchingPixels = 150 - XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") - } } extension FindInPageTests { From 0554d06158bc0cc4d48e87f039f31bfe0a6dde30 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:00:22 +0100 Subject: [PATCH 021/108] Formatting cleanup --- DuckDuckGo/FindInPage/FindInPageViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/FindInPage/FindInPageViewController.swift b/DuckDuckGo/FindInPage/FindInPageViewController.swift index 7189f1da78..142d9c1f18 100644 --- a/DuckDuckGo/FindInPage/FindInPageViewController.swift +++ b/DuckDuckGo/FindInPage/FindInPageViewController.swift @@ -59,7 +59,7 @@ final class FindInPageViewController: NSViewController { closeButton.toolTip = UserText.findInPageCloseTooltip nextButton.toolTip = UserText.findInPageNextTooltip previousButton.toolTip = UserText.findInPagePreviousTooltip - + nextButton.setAccessibilityIdentifier("FindInPageController.nextButton") closeButton.setAccessibilityIdentifier("FindInPageController.closeButton") previousButton.setAccessibilityIdentifier("FindInPageController.previousButton") From 3ca9266bf9c9be4090b5889cb29f27437b77f1e7 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:36:12 +0100 Subject: [PATCH 022/108] Test cleanup --- UITests/FindInPageTests.swift | 353 +++++++++++++++++++++++----------- 1 file changed, 246 insertions(+), 107 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 715bfd56c6..d3a1b49810 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -24,6 +24,7 @@ class FindInPageTests: XCTestCase { let addressBarTextField = XCUIApplication().windows.textFields["AddressBarViewController.addressBarTextField"] let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] + let minimumExpectedMatchingPixelsInFindHighlight = 150 override class func setUp() { saveLocalHTML() @@ -43,84 +44,145 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeOpenedWithKeyCommand() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) } func test_findInPage_canBeOpenedWithMenuBarItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] - XCTAssertTrue(findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe.") + XCTAssertTrue( + findInPageMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." + ) findInPageMenuBarItem.click() - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." + ) } func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") optionsButton.click() let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] - XCTAssertTrue(findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe.") + XCTAssertTrue( + findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), + "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." + ) findInPageMoreOptionsMenuItem.click() - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." + ) } func test_findInPage_canBeClosedWithEscape() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) app.typeKey(.escape, modifierFlags: []) - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) } func test_findInPage_canBeClosedWithShiftCommandF() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) app.typeKey("f", modifierFlags: [.command, .shift]) - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) } func test_findInPage_canBeClosedWithHideFindMenuItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] - XCTAssertTrue(findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe.") + XCTAssertTrue( + findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." + ) findInPageDoneMenuBarItem.click() - XCTAssertTrue(findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist.") + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) } func test_findInPage_showsCorrectNumberOfOccurrences() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] @@ -134,125 +196,207 @@ class FindInPageTests: XCTestCase { func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") let statusFieldTextContent = statusField.value as! String - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Test cannot continue because there was an unexpected number of matches for a \"Find in Page\" operation.") // Note: this is not a localized test element, and it should have a localization strategy. + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() - let count = webViewWithSelectedWordsScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) - let expectedNumberOfMatchingPixels = 150 - XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") + let highlightedPixelsInScreenshot = webViewWithSelectedWordsScreenshot.image.matchingPixels(of: .findHighlightColor) + XCTAssertGreaterThan( + highlightedPixelsInScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInScreenshot) matching pixels." + ) } func test_findInPage_findNextGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") let statusFieldTextContent = statusField.value as! String - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let findInPageScreenshot = loremIpsumWebView.screenshot() - let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] - XCTAssertTrue(findNextMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe.") + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot + let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] + XCTAssertTrue( + findNextMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." + ) findNextMenuBarItem.click() let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") - - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") let findNextScreenshot = loremIpsumWebView.screenshot() - XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different - let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) - let expectedNumberOfMatchingPixels = 150 - XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") + let highlightedPixelsInFindNextScreenshot = Set(findNextScreenshot.image + .matchingPixels(of: .findHighlightColor)) // Coordinates of highlighted pixels in the find next screenshot + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match for a \"Find next\" operation, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) } func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") let statusFieldTextContent = statusField.value as! String - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let findInPageScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot let findInPageNextButton = XCUIApplication().windows.buttons["FindInPageController.nextButton"] - XCTAssertTrue(findInPageNextButton.waitForExistence(timeout: timeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe.") + XCTAssertTrue( + findInPageNextButton.waitForExistence(timeout: timeout), + "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." + ) findInPageNextButton.click() let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") - - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") let findNextScreenshot = loremIpsumWebView.screenshot() - XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different - let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) - let expectedNumberOfMatchingPixels = 150 - XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") + let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) } func test_findInPage_commandGGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") - XCTAssertTrue(loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe.") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) app.typeKey("f", modifierFlags: .command) - XCTAssertTrue(findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist.") + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") let statusFieldTextContent = statusField.value as! String - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let findInPageScreenshot = loremIpsumWebView.screenshot() - + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot app.typeKey("g", modifierFlags: [.command]) let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue(updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") let findNextScreenshot = loremIpsumWebView.screenshot() - XCTAssertNotEqual(findInPageScreenshot.pngRepresentation, findNextScreenshot.pngRepresentation) // A screenshot of the find results and the find next results should be different - let count = findNextScreenshot.image.numberOfMatchingPixels(of: .findHighlightColor) - let expectedNumberOfMatchingPixels = 150 - XCTAssertGreaterThan(count, expectedNumberOfMatchingPixels, "Although the highlight color was detected, there are expected to be more than \(expectedNumberOfMatchingPixels) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, and the page text and background is black, and this test only found \(count) matching pixels.") + let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) } } +/// Helpers for the Find in Page tests extension FindInPageTests { + /// A shared URL to reference the local HTML file class func loremIpsumFileURL() -> URL { let loremIpsumFileName = "lorem_ipsum.html" - XCTAssertNotNil(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, "It wasn't possible to obtain a local file URL for the sandbox Documents directory.") + XCTAssertNotNil( + FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, + "It wasn't possible to obtain a local file URL for the sandbox Documents directory." + ) let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) return loremIpsumHTMLFileURL } + /// Save a local HTML file for testing find behavor against class func saveLocalHTML() { let loremIpsumHTML = """ - + Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

@@ -268,6 +412,7 @@ extension FindInPageTests { } } + /// Remove it when done class func removeLocalHTML() { do { try FileManager.default.removeItem(at: loremIpsumFileURL()) @@ -278,37 +423,44 @@ extension FindInPageTests { } extension NSImage { - - func numberOfMatchingPixels(of colorToMatch: NSColor) -> Int { - let imageNoAlpha = self.withNoAlphaChannel() // We remove the alpha, since this is for screenshot comparisons - XCTAssertNotNil(imageNoAlpha, "It wasn't possible to remove the alpha channel of the image when it was expected.") - let pixelData = imageNoAlpha!.pixels().pixelArray // Pixels of the image we will check for the requested color - let colorSpace = imageNoAlpha!.pixels().colorSpace // We have to check in the same colorspace of the image - XCTAssertNotNil(colorToMatch.usingColorSpace(colorSpace), "It wasn't possible to get the local colorspace for the UI Tests when this is expected.") - let colorToMatchWithColorSpace = colorToMatch.usingColorSpace(colorSpace)! // And this is the same color converted to the image's colorspace, so we can compare + /// Find matching pixels in an NSImage for a specific NSColor + /// - Parameter colorToMatch: the NSColor to match + /// - Returns: An array of Pixel structs + func matchingPixels(of colorToMatch: NSColor) -> [Pixel] { + let pixelArray = self.pixels().pixelArray // Pixels of the image we will check for the requested color + let colorSpace = self.pixels().colorSpace // We have to check in the same colorspace of the image + XCTAssertNotNil( + colorToMatch.usingColorSpace(colorSpace), + "It wasn't possible to get the local colorspace for the UI Tests when this is expected." + ) + let colorToMatchWithColorSpace = colorToMatch + .usingColorSpace(colorSpace)! // And this is the same color converted to the image's colorspace, so we can compare let colorToMatchRed = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace let colorToMatchGreen = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) let colorToMatchBlue = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) - let pixelSet = Set(pixelData) // A set of the pixels so we do our first check for existence of the color without enumerating a very large array - var matchingPixels: [Pixel: Int] = [:] - if pixelSet.contains(Pixel(red: colorToMatchRed, green: colorToMatchGreen, blue: colorToMatchBlue, alpha: 255)) { // If there is a match in the set - pixelData.forEach { matchingPixels[$0, default: 0] += 1 } // Get all the matches in the array - } else { - return 0 + var matchingPixels = [Pixel]() + for pixel in pixelArray { + if pixel.red == colorToMatchRed, pixel.green == colorToMatchGreen, pixel.blue == colorToMatchBlue { + matchingPixels.append(pixel) + } } - return matchingPixels.count + return matchingPixels } + /// The pixels in an NSSpace with their colorspace + /// - Returns: a tuple consisting of the array of Pixel structs in the NSImage and the NSColorSpace their colors are in func pixels() -> (pixelArray: [Pixel], colorSpace: NSColorSpace) { let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) + XCTAssertNotNil(cgImage, "It wasn't possible to obtain the CGImage of the NSImage.") let bitmap = NSBitmapImageRep(cgImage: cgImage!) let colorSpace = bitmap.colorSpace + XCTAssertNotNil(bitmap.bitmapData, "It wasn't possible to obtain the bitmapData of the bitmap.") var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! var red, green, blue, alpha: UInt8 var pixels: [Pixel] = [] - for _ in 0.. NSImage? { - guard let cgImageWithPossibleAlpha = cgImage(forProposedRect: nil, context: nil, hints: nil), - let colorSpace = cgImageWithPossibleAlpha.colorSpace, - let context = CGContext(data: nil, width: cgImageWithPossibleAlpha.width, - height: cgImageWithPossibleAlpha.height, - bitsPerComponent: cgImageWithPossibleAlpha.bitsPerComponent, - bytesPerRow: cgImageWithPossibleAlpha.bytesPerRow, - space: colorSpace, - bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue) // Remove alpha - else { - return nil - } - - context.draw(cgImageWithPossibleAlpha, in: CGRect(x: 0, y: 0, width: context.width, height: context.height)) - - guard let cgImageWithoutAlpha = context.makeImage() else { - return nil - } - - return NSImage(cgImage: cgImageWithoutAlpha, size: .zero) - } } +/// A struct of pixel color and coordinate values in 0-255 color values struct Pixel: Hashable { var red: UInt8 var green: UInt8 var blue: UInt8 var alpha: UInt8 + var point: CGPoint +} + +extension CGPoint: Hashable { + /// So we can do set operations with sets of CGPoints + public func hash(into hasher: inout Hasher) { + hasher.combine(x) + hasher.combine(y) + } } From 95c065175142281cb63988c2a9078e290324450d Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:51:16 +0100 Subject: [PATCH 023/108] Fix line spaces --- UITests/FindInPageTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index d3a1b49810..6e130adbc5 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -177,7 +177,7 @@ class FindInPageTests: XCTestCase { loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) - + app.typeKey("f", modifierFlags: .command) XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), @@ -205,7 +205,7 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") From e1ba0122dd0a52becaf9ebb132da1ed37e2aad84 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 18 Mar 2024 13:46:38 +0100 Subject: [PATCH 024/108] Add tests --- DuckDuckGo.xcodeproj/project.pbxproj | 4 +++ UITests/BrowsingHistoryTests.swift | 43 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 UITests/BrowsingHistoryTests.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 814167a8c8..51427b5c7a 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3122,6 +3122,7 @@ EE0629762B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; EE2F9C5B2B90F2FF00D45FC9 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = EE2F9C5A2B90F2FF00D45FC9 /* Subscription */; }; EE339228291BDEFD009F62C1 /* JSAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE339227291BDEFD009F62C1 /* JSAlertController.swift */; }; + EE430B692BA85C230050ACEC /* BrowsingHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE430B682BA85C230050ACEC /* BrowsingHistoryTests.swift */; }; EE66666F2B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; EE6666702B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; EE6666712B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; @@ -4502,6 +4503,7 @@ EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManagerExtension.swift; sourceTree = ""; }; EE339227291BDEFD009F62C1 /* JSAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSAlertController.swift; sourceTree = ""; }; + EE430B682BA85C230050ACEC /* BrowsingHistoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowsingHistoryTests.swift; sourceTree = ""; }; EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationsHostingViewController.swift; sourceTree = ""; }; EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNLocationViewModel.swift; sourceTree = ""; }; @@ -6231,6 +6233,7 @@ children = ( EEBCE6802BA444FA00B9DF00 /* Common */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, + EE430B682BA85C230050ACEC /* BrowsingHistoryTests.swift */, EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, ); path = UITests; @@ -11698,6 +11701,7 @@ EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */, EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, + EE430B692BA85C230050ACEC /* BrowsingHistoryTests.swift in Sources */, EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */, EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */, ); diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift new file mode 100644 index 0000000000..5fe4cecf90 --- /dev/null +++ b/UITests/BrowsingHistoryTests.swift @@ -0,0 +1,43 @@ +// +// BrowsingHistoryTests.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +class BrowsingHistoryTests: XCTestCase { + let app = XCUIApplication() + let timeout = 0.3 + + override class func setUp() { + + } + + override class func tearDown() { + + } + + override func setUpWithError() throws { + continueAfterFailure = false + app.launch() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) + } + + func test_failingTest() throws { + XCTFail("should always fail.") + } +} From b971c9c4c07cbce565dcc75e99a864ba7c775eb1 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 18 Mar 2024 13:46:55 +0100 Subject: [PATCH 025/108] Skip failing test with some info about required fixes --- UITests/TabBarTests.swift | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/UITests/TabBarTests.swift b/UITests/TabBarTests.swift index f59ef6991c..378ad56703 100644 --- a/UITests/TabBarTests.swift +++ b/UITests/TabBarTests.swift @@ -19,24 +19,24 @@ import XCTest class TabBarTests: XCTestCase { - - override func setUpWithError() throws { - continueAfterFailure = false - XCUIApplication().launch() - } - - func testWhenClickingAddTab_ThenTabsOpen() throws { - let app = XCUIApplication() - - let tabbarviewitemElementsQuery = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem") - // click on add tab button twice - tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 1).children(matching: .button).element.click() - tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 2).children(matching: .button).element.click() - - let tabs = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem").children(matching: .group) - .matching(identifier: "TabBarViewItem") - - XCTAssertEqual(tabs.count, 3) - } - + override func setUpWithError() throws { + continueAfterFailure = false + XCUIApplication().launch() + } + + func testWhenClickingAddTab_ThenTabsOpen() throws { + let app = XCUIApplication() + + let tabbarviewitemElementsQuery = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem") + // click on add tab button twice + // tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 1).children(matching: .button).element.click() + // tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 2).children(matching: .button).element.click() + + // let tabs = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem").children(matching: .group) + // .matching(identifier: "TabBarViewItem") + + // XCTAssertEqual(tabs.count, 3) + // TODO: replace with working identifiers + XCTSkip("Test needs accessibility identifier debugging before usage") + } } From 42ed55e975111d9b1b6e31ff832407f9ca62b1e1 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 18 Mar 2024 14:23:10 +0100 Subject: [PATCH 026/108] Bring late fix from Find in Page --- UITests/FindInPageTests.swift | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 6e130adbc5..1eaaa431e6 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -187,9 +187,7 @@ class FindInPageTests: XCTestCase { app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") - let statusFieldTextContent = statusField.value as! String - + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. } @@ -209,8 +207,7 @@ class FindInPageTests: XCTestCase { app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") - let statusFieldTextContent = statusField.value as! String + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") @@ -238,8 +235,7 @@ class FindInPageTests: XCTestCase { app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") - let statusFieldTextContent = statusField.value as! String + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let findInPageScreenshot = loremIpsumWebView.screenshot() @@ -292,8 +288,7 @@ class FindInPageTests: XCTestCase { app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") - let statusFieldTextContent = statusField.value as! String + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let findInPageScreenshot = loremIpsumWebView.screenshot() @@ -345,8 +340,8 @@ class FindInPageTests: XCTestCase { app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - XCTAssertNotNil(statusField.value as? String, "There was no string content in the \"Find in Page\" status field when it was expected.") - let statusFieldTextContent = statusField.value as! String + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let findInPageScreenshot = loremIpsumWebView.screenshot() From 27e01c42edaeb0b18861aa06f1397c6b988efa3f Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 18 Mar 2024 18:29:34 +0100 Subject: [PATCH 027/108] Bring over fix of pixel matching from find in page branch --- UITests/FindInPageTests.swift | 52 +++++++++++++++-------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 1eaaa431e6..cbbcc28b8c 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -422,53 +422,45 @@ extension NSImage { /// - Parameter colorToMatch: the NSColor to match /// - Returns: An array of Pixel structs func matchingPixels(of colorToMatch: NSColor) -> [Pixel] { - let pixelArray = self.pixels().pixelArray // Pixels of the image we will check for the requested color - let colorSpace = self.pixels().colorSpace // We have to check in the same colorspace of the image - XCTAssertNotNil( - colorToMatch.usingColorSpace(colorSpace), - "It wasn't possible to get the local colorspace for the UI Tests when this is expected." - ) - let colorToMatchWithColorSpace = colorToMatch - .usingColorSpace(colorSpace)! // And this is the same color converted to the image's colorspace, so we can compare - let colorToMatchRed = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace - let colorToMatchGreen = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) - let colorToMatchBlue = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) - var matchingPixels = [Pixel]() - for pixel in pixelArray { - if pixel.red == colorToMatchRed, pixel.green == colorToMatchGreen, pixel.blue == colorToMatchBlue { - matchingPixels.append(pixel) - } - } - return matchingPixels - } - /// The pixels in an NSSpace with their colorspace - /// - Returns: a tuple consisting of the array of Pixel structs in the NSImage and the NSColorSpace their colors are in - func pixels() -> (pixelArray: [Pixel], colorSpace: NSColorSpace) { let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) XCTAssertNotNil(cgImage, "It wasn't possible to obtain the CGImage of the NSImage.") let bitmap = NSBitmapImageRep(cgImage: cgImage!) let colorSpace = bitmap.colorSpace + + XCTAssertNotNil( + colorToMatch.usingColorSpace(colorSpace), + "It wasn't possible to get the color to match in the local colorspace." + ) + let colorToMatchWithColorSpace = colorToMatch + .usingColorSpace(colorSpace)! // We need to compare the color we want to look for in the image after it is in the same colorspace as the image + XCTAssertNotNil(bitmap.bitmapData, "It wasn't possible to obtain the bitmapData of the bitmap.") var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! - var red, green, blue, alpha: UInt8 + var redInImage, greenInImage, blueInImage, alphaInImage: UInt8 + + let redToMatch = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace + let greenToMatch = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) + let blueToMatch = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) + var pixels: [Pixel] = [] for yPoint in 0 ..< bitmap.pixelsHigh { for xPoint in 0 ..< bitmap.pixelsWide { - red = bitmapData.pointee + redInImage = bitmapData.pointee bitmapData = bitmapData.advanced(by: 1) - green = bitmapData.pointee + greenInImage = bitmapData.pointee bitmapData = bitmapData.advanced(by: 1) - blue = bitmapData.pointee + blueInImage = bitmapData.pointee bitmapData = bitmapData.advanced(by: 1) - alpha = bitmapData.pointee + alphaInImage = bitmapData.pointee bitmapData = bitmapData.advanced(by: 1) - pixels.append(Pixel(red: red, green: green, blue: blue, alpha: alpha, point: CGPoint(x: xPoint, y: yPoint))) + if redInImage == redToMatch, greenInImage == greenToMatch, blueInImage == blueToMatch { // We aren't matching alpha + pixels.append(Pixel(red: redInImage, green: greenInImage, blue: blueInImage, alpha: alphaInImage, point: CGPoint(x: xPoint, y: yPoint))) + } } } - - return (pixelArray: pixels, colorSpace: colorSpace) + return pixels } } From 3a7dded2155cf8d97b6227f34443734eb2a11fd0 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 10:11:14 +0100 Subject: [PATCH 028/108] Improvements to find in page tests from parallel branch --- UITests/FindInPageTests.swift | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index cbbcc28b8c..cc52abd52d 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -43,7 +43,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeOpenedWithKeyCommand() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -59,7 +59,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeOpenedWithMenuBarItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -80,7 +80,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -104,7 +104,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeClosedWithEscape() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -125,7 +125,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeClosedWithShiftCommandF() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -146,7 +146,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_canBeClosedWithHideFindMenuItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -172,7 +172,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_showsCorrectNumberOfOccurrences() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -193,7 +193,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -222,7 +222,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_findNextGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -275,7 +275,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -327,7 +327,7 @@ class FindInPageTests: XCTestCase { func test_findInPage_commandGGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(FindInPageTests.loremIpsumFileURL().absoluteString)\r") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." @@ -377,7 +377,7 @@ class FindInPageTests: XCTestCase { /// Helpers for the Find in Page tests extension FindInPageTests { /// A shared URL to reference the local HTML file - class func loremIpsumFileURL() -> URL { + class var loremIpsumFileURL: URL { let loremIpsumFileName = "lorem_ipsum.html" XCTAssertNotNil( FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, @@ -401,7 +401,7 @@ extension FindInPageTests { let loremIpsumData = Data(loremIpsumHTML.utf8) do { - try loremIpsumData.write(to: loremIpsumFileURL(), options: []) + try loremIpsumData.write(to: loremIpsumFileURL, options: []) } catch { XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") } @@ -410,7 +410,7 @@ extension FindInPageTests { /// Remove it when done class func removeLocalHTML() { do { - try FileManager.default.removeItem(at: loremIpsumFileURL()) + try FileManager.default.removeItem(at: loremIpsumFileURL) } catch { XCTFail("It wasn't possible to remove the required local HTML file for the tests: \(error.localizedDescription)") } From 4cb3242053848a8f82676d70e98b2c8834a3f176 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:57:42 +0100 Subject: [PATCH 029/108] Fixes for tests-server on independent scheme --- DuckDuckGo.xcodeproj/project.pbxproj | 27 +++++++++++++++++++ .../xcshareddata/xcschemes/UI Tests.xcscheme | 27 +++++++++++++++++++ .../Common/TestsURLExtension.swift | 3 +-- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 51427b5c7a..bb9638d393 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3122,6 +3122,10 @@ EE0629762B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; EE2F9C5B2B90F2FF00D45FC9 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = EE2F9C5A2B90F2FF00D45FC9 /* Subscription */; }; EE339228291BDEFD009F62C1 /* JSAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE339227291BDEFD009F62C1 /* JSAlertController.swift */; }; + EE3986342BA9A3D800CF98D6 /* TestsURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EC37FB29B83E99001ACE79 /* TestsURLExtension.swift */; }; + EE3986392BA9A61B00CF98D6 /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = EE3986382BA9A61B00CF98D6 /* BrowserServicesKit */; }; + EE39863A2BA9A6ED00CF98D6 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8EDF2624923EC70071C2E8 /* StringExtension.swift */; }; + EE424BF72BA89B0F004CC7AA /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AC3AF625D5DBFD00C7D2AA /* DataExtension.swift */; }; EE430B692BA85C230050ACEC /* BrowsingHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE430B682BA85C230050ACEC /* BrowsingHistoryTests.swift */; }; EE66666F2B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; EE6666702B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; @@ -3282,6 +3286,13 @@ remoteGlobalIDString = B6EC37E729B5DA2A001ACE79; remoteInfo = "tests-server"; }; + EE63BF802BA88681000C6325 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = AA585D76248FD31100E9A3E2 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B6EC37E729B5DA2A001ACE79; + remoteInfo = "tests-server"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -4710,6 +4721,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + EE3986392BA9A61B00CF98D6 /* BrowserServicesKit in Frameworks */, B64E42872B908501006C1346 /* SnapshotTesting in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -8808,11 +8820,13 @@ buildRules = ( ); dependencies = ( + EE63BF812BA88681000C6325 /* PBXTargetDependency */, B6F997BD2B8F35EF00476735 /* PBXTargetDependency */, ); name = "UI Tests"; packageProductDependencies = ( B65CD8D22B316E1700A595BB /* SnapshotTesting */, + EE3986382BA9A61B00CF98D6 /* BrowserServicesKit */, ); productName = "UI Tests"; productReference = 7B4CE8DA26F02108009134B1 /* UI Tests.xctest */; @@ -11702,6 +11716,9 @@ EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, EE430B692BA85C230050ACEC /* BrowsingHistoryTests.swift in Sources */, + EE424BF72BA89B0F004CC7AA /* DataExtension.swift in Sources */, + EE39863A2BA9A6ED00CF98D6 /* StringExtension.swift in Sources */, + EE3986342BA9A3D800CF98D6 /* TestsURLExtension.swift in Sources */, EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */, EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */, ); @@ -12894,6 +12911,11 @@ isa = PBXTargetDependency; productRef = B6F997C02B8F35F800476735 /* SwiftLintPlugin */; }; + EE63BF812BA88681000C6325 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B6EC37E729B5DA2A001ACE79 /* tests-server */; + targetProxy = EE63BF802BA88681000C6325 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -14464,6 +14486,11 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Subscription; }; + EE3986382BA9A61B00CF98D6 /* BrowserServicesKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = BrowserServicesKit; + }; EE7295E22A545B9A008C0991 /* NetworkProtection */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/UI Tests.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/UI Tests.xcscheme index fc51845d81..ce215006ce 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/UI Tests.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/UI Tests.xcscheme @@ -28,6 +28,33 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" shouldAutocreateTestPlan = "YES"> + + + + + + + + + + + + + + + + diff --git a/IntegrationTests/Common/TestsURLExtension.swift b/IntegrationTests/Common/TestsURLExtension.swift index da6cb33722..b6ae3eaf88 100644 --- a/IntegrationTests/Common/TestsURLExtension.swift +++ b/IntegrationTests/Common/TestsURLExtension.swift @@ -17,8 +17,7 @@ // import Foundation - -@testable import DuckDuckGo_Privacy_Browser +import Common // Integration Tests helpers extension URL { From a494a7b64ae8bae7bbed7d6f457d510b797ad7e3 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:58:36 +0100 Subject: [PATCH 030/108] Add accessibility identifiers for recently visited sites --- DuckDuckGo/Menus/HistoryMenu.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/Menus/HistoryMenu.swift b/DuckDuckGo/Menus/HistoryMenu.swift index aca38c3e10..0df5f3cbfe 100644 --- a/DuckDuckGo/Menus/HistoryMenu.swift +++ b/DuckDuckGo/Menus/HistoryMenu.swift @@ -106,6 +106,7 @@ final class HistoryMenu: NSMenu { var recentlyVisitedHeaderMenuItem: NSMenuItem { let item = NSMenuItem(title: UserText.recentlyVisitedMenuSection) item.isEnabled = false + item.setAccessibilityIdentifier("HistoryMenu.recentlyVisitedHeaderMenuItem") return item } @@ -118,8 +119,13 @@ final class HistoryMenu: NSMenu { VisitMenuItem(visitViewModel: VisitViewModel(visit: $0)) } ) - recentlyVisitedMenuItems.forEach { - addItem($0) + for index in recentlyVisitedMenuItems.indices { + let menuItem = recentlyVisitedMenuItems[index] + if index > 0 { // index zero is set in recentlyVisitedHeaderMenuItem, + // and we're zero-indexing the list of recently visited sites, so their naming in tests follows convention. + menuItem.setAccessibilityIdentifier("HistoryMenu.recentlyVisitedMenuItem.\(index - 1)") + } + addItem(menuItem) } } From ba5a0113b2ca2c20aa2980ffe178232e2ed12098 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:01:11 +0100 Subject: [PATCH 031/108] Test of whether visited site is added to recently visited --- UITests/BrowsingHistoryTests.swift | 82 ++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 5fe4cecf90..9071c05414 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -1,43 +1,73 @@ // -// BrowsingHistoryTests.swift +// BrowsingHistoryTests.swift // -// Copyright © 2024 DuckDuckGo. All rights reserved. +// Copyright © 2024 DuckDuckGo. All rights reserved. // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // +import Common import XCTest class BrowsingHistoryTests: XCTestCase { - let app = XCUIApplication() - let timeout = 0.3 + let app = XCUIApplication() + let timeout = 0.3 - override class func setUp() { + override class func setUp() {} - } + override class func tearDown() {} - override class func tearDown() { + override func setUpWithError() throws { + continueAfterFailure = false + app.launch() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) + } - } + func test_visitedSiteIsAddedToRecentlyVisited() throws { - override func setUpWithError() throws { - continueAfterFailure = false - app.launch() - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window - app.typeKey("n", modifierFlags: .command) - } + let historyPageTitleExpectedToBeFirstInRecentlyVisited = "First History Entry" + let url = URL.testsServer + .appendingTestParameters(data: """ + + + \(historyPageTitleExpectedToBeFirstInRecentlyVisited) + + +

Text

+ + + """.utf8data) - func test_failingTest() throws { - XCTFail("should always fail.") - } + let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + let historyMenuBarItem = app.menuBarItems["History"] + let firstSiteInRecentlyVisitedSection = app.menuItems["HistoryMenu.recentlyVisitedMenuItem.0"] + + addressBarTextField.typeText("\(url.absoluteString)\r") + XCTAssertTrue( + XCUIApplication().windows.webViews["\(historyPageTitleExpectedToBeFirstInRecentlyVisited)"].waitForExistence(timeout: timeout), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + XCTAssertTrue( + historyMenuBarItem.waitForExistence(timeout: timeout), + "History menu bar item didn't appear in a reasonable timeframe." + ) + historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. + XCTAssertTrue( + firstSiteInRecentlyVisitedSection.waitForExistence(timeout: timeout), + "The first site in the recently visited section didn't appear in a reasonable timeframe." + ) + + XCTAssertEqual(historyPageTitleExpectedToBeFirstInRecentlyVisited, firstSiteInRecentlyVisitedSection.title) + } } From c4fc7f432f3f8819b892bbaeeadeb7976c26c8e8 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:53:45 +0100 Subject: [PATCH 032/108] Set clear history accessibility identifier --- DuckDuckGo/Menus/HistoryMenu.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/Menus/HistoryMenu.swift b/DuckDuckGo/Menus/HistoryMenu.swift index 0df5f3cbfe..dbc449f28e 100644 --- a/DuckDuckGo/Menus/HistoryMenu.swift +++ b/DuckDuckGo/Menus/HistoryMenu.swift @@ -31,9 +31,13 @@ final class HistoryMenu: NSMenu { private let reopenLastClosedMenuItem = NSMenuItem(title: UserText.reopenLastClosedTab, action: #selector(AppDelegate.reopenLastClosedTab)) private let reopenAllWindowsFromLastSessionMenuItem = NSMenuItem(title: UserText.mainMenuHistoryReopenAllWindowsFromLastSession, action: #selector(AppDelegate.reopenAllWindowsFromLastSession)) - private let clearAllHistoryMenuItem = NSMenuItem(title: UserText.mainMenuHistoryClearAllHistory, - action: #selector(MainViewController.clearAllHistory), - keyEquivalent: [.command, .shift, .backspace]) + private let clearAllHistoryMenuItem: NSMenuItem = { + let item = NSMenuItem(title: UserText.mainMenuHistoryClearAllHistory, + action: #selector(MainViewController.clearAllHistory), + keyEquivalent: [.command, .shift, .backspace]) + item.setAccessibilityIdentifier("HistoryMenu.clearAllHistory") + return item + }() private let clearAllHistorySeparator = NSMenuItem.separator() private let historyCoordinator: HistoryCoordinating From f705a9dbeeccacac9cab10dcea8e33104b235324 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:54:10 +0100 Subject: [PATCH 033/108] Set clear all history alert buttons accessibility identifiers --- DuckDuckGo/Common/Extensions/NSAlertExtension.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/Common/Extensions/NSAlertExtension.swift b/DuckDuckGo/Common/Extensions/NSAlertExtension.swift index feafa950f9..f92e373c22 100644 --- a/DuckDuckGo/Common/Extensions/NSAlertExtension.swift +++ b/DuckDuckGo/Common/Extensions/NSAlertExtension.swift @@ -38,8 +38,11 @@ extension NSAlert { alert.informativeText = UserText.clearAllDataDescription alert.alertStyle = .warning alert.icon = .burnAlert - alert.addButton(withTitle: UserText.clear) - alert.addButton(withTitle: UserText.cancel) + + let clearButton = alert.addButton(withTitle: UserText.clear) + let cancelButton = alert.addButton(withTitle: UserText.cancel) + clearButton.setAccessibilityIdentifier("ClearAllHistoryAndDataAlert.clearButton") + cancelButton.setAccessibilityIdentifier("ClearAllHistoryAndDataAlert.cancelButton") return alert } From fd121f486ddb654a114a6f9dace9d6d195e8e348 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 16:01:11 +0100 Subject: [PATCH 034/108] WIP commit showing fatal error --- UITests/BrowsingHistoryTests.swift | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 9071c05414..d311467486 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -22,7 +22,9 @@ import XCTest class BrowsingHistoryTests: XCTestCase { let app = XCUIApplication() let timeout = 0.3 - + let historyMenuBarItem = XCUIApplication().menuBarItems["History"] + let clearAllHistory = XCUIApplication().menuItems["HistoryMenu.clearAllHistory"] + override class func setUp() {} override class func tearDown() {} @@ -32,10 +34,28 @@ class BrowsingHistoryTests: XCTestCase { app.launch() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window app.typeKey("n", modifierFlags: .command) + + XCTAssertTrue( + historyMenuBarItem.waitForExistence(timeout: timeout), + "History menu bar item didn't appear in a reasonable timeframe." + ) + historyMenuBarItem.click() + + XCTAssertTrue( + clearAllHistory.waitForExistence(timeout: timeout), + "Clear all history item didn't appear in a reasonable timeframe." + ) + clearAllHistory.click() + + XCTAssertTrue( + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: timeout), + "Clear all history item didn't appear in a reasonable timeframe." + ) + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() } func test_visitedSiteIsAddedToRecentlyVisited() throws { - +// DuckDuckGo/macos-browser/DuckDuckGo/Menus/MainMenuActions.swift:279: Fatal error: Could not get currently active Tab let historyPageTitleExpectedToBeFirstInRecentlyVisited = "First History Entry" let url = URL.testsServer .appendingTestParameters(data: """ @@ -50,7 +70,6 @@ class BrowsingHistoryTests: XCTestCase { """.utf8data) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] - let historyMenuBarItem = app.menuBarItems["History"] let firstSiteInRecentlyVisitedSection = app.menuItems["HistoryMenu.recentlyVisitedMenuItem.0"] addressBarTextField.typeText("\(url.absoluteString)\r") @@ -63,6 +82,7 @@ class BrowsingHistoryTests: XCTestCase { "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. + XCTAssertTrue( firstSiteInRecentlyVisitedSection.waitForExistence(timeout: timeout), "The first site in the recently visited section didn't appear in a reasonable timeframe." From aee805bd8736c741df84c53e841743e8769e941c Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 16:01:41 +0100 Subject: [PATCH 035/108] WIP fatal error more cleanup --- UITests/BrowsingHistoryTests.swift | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index d311467486..e846e9531d 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -24,10 +24,6 @@ class BrowsingHistoryTests: XCTestCase { let timeout = 0.3 let historyMenuBarItem = XCUIApplication().menuBarItems["History"] let clearAllHistory = XCUIApplication().menuItems["HistoryMenu.clearAllHistory"] - - override class func setUp() {} - - override class func tearDown() {} override func setUpWithError() throws { continueAfterFailure = false @@ -51,11 +47,13 @@ class BrowsingHistoryTests: XCTestCase { app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: timeout), "Clear all history item didn't appear in a reasonable timeframe." ) - app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And remove the history + + // DuckDuckGo/macos-browser/DuckDuckGo/Menus/MainMenuActions.swift:279: Fatal error: Could not get currently active Tab } func test_visitedSiteIsAddedToRecentlyVisited() throws { -// DuckDuckGo/macos-browser/DuckDuckGo/Menus/MainMenuActions.swift:279: Fatal error: Could not get currently active Tab + let historyPageTitleExpectedToBeFirstInRecentlyVisited = "First History Entry" let url = URL.testsServer .appendingTestParameters(data: """ @@ -72,9 +70,14 @@ class BrowsingHistoryTests: XCTestCase { let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInRecentlyVisitedSection = app.menuItems["HistoryMenu.recentlyVisitedMenuItem.0"] + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: timeout), + "The address bar text field didn't become available in a reasonable timeframe." + ) + addressBarTextField.typeText("\(url.absoluteString)\r") XCTAssertTrue( - XCUIApplication().windows.webViews["\(historyPageTitleExpectedToBeFirstInRecentlyVisited)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(historyPageTitleExpectedToBeFirstInRecentlyVisited)"].waitForExistence(timeout: timeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) XCTAssertTrue( From b42661de0630a2bf7f841308b6e2e6ebb05cfd3b Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 17:45:35 +0100 Subject: [PATCH 036/108] Fix for validateMenuItem error during burning --- DuckDuckGo/Fire/View/FireViewController.swift | 2 +- DuckDuckGo/Menus/MainMenuActions.swift | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/Fire/View/FireViewController.swift b/DuckDuckGo/Fire/View/FireViewController.swift index a107578fef..a2c5474e94 100644 --- a/DuckDuckGo/Fire/View/FireViewController.swift +++ b/DuckDuckGo/Fire/View/FireViewController.swift @@ -27,7 +27,7 @@ final class FireViewController: NSViewController { static let animationName = "01_Fire_really_small" } - private var fireViewModel: FireViewModel + private(set) var fireViewModel: FireViewModel private let tabCollectionViewModel: TabCollectionViewModel private var cancellables = Set() diff --git a/DuckDuckGo/Menus/MainMenuActions.swift b/DuckDuckGo/Menus/MainMenuActions.swift index 3a926dbbcc..c06e00fd6a 100644 --- a/DuckDuckGo/Menus/MainMenuActions.swift +++ b/DuckDuckGo/Menus/MainMenuActions.swift @@ -880,6 +880,9 @@ extension MainViewController: NSMenuItemValidation { // swiftlint:disable cyclomatic_complexity // swiftlint:disable function_body_length func validateMenuItem(_ menuItem: NSMenuItem) -> Bool { + guard fireViewController.fireViewModel.fire.burningData == nil else { + return true + } switch menuItem.action { // Back/Forward case #selector(MainViewController.back(_:)): From f746aeb52c3a642c5f77884a65af41dbc30ded55 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 19 Mar 2024 18:37:24 +0100 Subject: [PATCH 037/108] All passing tests --- DuckDuckGo/Menus/HistoryMenu.swift | 9 ++ UITests/BrowsingHistoryTests.swift | 127 ++++++++++++++++++++++++++++- 2 files changed, 132 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/Menus/HistoryMenu.swift b/DuckDuckGo/Menus/HistoryMenu.swift index dbc449f28e..337fb2dad7 100644 --- a/DuckDuckGo/Menus/HistoryMenu.swift +++ b/DuckDuckGo/Menus/HistoryMenu.swift @@ -60,6 +60,7 @@ final class HistoryMenu: NSMenu { } reopenMenuItemKeyEquivalentManager.reopenLastClosedMenuItem = reopenLastClosedMenuItem + reopenAllWindowsFromLastSessionMenuItem.setAccessibilityIdentifier("HistoryMenu.reopenAllWindowsFromLastSessionMenuItem") reopenMenuItemKeyEquivalentManager.lastSessionMenuItem = reopenAllWindowsFromLastSessionMenuItem } @@ -96,6 +97,7 @@ final class HistoryMenu: NSMenu { default: reopenLastClosedMenuItem.title = UserText.reopenLastClosedTab } + reopenLastClosedMenuItem.setAccessibilityIdentifier("HistoryMenu.reopenLastClosedWindowOrTab") } @@ -179,6 +181,13 @@ final class HistoryMenu: NSMenu { let dateString = isToday ? nil : title.1 let subMenuItems = makeClearThisHistoryMenuItems(with: dateString) + makeMenuItems(from: grouping) let submenu = NSMenu(items: subMenuItems) + subMenuItems.indices.forEach { index in + if index > 1 { + // The first two indices are the clear all item and the separator, so we zero index the + // accessibilityIdentifier to match expected convention in the UI test. + subMenuItems[index].setAccessibilityIdentifier("HistoryMenu.historyMenuItem.\(isToday ? "Today" : title.1).\(index - 2)") + } + } menuItem.submenu = submenu return menuItem } diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index e846e9531d..bbebf872e8 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -48,13 +48,10 @@ class BrowsingHistoryTests: XCTestCase { "Clear all history item didn't appear in a reasonable timeframe." ) app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And remove the history - - // DuckDuckGo/macos-browser/DuckDuckGo/Menus/MainMenuActions.swift:279: Fatal error: Could not get currently active Tab } func test_visitedSiteIsAddedToRecentlyVisited() throws { - - let historyPageTitleExpectedToBeFirstInRecentlyVisited = "First History Entry" + let historyPageTitleExpectedToBeFirstInRecentlyVisited = "Title 1" let url = URL.testsServer .appendingTestParameters(data: """ @@ -93,4 +90,126 @@ class BrowsingHistoryTests: XCTestCase { XCTAssertEqual(historyPageTitleExpectedToBeFirstInRecentlyVisited, firstSiteInRecentlyVisitedSection.title) } + + func test_visitedSiteIsInHistoryAfterClosingAndReopeningWindow() throws { + let historyPageTitleExpectedToBeFirstInTodayHistory = "Title 2" + let url = URL.testsServer + .appendingTestParameters(data: """ + + + \(historyPageTitleExpectedToBeFirstInTodayHistory) + + +

Text

+ + + """.utf8data) + + let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + let firstSiteInHistory = app.menuItems["HistoryMenu.historyMenuItem.Today.0"] + + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: timeout), + "The address bar text field didn't become available in a reasonable timeframe." + ) + + addressBarTextField.typeText("\(url.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews["\(historyPageTitleExpectedToBeFirstInTodayHistory)"].waitForExistence(timeout: timeout), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) + + XCTAssertTrue( + historyMenuBarItem.waitForExistence(timeout: timeout), + "History menu bar item didn't appear in a reasonable timeframe." + ) + historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. + + XCTAssertTrue( + firstSiteInHistory.waitForExistence(timeout: timeout), + "The first site in the recently visited section didn't appear in a reasonable timeframe." + ) + + XCTAssertEqual(historyPageTitleExpectedToBeFirstInTodayHistory, firstSiteInHistory.title) + } + + func test_previousSessionTabsCanBeReopenedViaHistory() throws { + let titleOfFirstTabWhichShouldRestore = UUID().uuidString.prefix(4) + let titleOfSecondTabWhichShouldRestore = UUID().uuidString.prefix(4) + + let urlForFirstTab = URL.testsServer + .appendingTestParameters(data: """ + + + \(titleOfFirstTabWhichShouldRestore) + + +

Text

+ + + """.utf8data) + + let urlForSecondTab = URL.testsServer + .appendingTestParameters(data: """ + + + \(titleOfSecondTabWhichShouldRestore) + + +

Text

+ + + """.utf8data) + + let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: timeout), + "The address bar text field didn't become available in a reasonable timeframe." + ) + + addressBarTextField.typeText("\(urlForFirstTab.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + + app.typeKey("t", modifierFlags: .command) + + addressBarTextField.typeText("\(urlForSecondTab.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + + let reopenLastClosedWindowOrTab = app.menuItems["HistoryMenu.reopenLastClosedWindowOrTab"] + + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + + XCTAssertTrue( + historyMenuBarItem.waitForExistence(timeout: timeout), + "History menu bar item didn't appear in a reasonable timeframe." + ) + historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. + let reopenAllWindowsFromLastSessionMenuItem = app.menuItems["HistoryMenu.reopenAllWindowsFromLastSessionMenuItem"] + XCTAssertTrue( + reopenLastClosedWindowOrTab.waitForExistence(timeout: timeout), + "The first site in the recently visited section didn't appear in a reasonable timeframe." + ) + reopenLastClosedWindowOrTab.click() + + XCTAssertTrue( + app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + "Restored visited site wasn't available with the expected title in a reasonable timeframe." + ) + app.typeKey("w", modifierFlags: [.command]) + XCTAssertTrue( + app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + "Restored visited site wasn't available with the expected title in a reasonable timeframe." + ) + } } From 906ff8d721fe73810b5ae444e723835efc8cdc15 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:22:02 +0100 Subject: [PATCH 038/108] Clean up accessibility identifier assignment --- DuckDuckGo/Menus/HistoryMenu.swift | 32 +++++++++++++----------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/DuckDuckGo/Menus/HistoryMenu.swift b/DuckDuckGo/Menus/HistoryMenu.swift index 337fb2dad7..4e51a5afc2 100644 --- a/DuckDuckGo/Menus/HistoryMenu.swift +++ b/DuckDuckGo/Menus/HistoryMenu.swift @@ -120,18 +120,15 @@ final class HistoryMenu: NSMenu { private func addRecentlyVisited() { recentlyVisitedMenuItems = [recentlyVisitedHeaderMenuItem] - recentlyVisitedMenuItems.append(contentsOf: historyCoordinator.getRecentVisits(maxCount: 14) + let recentVisits = historyCoordinator.getRecentVisits(maxCount: 14) + recentlyVisitedMenuItems.append(contentsOf: recentVisits .map { VisitMenuItem(visitViewModel: VisitViewModel(visit: $0)) - } - ) - for index in recentlyVisitedMenuItems.indices { - let menuItem = recentlyVisitedMenuItems[index] - if index > 0 { // index zero is set in recentlyVisitedHeaderMenuItem, - // and we're zero-indexing the list of recently visited sites, so their naming in tests follows convention. - menuItem.setAccessibilityIdentifier("HistoryMenu.recentlyVisitedMenuItem.\(index - 1)") - } - addItem(menuItem) + // An accessibility identifier with a negative index will cause the relevant UI tests to fail, correctly + .withAccessibilityIdentifier("HistoryMenu.recentlyVisitedMenuItem.\(recentVisits.firstIndex(of: $0) ?? -1)") + }) + for recentlyVisitedMenuItem in recentlyVisitedMenuItems { + addItem(recentlyVisitedMenuItem) } } @@ -181,13 +178,6 @@ final class HistoryMenu: NSMenu { let dateString = isToday ? nil : title.1 let subMenuItems = makeClearThisHistoryMenuItems(with: dateString) + makeMenuItems(from: grouping) let submenu = NSMenu(items: subMenuItems) - subMenuItems.indices.forEach { index in - if index > 1 { - // The first two indices are the clear all item and the separator, so we zero index the - // accessibilityIdentifier to match expected convention in the UI test. - subMenuItems[index].setAccessibilityIdentifier("HistoryMenu.historyMenuItem.\(isToday ? "Today" : title.1).\(index - 2)") - } - } menuItem.submenu = submenu return menuItem } @@ -208,8 +198,14 @@ final class HistoryMenu: NSMenu { } private func makeMenuItems(from grouping: HistoryGrouping) -> [NSMenuItem] { - return grouping.visits.map { visit in + let date = grouping.date + let isToday = NSCalendar.current.isDateInToday(date) + let visits = grouping.visits + return visits.map { visit in VisitMenuItem(visitViewModel: VisitViewModel(visit: visit)) + .withAccessibilityIdentifier( // An identifier with a negative index will cause the relevant UI tests to fail, correctly + "HistoryMenu.historyMenuItem.\(isToday ? "Today" : "\(date)").\(visits.firstIndex(of: visit) ?? -1)" + ) } } From 59abc79c0d8acf8ec32bda75c49d8726f21cc994 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:31:55 +0100 Subject: [PATCH 039/108] Fix bad pbxproj merge --- DuckDuckGo.xcodeproj/project.pbxproj | 55 +--------------------------- 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index b17c03ff61..736042bf3c 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3216,7 +3216,6 @@ EAC80DE0271F6C0100BBF02D /* fb-sdk.js in Resources */ = {isa = PBXBuildFile; fileRef = EAC80DDF271F6C0100BBF02D /* fb-sdk.js */; }; EAE42800275D47FA00DAC26B /* ClickToLoadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE427FF275D47FA00DAC26B /* ClickToLoadModel.swift */; }; EAFAD6CA2728BD1200F9DF00 /* clickToLoad.js in Resources */ = {isa = PBXBuildFile; fileRef = EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */; }; - EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */; }; EE0629722B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; EE0629732B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; EE0629742B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; @@ -3229,11 +3228,6 @@ EE66418C2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */; }; EE66418D2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */; }; EE66418F2B9B1BD1005BCD17 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = EE66418E2B9B1BD1005BCD17 /* Subscription */; }; - EE3986342BA9A3D800CF98D6 /* TestsURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EC37FB29B83E99001ACE79 /* TestsURLExtension.swift */; }; - EE3986392BA9A61B00CF98D6 /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = EE3986382BA9A61B00CF98D6 /* BrowserServicesKit */; }; - EE39863A2BA9A6ED00CF98D6 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8EDF2624923EC70071C2E8 /* StringExtension.swift */; }; - EE424BF72BA89B0F004CC7AA /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AC3AF625D5DBFD00C7D2AA /* DataExtension.swift */; }; - EE430B692BA85C230050ACEC /* BrowsingHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE430B682BA85C230050ACEC /* BrowsingHistoryTests.swift */; }; EE66666F2B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; EE6666702B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; EE6666712B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; @@ -3245,9 +3239,6 @@ EEA3EEB12B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; EEA3EEB32B24EC0600E8333A /* VPNLocationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */; }; EEAD7A7C2A1D3E20002A24E7 /* AppLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */; }; - EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */; }; - EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B139AFC26B60BD800894F82 /* NSImageExtensions.swift */; }; - EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC5E4E325D6BA9C007F5990 /* NSSizeExtension.swift */; }; EEC111E4294D06020086524F /* JSAlert.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEC111E3294D06020086524F /* JSAlert.storyboard */; }; EEC111E6294D06290086524F /* JSAlertViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC111E5294D06290086524F /* JSAlertViewModel.swift */; }; EEC4A65E2B277E8D00F7C0AA /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; @@ -3395,13 +3386,6 @@ remoteGlobalIDString = B6EC37E729B5DA2A001ACE79; remoteInfo = "tests-server"; }; - EE63BF802BA88681000C6325 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = AA585D76248FD31100E9A3E2 /* Project object */; - proxyType = 1; - remoteGlobalIDString = B6EC37E729B5DA2A001ACE79; - remoteInfo = "tests-server"; - }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -4657,17 +4641,14 @@ EAC80DDF271F6C0100BBF02D /* fb-sdk.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "fb-sdk.js"; sourceTree = ""; }; EAE427FF275D47FA00DAC26B /* ClickToLoadModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClickToLoadModel.swift; sourceTree = ""; }; EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = clickToLoad.js; sourceTree = ""; }; - EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManagerExtension.swift; sourceTree = ""; }; EE339227291BDEFD009F62C1 /* JSAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSAlertController.swift; sourceTree = ""; }; EE34245D2BA0853900173B1B /* VPNUninstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNUninstaller.swift; sourceTree = ""; }; EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift"; sourceTree = ""; }; - EE430B682BA85C230050ACEC /* BrowsingHistoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowsingHistoryTests.swift; sourceTree = ""; }; EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationsHostingViewController.swift; sourceTree = ""; }; EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNLocationViewModel.swift; sourceTree = ""; }; EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLauncher.swift; sourceTree = ""; }; - EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCUIElementExtension.swift; sourceTree = ""; }; EEC111E3294D06020086524F /* JSAlert.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = JSAlert.storyboard; sourceTree = ""; }; EEC111E5294D06290086524F /* JSAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSAlertViewModel.swift; sourceTree = ""; }; EEC4A6682B2C87D300F7C0AA /* VPNLocationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationView.swift; sourceTree = ""; }; @@ -4876,7 +4857,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EE3986392BA9A61B00CF98D6 /* BrowserServicesKit in Frameworks */, B64E42872B908501006C1346 /* SnapshotTesting in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6419,10 +6399,7 @@ 7B4CE8DB26F02108009134B1 /* UITests */ = { isa = PBXGroup; children = ( - EEBCE6802BA444FA00B9DF00 /* Common */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, - EE430B682BA85C230050ACEC /* BrowsingHistoryTests.swift */, - EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, ); path = UITests; sourceTree = ""; @@ -8647,14 +8624,6 @@ path = JSAlert; sourceTree = ""; }; - EEBCE6802BA444FA00B9DF00 /* Common */ = { - isa = PBXGroup; - children = ( - EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */, - ); - path = Common; - sourceTree = ""; - }; EEC589D62A4F1B1F00BCD60C /* AppAndExtensionAndAgentTargets */ = { isa = PBXGroup; children = ( @@ -9054,13 +9023,11 @@ buildRules = ( ); dependencies = ( - EE63BF812BA88681000C6325 /* PBXTargetDependency */, B6F997BD2B8F35EF00476735 /* PBXTargetDependency */, ); name = "UI Tests"; packageProductDependencies = ( B65CD8D22B316E1700A595BB /* SnapshotTesting */, - EE3986382BA9A61B00CF98D6 /* BrowserServicesKit */, ); productName = "UI Tests"; productReference = 7B4CE8DA26F02108009134B1 /* UI Tests.xctest */; @@ -12014,15 +11981,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */, - EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, - EE430B692BA85C230050ACEC /* BrowsingHistoryTests.swift in Sources */, - EE424BF72BA89B0F004CC7AA /* DataExtension.swift in Sources */, - EE39863A2BA9A6ED00CF98D6 /* StringExtension.swift in Sources */, - EE3986342BA9A3D800CF98D6 /* TestsURLExtension.swift in Sources */, - EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */, - EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -13249,11 +13208,6 @@ isa = PBXTargetDependency; productRef = B6F997C02B8F35F800476735 /* SwiftLintPlugin */; }; - EE63BF812BA88681000C6325 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = B6EC37E729B5DA2A001ACE79 /* tests-server */; - targetProxy = EE63BF802BA88681000C6325 /* PBXContainerItemProxy */; - }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -14857,12 +14811,7 @@ isa = XCSwiftPackageProductDependency; productName = Subscription; }; - EE3986382BA9A61B00CF98D6 /* BrowserServicesKit */ = { - isa = XCSwiftPackageProductDependency; - package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; - productName = BrowserServicesKit; - }; - E EE7295E22A545B9A008C0991 /* NetworkProtection */ = { + EE7295E22A545B9A008C0991 /* NetworkProtection */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = NetworkProtection; @@ -15015,4 +14964,4 @@ /* End XCVersionGroup section */ }; rootObject = AA585D76248FD31100E9A3E2 /* Project object */; -} +} \ No newline at end of file From f7ce14f4fb2a79677e88f083906b326867f85095 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:43:20 +0100 Subject: [PATCH 040/108] Remove warnings from tab bar tests --- UITests/TabBarTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/UITests/TabBarTests.swift b/UITests/TabBarTests.swift index 378ad56703..c869663d89 100644 --- a/UITests/TabBarTests.swift +++ b/UITests/TabBarTests.swift @@ -25,9 +25,9 @@ class TabBarTests: XCTestCase { } func testWhenClickingAddTab_ThenTabsOpen() throws { - let app = XCUIApplication() - - let tabbarviewitemElementsQuery = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem") +// let app = XCUIApplication() +// +// let tabbarviewitemElementsQuery = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem") // click on add tab button twice // tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 1).children(matching: .button).element.click() // tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 2).children(matching: .button).element.click() @@ -37,6 +37,6 @@ class TabBarTests: XCTestCase { // XCTAssertEqual(tabs.count, 3) // TODO: replace with working identifiers - XCTSkip("Test needs accessibility identifier debugging before usage") + let _ = XCTSkip("Test needs accessibility identifier debugging before usage") } } From a9a41cbdb1ae7f579d50cd980a844dab0acdc165 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:43:45 +0100 Subject: [PATCH 041/108] Restore test dependencies to pbxproj bad merge fix file --- DuckDuckGo.xcodeproj/project.pbxproj | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 736042bf3c..2b7cf5689d 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3225,6 +3225,13 @@ EE339228291BDEFD009F62C1 /* JSAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE339227291BDEFD009F62C1 /* JSAlertController.swift */; }; EE3424602BA0853900173B1B /* VPNUninstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE34245D2BA0853900173B1B /* VPNUninstaller.swift */; }; EE3424612BA0853900173B1B /* VPNUninstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE34245D2BA0853900173B1B /* VPNUninstaller.swift */; }; + EE4F2C732BAAE50E00F2DA55 /* BrowsingHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE4F2C6F2BAAE50D00F2DA55 /* BrowsingHistoryTests.swift */; }; + EE4F2C742BAAE50E00F2DA55 /* XCUIElementExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE4F2C712BAAE50E00F2DA55 /* XCUIElementExtension.swift */; }; + EE4F2C752BAAE50E00F2DA55 /* FindInPageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE4F2C722BAAE50E00F2DA55 /* FindInPageTests.swift */; }; + EE4F2C772BAAE54E00F2DA55 /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = EE4F2C762BAAE54E00F2DA55 /* BrowserServicesKit */; }; + EE4F2C7A2BAAE57400F2DA55 /* TestsURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EC37FB29B83E99001ACE79 /* TestsURLExtension.swift */; }; + EE4F2C7C2BAAE5A200F2DA55 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8EDF2624923EC70071C2E8 /* StringExtension.swift */; }; + EE4F2C7D2BAAE5A900F2DA55 /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AC3AF625D5DBFD00C7D2AA /* DataExtension.swift */; }; EE66418C2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */; }; EE66418D2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */; }; EE66418F2B9B1BD1005BCD17 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = EE66418E2B9B1BD1005BCD17 /* Subscription */; }; @@ -3386,6 +3393,13 @@ remoteGlobalIDString = B6EC37E729B5DA2A001ACE79; remoteInfo = "tests-server"; }; + EE4F2C782BAAE55600F2DA55 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = AA585D76248FD31100E9A3E2 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B6EC37E729B5DA2A001ACE79; + remoteInfo = "tests-server"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -4644,6 +4658,9 @@ EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManagerExtension.swift; sourceTree = ""; }; EE339227291BDEFD009F62C1 /* JSAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSAlertController.swift; sourceTree = ""; }; EE34245D2BA0853900173B1B /* VPNUninstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNUninstaller.swift; sourceTree = ""; }; + EE4F2C6F2BAAE50D00F2DA55 /* BrowsingHistoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowsingHistoryTests.swift; sourceTree = ""; }; + EE4F2C712BAAE50E00F2DA55 /* XCUIElementExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCUIElementExtension.swift; sourceTree = ""; }; + EE4F2C722BAAE50E00F2DA55 /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift"; sourceTree = ""; }; EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationsHostingViewController.swift; sourceTree = ""; }; EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; @@ -4857,6 +4874,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + EE4F2C772BAAE54E00F2DA55 /* BrowserServicesKit in Frameworks */, B64E42872B908501006C1346 /* SnapshotTesting in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6399,6 +6417,9 @@ 7B4CE8DB26F02108009134B1 /* UITests */ = { isa = PBXGroup; children = ( + EE4F2C702BAAE50E00F2DA55 /* Common */, + EE4F2C6F2BAAE50D00F2DA55 /* BrowsingHistoryTests.swift */, + EE4F2C722BAAE50E00F2DA55 /* FindInPageTests.swift */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, ); path = UITests; @@ -8601,6 +8622,14 @@ name = "Recovered References"; sourceTree = ""; }; + EE4F2C702BAAE50E00F2DA55 /* Common */ = { + isa = PBXGroup; + children = ( + EE4F2C712BAAE50E00F2DA55 /* XCUIElementExtension.swift */, + ); + path = Common; + sourceTree = ""; + }; EEA3EEAF2B24EB5100E8333A /* VPNLocation */ = { isa = PBXGroup; children = ( @@ -9023,11 +9052,13 @@ buildRules = ( ); dependencies = ( + EE4F2C792BAAE55600F2DA55 /* PBXTargetDependency */, B6F997BD2B8F35EF00476735 /* PBXTargetDependency */, ); name = "UI Tests"; packageProductDependencies = ( B65CD8D22B316E1700A595BB /* SnapshotTesting */, + EE4F2C762BAAE54E00F2DA55 /* BrowserServicesKit */, ); productName = "UI Tests"; productReference = 7B4CE8DA26F02108009134B1 /* UI Tests.xctest */; @@ -11981,7 +12012,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EE4F2C752BAAE50E00F2DA55 /* FindInPageTests.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, + EE4F2C7C2BAAE5A200F2DA55 /* StringExtension.swift in Sources */, + EE4F2C732BAAE50E00F2DA55 /* BrowsingHistoryTests.swift in Sources */, + EE4F2C7D2BAAE5A900F2DA55 /* DataExtension.swift in Sources */, + EE4F2C7A2BAAE57400F2DA55 /* TestsURLExtension.swift in Sources */, + EE4F2C742BAAE50E00F2DA55 /* XCUIElementExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -13208,6 +13245,11 @@ isa = PBXTargetDependency; productRef = B6F997C02B8F35F800476735 /* SwiftLintPlugin */; }; + EE4F2C792BAAE55600F2DA55 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B6EC37E729B5DA2A001ACE79 /* tests-server */; + targetProxy = EE4F2C782BAAE55600F2DA55 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -14807,6 +14849,11 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Subscription; }; + EE4F2C762BAAE54E00F2DA55 /* BrowserServicesKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = BrowserServicesKit; + }; EE66418E2B9B1BD1005BCD17 /* Subscription */ = { isa = XCSwiftPackageProductDependency; productName = Subscription; From 91b155d5587bd357f9cfe84d769479655817f1cb Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:23:56 +0100 Subject: [PATCH 042/108] Improvement to accessibility identifier for window and tab restoration --- DuckDuckGo/Menus/HistoryMenu.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/Menus/HistoryMenu.swift b/DuckDuckGo/Menus/HistoryMenu.swift index 4e51a5afc2..0a091ab6a4 100644 --- a/DuckDuckGo/Menus/HistoryMenu.swift +++ b/DuckDuckGo/Menus/HistoryMenu.swift @@ -94,10 +94,11 @@ final class HistoryMenu: NSMenu { switch RecentlyClosedCoordinator.shared.cache.last { case is RecentlyClosedWindow: reopenLastClosedMenuItem.title = UserText.reopenLastClosedWindow + reopenLastClosedMenuItem.setAccessibilityIdentifier("HistoryMenu.reopenLastClosedWindow") default: reopenLastClosedMenuItem.title = UserText.reopenLastClosedTab + reopenLastClosedMenuItem.setAccessibilityIdentifier("HistoryMenu.reopenLastClosedTab") } - reopenLastClosedMenuItem.setAccessibilityIdentifier("HistoryMenu.reopenLastClosedWindowOrTab") } From 4abac8b8fd0527ea905c718d13812be8fecfdaf8 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:24:12 +0100 Subject: [PATCH 043/108] Simplify and clarify tests --- UITests/BrowsingHistoryTests.swift | 120 ++++++++++------------------- 1 file changed, 42 insertions(+), 78 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index bbebf872e8..2bfa72f068 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -24,6 +24,7 @@ class BrowsingHistoryTests: XCTestCase { let timeout = 0.3 let historyMenuBarItem = XCUIApplication().menuBarItems["History"] let clearAllHistory = XCUIApplication().menuItems["HistoryMenu.clearAllHistory"] + let lengthForRandomPageTitle = 8 override func setUpWithError() throws { continueAfterFailure = false @@ -47,26 +48,14 @@ class BrowsingHistoryTests: XCTestCase { app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: timeout), "Clear all history item didn't appear in a reasonable timeframe." ) - app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And remove the history + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And manually remove the history } func test_visitedSiteIsAddedToRecentlyVisited() throws { - let historyPageTitleExpectedToBeFirstInRecentlyVisited = "Title 1" - let url = URL.testsServer - .appendingTestParameters(data: """ - - - \(historyPageTitleExpectedToBeFirstInRecentlyVisited) - - -

Text

- - - """.utf8data) - + let historyPageTitleExpectedToBeFirstInRecentlyVisited = randomPageTitle(length: lengthForRandomPageTitle) + let url = locallyServedURL(pageTitle: historyPageTitleExpectedToBeFirstInRecentlyVisited) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInRecentlyVisitedSection = app.menuItems["HistoryMenu.recentlyVisitedMenuItem.0"] - XCTAssertTrue( addressBarTextField.waitForExistence(timeout: timeout), "The address bar text field didn't become available in a reasonable timeframe." @@ -82,7 +71,6 @@ class BrowsingHistoryTests: XCTestCase { "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. - XCTAssertTrue( firstSiteInRecentlyVisitedSection.waitForExistence(timeout: timeout), "The first site in the recently visited section didn't appear in a reasonable timeframe." @@ -92,22 +80,10 @@ class BrowsingHistoryTests: XCTestCase { } func test_visitedSiteIsInHistoryAfterClosingAndReopeningWindow() throws { - let historyPageTitleExpectedToBeFirstInTodayHistory = "Title 2" - let url = URL.testsServer - .appendingTestParameters(data: """ - - - \(historyPageTitleExpectedToBeFirstInTodayHistory) - - -

Text

- - - """.utf8data) - + let historyPageTitleExpectedToBeFirstInTodayHistory = randomPageTitle(length: lengthForRandomPageTitle) + let url = locallyServedURL(pageTitle: historyPageTitleExpectedToBeFirstInTodayHistory) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInHistory = app.menuItems["HistoryMenu.historyMenuItem.Today.0"] - XCTAssertTrue( addressBarTextField.waitForExistence(timeout: timeout), "The address bar text field didn't become available in a reasonable timeframe." @@ -118,16 +94,13 @@ class BrowsingHistoryTests: XCTestCase { app.windows.webViews["\(historyPageTitleExpectedToBeFirstInTodayHistory)"].waitForExistence(timeout: timeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) - - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window - app.typeKey("n", modifierFlags: .command) - + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close all windows + app.typeKey("n", modifierFlags: .command) // New window XCTAssertTrue( historyMenuBarItem.waitForExistence(timeout: timeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. - XCTAssertTrue( firstSiteInHistory.waitForExistence(timeout: timeout), "The first site in the recently visited section didn't appear in a reasonable timeframe." @@ -136,47 +109,21 @@ class BrowsingHistoryTests: XCTestCase { XCTAssertEqual(historyPageTitleExpectedToBeFirstInTodayHistory, firstSiteInHistory.title) } - func test_previousSessionTabsCanBeReopenedViaHistory() throws { - let titleOfFirstTabWhichShouldRestore = UUID().uuidString.prefix(4) - let titleOfSecondTabWhichShouldRestore = UUID().uuidString.prefix(4) - - let urlForFirstTab = URL.testsServer - .appendingTestParameters(data: """ - - - \(titleOfFirstTabWhichShouldRestore) - - -

Text

- - - """.utf8data) - - let urlForSecondTab = URL.testsServer - .appendingTestParameters(data: """ - - - \(titleOfSecondTabWhichShouldRestore) - - -

Text

- - - """.utf8data) - + func test_lastClosedWindowsTabsCanBeReopenedViaHistory() throws { + let titleOfFirstTabWhichShouldRestore = randomPageTitle(length: lengthForRandomPageTitle) + let titleOfSecondTabWhichShouldRestore = randomPageTitle(length: lengthForRandomPageTitle) + let urlForFirstTab = locallyServedURL(pageTitle: titleOfFirstTabWhichShouldRestore) + let urlForSecondTab = locallyServedURL(pageTitle: titleOfSecondTabWhichShouldRestore) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] - XCTAssertTrue( addressBarTextField.waitForExistence(timeout: timeout), "The address bar text field didn't become available in a reasonable timeframe." ) - addressBarTextField.typeText("\(urlForFirstTab.absoluteString)\r") XCTAssertTrue( app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: timeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) - app.typeKey("t", modifierFlags: .command) addressBarTextField.typeText("\(urlForSecondTab.absoluteString)\r") @@ -184,32 +131,49 @@ class BrowsingHistoryTests: XCTestCase { app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: timeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) - - let reopenLastClosedWindowOrTab = app.menuItems["HistoryMenu.reopenLastClosedWindowOrTab"] - - app.typeKey("w", modifierFlags: [.command, .option, .shift]) - app.typeKey("n", modifierFlags: .command) - + let reopenLastClosedWindowMenuItem = app.menuItems["HistoryMenu.reopenLastClosedWindow"] + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close all windows + app.typeKey("n", modifierFlags: .command) // New window XCTAssertTrue( historyMenuBarItem.waitForExistence(timeout: timeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. - let reopenAllWindowsFromLastSessionMenuItem = app.menuItems["HistoryMenu.reopenAllWindowsFromLastSessionMenuItem"] XCTAssertTrue( - reopenLastClosedWindowOrTab.waitForExistence(timeout: timeout), - "The first site in the recently visited section didn't appear in a reasonable timeframe." + reopenLastClosedWindowMenuItem.waitForExistence(timeout: timeout), + "The \"Reopen Last Closed Window\" menu item didn't appear in a reasonable timeframe." ) - reopenLastClosedWindowOrTab.click() + reopenLastClosedWindowMenuItem.click() XCTAssertTrue( app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: timeout), - "Restored visited site wasn't available with the expected title in a reasonable timeframe." + "Restored visited tab 1 wasn't available with the expected title in a reasonable timeframe." ) app.typeKey("w", modifierFlags: [.command]) XCTAssertTrue( app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: timeout), - "Restored visited site wasn't available with the expected title in a reasonable timeframe." + "Restored visited tab 2 wasn't available with the expected title in a reasonable timeframe." ) } } + +extension BrowsingHistoryTests { + + func randomPageTitle(length: Int) -> String { + return String(UUID().uuidString.prefix(length)) + } + + func locallyServedURL(pageTitle: String) -> URL { + return URL.testsServer + .appendingTestParameters(data: """ + + + \(pageTitle) + + +

Sample text

+ + + """.utf8data) + } +} From 508cebbce166403a8fa0e9183dc2e783adc38b0e Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:36:42 +0100 Subject: [PATCH 044/108] Linter warning fixes --- .../Common/Extensions/NSAlertExtension.swift | 1 - UITests/FindInPageTests.swift | 22 ++++++------ UITests/TabBarTests.swift | 35 +++++++++---------- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/DuckDuckGo/Common/Extensions/NSAlertExtension.swift b/DuckDuckGo/Common/Extensions/NSAlertExtension.swift index f92e373c22..554ce896e5 100644 --- a/DuckDuckGo/Common/Extensions/NSAlertExtension.swift +++ b/DuckDuckGo/Common/Extensions/NSAlertExtension.swift @@ -38,7 +38,6 @@ extension NSAlert { alert.informativeText = UserText.clearAllDataDescription alert.alertStyle = .warning alert.icon = .burnAlert - let clearButton = alert.addButton(withTitle: UserText.clear) let cancelButton = alert.addButton(withTitle: UserText.cancel) clearButton.setAccessibilityIdentifier("ClearAllHistoryAndDataAlert.clearButton") diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index cc52abd52d..660e68aeab 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -1,19 +1,19 @@ // -// FindInPageTests.swift +// FindInPageTests.swift // -// Copyright © 2024 DuckDuckGo. All rights reserved. +// Copyright © 2024 DuckDuckGo. All rights reserved. // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // import XCTest diff --git a/UITests/TabBarTests.swift b/UITests/TabBarTests.swift index c869663d89..d1c70e925d 100644 --- a/UITests/TabBarTests.swift +++ b/UITests/TabBarTests.swift @@ -19,24 +19,23 @@ import XCTest class TabBarTests: XCTestCase { - override func setUpWithError() throws { - continueAfterFailure = false - XCUIApplication().launch() - } + override func setUpWithError() throws { + continueAfterFailure = false + XCUIApplication().launch() + } - func testWhenClickingAddTab_ThenTabsOpen() throws { -// let app = XCUIApplication() + func testWhenClickingAddTab_ThenTabsOpen() throws { +// let app = XCUIApplication() // -// let tabbarviewitemElementsQuery = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem") - // click on add tab button twice - // tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 1).children(matching: .button).element.click() - // tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 2).children(matching: .button).element.click() - - // let tabs = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem").children(matching: .group) - // .matching(identifier: "TabBarViewItem") - - // XCTAssertEqual(tabs.count, 3) - // TODO: replace with working identifiers - let _ = XCTSkip("Test needs accessibility identifier debugging before usage") - } +// let tabbarviewitemElementsQuery = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem") +// // click on add tab button twice +// tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 1).children(matching: .button).element.click() +// tabbarviewitemElementsQuery.children(matching: .group).element(boundBy: 2).children(matching: .button).element.click() +// +// let tabs = app.windows.collectionViews.otherElements.containing(.group, identifier: "TabBarViewItem").children(matching: .group) +// .matching(identifier: "TabBarViewItem") +// +// XCTAssertEqual(tabs.count, 3) + _ = XCTSkip("Test needs accessibility identifier debugging before usage") + } } From 0aa615bf7f3db91c67a06d3e8dd34641b06179f0 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:49:33 +0100 Subject: [PATCH 045/108] Add test case --- DuckDuckGo.xcodeproj/project.pbxproj | 6 ++++- UITests/AutocompleteTests.swift | 36 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 UITests/AutocompleteTests.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 2b7cf5689d..a5cb705ba7 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3243,6 +3243,7 @@ EE7295E92A545BC4008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295E82A545BC4008C0991 /* NetworkProtection */; }; EE7295ED2A545C0A008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295EC2A545C0A008C0991 /* NetworkProtection */; }; EE7295EF2A545C12008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295EE2A545C12008C0991 /* NetworkProtection */; }; + EE8BEF0E2BAAF5FB0093CB6D /* AutocompleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE8BEF0D2BAAF5FA0093CB6D /* AutocompleteTests.swift */; }; EEA3EEB12B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; EEA3EEB32B24EC0600E8333A /* VPNLocationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */; }; EEAD7A7C2A1D3E20002A24E7 /* AppLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */; }; @@ -4663,6 +4664,7 @@ EE4F2C722BAAE50E00F2DA55 /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift"; sourceTree = ""; }; EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationsHostingViewController.swift; sourceTree = ""; }; + EE8BEF0D2BAAF5FA0093CB6D /* AutocompleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutocompleteTests.swift; path = UITests/AutocompleteTests.swift; sourceTree = SOURCE_ROOT; }; EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNLocationViewModel.swift; sourceTree = ""; }; EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLauncher.swift; sourceTree = ""; }; @@ -6418,6 +6420,7 @@ isa = PBXGroup; children = ( EE4F2C702BAAE50E00F2DA55 /* Common */, + EE8BEF0D2BAAF5FA0093CB6D /* AutocompleteTests.swift */, EE4F2C6F2BAAE50D00F2DA55 /* BrowsingHistoryTests.swift */, EE4F2C722BAAE50E00F2DA55 /* FindInPageTests.swift */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, @@ -12019,6 +12022,7 @@ EE4F2C7D2BAAE5A900F2DA55 /* DataExtension.swift in Sources */, EE4F2C7A2BAAE57400F2DA55 /* TestsURLExtension.swift in Sources */, EE4F2C742BAAE50E00F2DA55 /* XCUIElementExtension.swift in Sources */, + EE8BEF0E2BAAF5FB0093CB6D /* AutocompleteTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -15011,4 +15015,4 @@ /* End XCVersionGroup section */ }; rootObject = AA585D76248FD31100E9A3E2 /* Project object */; -} \ No newline at end of file +} diff --git a/UITests/AutocompleteTests.swift b/UITests/AutocompleteTests.swift new file mode 100644 index 0000000000..8936e996a9 --- /dev/null +++ b/UITests/AutocompleteTests.swift @@ -0,0 +1,36 @@ +// +// AutocompleteTests.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Common +import XCTest + +class AutocompleteTests: XCTestCase { + let app = XCUIApplication() + let timeout = 0.3 + + override func setUpWithError() throws { + continueAfterFailure = false + app.launch() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) + } + + func test_failingTest() throws { + XCTFail("Test should always fail") + } +} From 00340e388958b855e6d703850b569c9a3b86ecec Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:29:52 +0100 Subject: [PATCH 046/108] Fix tabs in FindInPageTests --- UITests/FindInPageTests.swift | 902 +++++++++++++++++----------------- 1 file changed, 453 insertions(+), 449 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 660e68aeab..806e18c04a 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -19,464 +19,468 @@ import XCTest class FindInPageTests: XCTestCase { - let app = XCUIApplication() - let timeout = 0.3 - let addressBarTextField = XCUIApplication().windows.textFields["AddressBarViewController.addressBarTextField"] - let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] - let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] - let minimumExpectedMatchingPixelsInFindHighlight = 150 - - override class func setUp() { - saveLocalHTML() - } - - override class func tearDown() { - removeLocalHTML() - } - - override func setUpWithError() throws { - continueAfterFailure = false - app.launch() - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window - app.typeKey("n", modifierFlags: .command) - } - - func test_findInPage_canBeOpenedWithKeyCommand() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - - app.typeKey("f", modifierFlags: .command) - - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - } - - func test_findInPage_canBeOpenedWithMenuBarItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] - XCTAssertTrue( - findInPageMenuBarItem.waitForExistence(timeout: timeout), - "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." - ) - - findInPageMenuBarItem.click() - - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." - ) - } - - func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] - XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") - optionsButton.click() - - let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] - XCTAssertTrue( - findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), - "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." - ) - findInPageMoreOptionsMenuItem.click() - - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." - ) - } - - func test_findInPage_canBeClosedWithEscape() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - app.typeKey(.escape, modifierFlags: []) - - XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), - "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." - ) - } - - func test_findInPage_canBeClosedWithShiftCommandF() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - app.typeKey("f", modifierFlags: [.command, .shift]) - - XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), - "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." - ) - } - - func test_findInPage_canBeClosedWithHideFindMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] - XCTAssertTrue( - findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), - "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." - ) - findInPageDoneMenuBarItem.click() - - XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), - "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." - ) - } - - func test_findInPage_showsCorrectNumberOfOccurrences() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. - } - - func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - // Note: the following is not a localized test element, but it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - - let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInScreenshot = webViewWithSelectedWordsScreenshot.image.matchingPixels(of: .findHighlightColor) - XCTAssertGreaterThan( - highlightedPixelsInScreenshot.count, - minimumExpectedMatchingPixelsInFindHighlight, - "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInScreenshot) matching pixels." - ) - } - - func test_findInPage_findNextGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - // Note: the following is not a localized test element, but it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - let findInPageScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) - let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot - - let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] - XCTAssertTrue( - findNextMenuBarItem.waitForExistence(timeout: timeout), - "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." - ) - findNextMenuBarItem.click() - let updatedStatusField = app.textFields["FindInPageController.statusField"] - let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), - "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." - ) - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") - let findNextScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindNextScreenshot = Set(findNextScreenshot.image - .matchingPixels(of: .findHighlightColor)) // Coordinates of highlighted pixels in the find next screenshot - let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } - let pixelSetIntersection = findHighlightPoints - .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - - XCTAssertGreaterThan( - highlightedPixelsInFindNextScreenshot.count, - minimumExpectedMatchingPixelsInFindHighlight, - "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match for a \"Find next\" operation, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." - ) - XCTAssertTrue( - pixelSetIntersection.count == 0, - "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." - ) - } - - func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - // Note: the following is not a localized test element, but it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - let findInPageScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) - let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot - let findInPageNextButton = XCUIApplication().windows.buttons["FindInPageController.nextButton"] - XCTAssertTrue( - findInPageNextButton.waitForExistence(timeout: timeout), - "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." - ) - - findInPageNextButton.click() - let updatedStatusField = app.textFields["FindInPageController.statusField"] - let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), - "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." - ) - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") - let findNextScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) - let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } - let pixelSetIntersection = findHighlightPoints - .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - - XCTAssertGreaterThan( - highlightedPixelsInFindNextScreenshot.count, - minimumExpectedMatchingPixelsInFindHighlight, - "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." - ) - XCTAssertTrue( - pixelSetIntersection.count == 0, - "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." - ) - } - - func test_findInPage_commandGGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - - // Note: the following is not a localized test element, but it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - let findInPageScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) - let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot - app.typeKey("g", modifierFlags: [.command]) - let updatedStatusField = app.textFields["FindInPageController.statusField"] - let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), - "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." - ) - - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") - let findNextScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) - let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } - let pixelSetIntersection = findHighlightPoints - .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - - XCTAssertGreaterThan( - highlightedPixelsInFindNextScreenshot.count, - minimumExpectedMatchingPixelsInFindHighlight, - "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." - ) - XCTAssertTrue( - pixelSetIntersection.count == 0, - "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." - ) - } + var app: XCUIApplication! + let timeout = 0.3 + var addressBarTextField: XCUIElement! + var loremIpsumWebView: XCUIElement! + var findInPageCloseButton: XCUIElement! + let minimumExpectedMatchingPixelsInFindHighlight = 150 + + override class func setUp() { + saveLocalHTML() + } + + override class func tearDown() { + removeLocalHTML() + } + + override func setUpWithError() throws { + continueAfterFailure = false + app = XCUIApplication() + addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + loremIpsumWebView = app.windows.webViews["Lorem Ipsum"] + findInPageCloseButton = app.windows.buttons["FindInPageController.closeButton"] + app.launch() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) + } + + func test_findInPage_canBeOpenedWithKeyCommand() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + + app.typeKey("f", modifierFlags: .command) + + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + } + + func test_findInPage_canBeOpenedWithMenuBarItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] + XCTAssertTrue( + findInPageMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." + ) + + findInPageMenuBarItem.click() + + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." + ) + } + + func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] + XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") + optionsButton.click() + + let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] + XCTAssertTrue( + findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), + "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." + ) + findInPageMoreOptionsMenuItem.click() + + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." + ) + } + + func test_findInPage_canBeClosedWithEscape() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + app.typeKey(.escape, modifierFlags: []) + + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) + } + + func test_findInPage_canBeClosedWithShiftCommandF() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + app.typeKey("f", modifierFlags: [.command, .shift]) + + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) + } + + func test_findInPage_canBeClosedWithHideFindMenuItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] + XCTAssertTrue( + findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." + ) + findInPageDoneMenuBarItem.click() + + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) + } + + func test_findInPage_showsCorrectNumberOfOccurrences() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + } + + func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") + + let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInScreenshot = webViewWithSelectedWordsScreenshot.image.matchingPixels(of: .findHighlightColor) + XCTAssertGreaterThan( + highlightedPixelsInScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInScreenshot) matching pixels." + ) + } + + func test_findInPage_findNextGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") + let findInPageScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot + + let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] + XCTAssertTrue( + findNextMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." + ) + findNextMenuBarItem.click() + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") + let findNextScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindNextScreenshot = Set(findNextScreenshot.image + .matchingPixels(of: .findHighlightColor)) // Coordinates of highlighted pixels in the find next screenshot + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match for a \"Find next\" operation, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) + } + + func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") + let findInPageScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot + let findInPageNextButton = app.windows.buttons["FindInPageController.nextButton"] + XCTAssertTrue( + findInPageNextButton.waitForExistence(timeout: timeout), + "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." + ) + + findInPageNextButton.click() + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") + let findNextScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) + } + + func test_findInPage_commandGGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") + let findInPageScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot + app.typeKey("g", modifierFlags: [.command]) + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) + + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") + let findNextScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) + } } /// Helpers for the Find in Page tests -extension FindInPageTests { - /// A shared URL to reference the local HTML file - class var loremIpsumFileURL: URL { - let loremIpsumFileName = "lorem_ipsum.html" - XCTAssertNotNil( - FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, - "It wasn't possible to obtain a local file URL for the sandbox Documents directory." - ) - let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! - let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) - return loremIpsumHTMLFileURL - } - - /// Save a local HTML file for testing find behavor against - class func saveLocalHTML() { - let loremIpsumHTML = """ - - Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

- -

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

- -

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

- """ - let loremIpsumData = Data(loremIpsumHTML.utf8) - - do { - try loremIpsumData.write(to: loremIpsumFileURL, options: []) - } catch { - XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") - } - } - - /// Remove it when done - class func removeLocalHTML() { - do { - try FileManager.default.removeItem(at: loremIpsumFileURL) - } catch { - XCTFail("It wasn't possible to remove the required local HTML file for the tests: \(error.localizedDescription)") - } - } +private extension FindInPageTests { + /// A shared URL to reference the local HTML file + class var loremIpsumFileURL: URL { + let loremIpsumFileName = "lorem_ipsum.html" + XCTAssertNotNil( + FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, + "It wasn't possible to obtain a local file URL for the sandbox Documents directory." + ) + let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) + return loremIpsumHTMLFileURL + } + + /// Save a local HTML file for testing find behavor against + class func saveLocalHTML() { + let loremIpsumHTML = """ + + Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

+ +

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

+ +

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

+ """ + let loremIpsumData = Data(loremIpsumHTML.utf8) + + do { + try loremIpsumData.write(to: loremIpsumFileURL, options: []) + } catch { + XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") + } + } + + /// Remove it when done + class func removeLocalHTML() { + do { + try FileManager.default.removeItem(at: loremIpsumFileURL) + } catch { + XCTFail("It wasn't possible to remove the required local HTML file for the tests: \(error.localizedDescription)") + } + } } -extension NSImage { - /// Find matching pixels in an NSImage for a specific NSColor - /// - Parameter colorToMatch: the NSColor to match - /// - Returns: An array of Pixel structs - func matchingPixels(of colorToMatch: NSColor) -> [Pixel] { - - let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) - XCTAssertNotNil(cgImage, "It wasn't possible to obtain the CGImage of the NSImage.") - let bitmap = NSBitmapImageRep(cgImage: cgImage!) - let colorSpace = bitmap.colorSpace - - XCTAssertNotNil( - colorToMatch.usingColorSpace(colorSpace), - "It wasn't possible to get the color to match in the local colorspace." - ) - let colorToMatchWithColorSpace = colorToMatch - .usingColorSpace(colorSpace)! // We need to compare the color we want to look for in the image after it is in the same colorspace as the image - - XCTAssertNotNil(bitmap.bitmapData, "It wasn't possible to obtain the bitmapData of the bitmap.") - var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! - var redInImage, greenInImage, blueInImage, alphaInImage: UInt8 - - let redToMatch = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace - let greenToMatch = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) - let blueToMatch = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) - - var pixels: [Pixel] = [] - - for yPoint in 0 ..< bitmap.pixelsHigh { - for xPoint in 0 ..< bitmap.pixelsWide { - redInImage = bitmapData.pointee - bitmapData = bitmapData.advanced(by: 1) - greenInImage = bitmapData.pointee - bitmapData = bitmapData.advanced(by: 1) - blueInImage = bitmapData.pointee - bitmapData = bitmapData.advanced(by: 1) - alphaInImage = bitmapData.pointee - bitmapData = bitmapData.advanced(by: 1) - if redInImage == redToMatch, greenInImage == greenToMatch, blueInImage == blueToMatch { // We aren't matching alpha - pixels.append(Pixel(red: redInImage, green: greenInImage, blue: blueInImage, alpha: alphaInImage, point: CGPoint(x: xPoint, y: yPoint))) - } - } - } - return pixels - } +private extension NSImage { + /// Find matching pixels in an NSImage for a specific NSColor + /// - Parameter colorToMatch: the NSColor to match + /// - Returns: An array of Pixel structs + func matchingPixels(of colorToMatch: NSColor) -> [Pixel] { + + let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) + XCTAssertNotNil(cgImage, "It wasn't possible to obtain the CGImage of the NSImage.") + let bitmap = NSBitmapImageRep(cgImage: cgImage!) + let colorSpace = bitmap.colorSpace + + XCTAssertNotNil( + colorToMatch.usingColorSpace(colorSpace), + "It wasn't possible to get the color to match in the local colorspace." + ) + let colorToMatchWithColorSpace = colorToMatch + .usingColorSpace(colorSpace)! // We need to compare the color we want to look for in the image after it is in the same colorspace as the image + + XCTAssertNotNil(bitmap.bitmapData, "It wasn't possible to obtain the bitmapData of the bitmap.") + var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! + var redInImage, greenInImage, blueInImage, alphaInImage: UInt8 + + let redToMatch = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace + let greenToMatch = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) + let blueToMatch = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) + + var pixels: [Pixel] = [] + + for yPoint in 0 ..< bitmap.pixelsHigh { + for xPoint in 0 ..< bitmap.pixelsWide { + redInImage = bitmapData.pointee + bitmapData = bitmapData.advanced(by: 1) + greenInImage = bitmapData.pointee + bitmapData = bitmapData.advanced(by: 1) + blueInImage = bitmapData.pointee + bitmapData = bitmapData.advanced(by: 1) + alphaInImage = bitmapData.pointee + bitmapData = bitmapData.advanced(by: 1) + if redInImage == redToMatch, greenInImage == greenToMatch, blueInImage == blueToMatch { // We aren't matching alpha + pixels.append(Pixel(red: redInImage, green: greenInImage, blue: blueInImage, alpha: alphaInImage, point: CGPoint(x: xPoint, y: yPoint))) + } + } + } + return pixels + } } /// A struct of pixel color and coordinate values in 0-255 color values -struct Pixel: Hashable { - var red: UInt8 - var green: UInt8 - var blue: UInt8 - var alpha: UInt8 - var point: CGPoint +private struct Pixel: Hashable { + var red: UInt8 + var green: UInt8 + var blue: UInt8 + var alpha: UInt8 + var point: CGPoint } extension CGPoint: Hashable { - /// So we can do set operations with sets of CGPoints - public func hash(into hasher: inout Hasher) { - hasher.combine(x) - hasher.combine(y) - } + /// So we can do set operations with sets of CGPoints + public func hash(into hasher: inout Hasher) { + hasher.combine(x) + hasher.combine(y) + } } From e9f324c4d30a7a004e3aaac2d7fb8a73b1a6f552 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:30:26 +0100 Subject: [PATCH 047/108] Elements and access levels --- UITests/BrowsingHistoryTests.swift | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 2bfa72f068..1a06f2af54 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -20,14 +20,17 @@ import Common import XCTest class BrowsingHistoryTests: XCTestCase { - let app = XCUIApplication() - let timeout = 0.3 - let historyMenuBarItem = XCUIApplication().menuBarItems["History"] - let clearAllHistory = XCUIApplication().menuItems["HistoryMenu.clearAllHistory"] + var app: XCUIApplication! + var historyMenuBarItem: XCUIElement! + var clearAllHistory: XCUIElement! let lengthForRandomPageTitle = 8 + let timeout = 0.3 override func setUpWithError() throws { continueAfterFailure = false + app = XCUIApplication() + historyMenuBarItem = app.menuBarItems["History"] + clearAllHistory = app.menuItems["HistoryMenu.clearAllHistory"] app.launch() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window app.typeKey("n", modifierFlags: .command) @@ -157,7 +160,7 @@ class BrowsingHistoryTests: XCTestCase { } } -extension BrowsingHistoryTests { +private extension BrowsingHistoryTests { func randomPageTitle(length: Int) -> String { return String(UUID().uuidString.prefix(length)) From ffd104fcaa2c50f87182f946d9d0a1f9620f54fe Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:34:01 +0100 Subject: [PATCH 048/108] Sync previous test cases already in PR --- UITests/BrowsingHistoryTests.swift | 13 +- UITests/FindInPageTests.swift | 902 +++++++++++++++-------------- 2 files changed, 461 insertions(+), 454 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 2bfa72f068..1a06f2af54 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -20,14 +20,17 @@ import Common import XCTest class BrowsingHistoryTests: XCTestCase { - let app = XCUIApplication() - let timeout = 0.3 - let historyMenuBarItem = XCUIApplication().menuBarItems["History"] - let clearAllHistory = XCUIApplication().menuItems["HistoryMenu.clearAllHistory"] + var app: XCUIApplication! + var historyMenuBarItem: XCUIElement! + var clearAllHistory: XCUIElement! let lengthForRandomPageTitle = 8 + let timeout = 0.3 override func setUpWithError() throws { continueAfterFailure = false + app = XCUIApplication() + historyMenuBarItem = app.menuBarItems["History"] + clearAllHistory = app.menuItems["HistoryMenu.clearAllHistory"] app.launch() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window app.typeKey("n", modifierFlags: .command) @@ -157,7 +160,7 @@ class BrowsingHistoryTests: XCTestCase { } } -extension BrowsingHistoryTests { +private extension BrowsingHistoryTests { func randomPageTitle(length: Int) -> String { return String(UUID().uuidString.prefix(length)) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 660e68aeab..806e18c04a 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -19,464 +19,468 @@ import XCTest class FindInPageTests: XCTestCase { - let app = XCUIApplication() - let timeout = 0.3 - let addressBarTextField = XCUIApplication().windows.textFields["AddressBarViewController.addressBarTextField"] - let loremIpsumWebView = XCUIApplication().windows.webViews["Lorem Ipsum"] - let findInPageCloseButton = XCUIApplication().windows.buttons["FindInPageController.closeButton"] - let minimumExpectedMatchingPixelsInFindHighlight = 150 - - override class func setUp() { - saveLocalHTML() - } - - override class func tearDown() { - removeLocalHTML() - } - - override func setUpWithError() throws { - continueAfterFailure = false - app.launch() - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window - app.typeKey("n", modifierFlags: .command) - } - - func test_findInPage_canBeOpenedWithKeyCommand() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - - app.typeKey("f", modifierFlags: .command) - - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - } - - func test_findInPage_canBeOpenedWithMenuBarItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] - XCTAssertTrue( - findInPageMenuBarItem.waitForExistence(timeout: timeout), - "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." - ) - - findInPageMenuBarItem.click() - - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." - ) - } - - func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] - XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") - optionsButton.click() - - let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] - XCTAssertTrue( - findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), - "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." - ) - findInPageMoreOptionsMenuItem.click() - - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." - ) - } - - func test_findInPage_canBeClosedWithEscape() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - app.typeKey(.escape, modifierFlags: []) - - XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), - "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." - ) - } - - func test_findInPage_canBeClosedWithShiftCommandF() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - app.typeKey("f", modifierFlags: [.command, .shift]) - - XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), - "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." - ) - } - - func test_findInPage_canBeClosedWithHideFindMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] - XCTAssertTrue( - findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), - "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." - ) - findInPageDoneMenuBarItem.click() - - XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), - "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." - ) - } - - func test_findInPage_showsCorrectNumberOfOccurrences() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. - } - - func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - // Note: the following is not a localized test element, but it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - - let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInScreenshot = webViewWithSelectedWordsScreenshot.image.matchingPixels(of: .findHighlightColor) - XCTAssertGreaterThan( - highlightedPixelsInScreenshot.count, - minimumExpectedMatchingPixelsInFindHighlight, - "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInScreenshot) matching pixels." - ) - } - - func test_findInPage_findNextGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - // Note: the following is not a localized test element, but it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - let findInPageScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) - let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot - - let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] - XCTAssertTrue( - findNextMenuBarItem.waitForExistence(timeout: timeout), - "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." - ) - findNextMenuBarItem.click() - let updatedStatusField = app.textFields["FindInPageController.statusField"] - let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), - "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." - ) - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") - let findNextScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindNextScreenshot = Set(findNextScreenshot.image - .matchingPixels(of: .findHighlightColor)) // Coordinates of highlighted pixels in the find next screenshot - let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } - let pixelSetIntersection = findHighlightPoints - .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - - XCTAssertGreaterThan( - highlightedPixelsInFindNextScreenshot.count, - minimumExpectedMatchingPixelsInFindHighlight, - "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match for a \"Find next\" operation, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." - ) - XCTAssertTrue( - pixelSetIntersection.count == 0, - "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." - ) - } - - func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - // Note: the following is not a localized test element, but it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - let findInPageScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) - let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot - let findInPageNextButton = XCUIApplication().windows.buttons["FindInPageController.nextButton"] - XCTAssertTrue( - findInPageNextButton.waitForExistence(timeout: timeout), - "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." - ) - - findInPageNextButton.click() - let updatedStatusField = app.textFields["FindInPageController.statusField"] - let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), - "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." - ) - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") - let findNextScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) - let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } - let pixelSetIntersection = findHighlightPoints - .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - - XCTAssertGreaterThan( - highlightedPixelsInFindNextScreenshot.count, - minimumExpectedMatchingPixelsInFindHighlight, - "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." - ) - XCTAssertTrue( - pixelSetIntersection.count == 0, - "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." - ) - } - - func test_findInPage_commandGGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") - addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") - XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), - "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." - ) - app.typeKey("f", modifierFlags: .command) - XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), - "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." - ) - app.typeText("maximus\r") - let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") - let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - - // Note: the following is not a localized test element, but it should have a localization strategy. - XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - let findInPageScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) - let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot - app.typeKey("g", modifierFlags: [.command]) - let updatedStatusField = app.textFields["FindInPageController.statusField"] - let updatedStatusFieldTextContent = updatedStatusField.value as! String - XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), - "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." - ) - - XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") - let findNextScreenshot = loremIpsumWebView.screenshot() - let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) - let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } - let pixelSetIntersection = findHighlightPoints - .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - - XCTAssertGreaterThan( - highlightedPixelsInFindNextScreenshot.count, - minimumExpectedMatchingPixelsInFindHighlight, - "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." - ) - XCTAssertTrue( - pixelSetIntersection.count == 0, - "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." - ) - } + var app: XCUIApplication! + let timeout = 0.3 + var addressBarTextField: XCUIElement! + var loremIpsumWebView: XCUIElement! + var findInPageCloseButton: XCUIElement! + let minimumExpectedMatchingPixelsInFindHighlight = 150 + + override class func setUp() { + saveLocalHTML() + } + + override class func tearDown() { + removeLocalHTML() + } + + override func setUpWithError() throws { + continueAfterFailure = false + app = XCUIApplication() + addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + loremIpsumWebView = app.windows.webViews["Lorem Ipsum"] + findInPageCloseButton = app.windows.buttons["FindInPageController.closeButton"] + app.launch() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) + } + + func test_findInPage_canBeOpenedWithKeyCommand() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + + app.typeKey("f", modifierFlags: .command) + + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + } + + func test_findInPage_canBeOpenedWithMenuBarItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] + XCTAssertTrue( + findInPageMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." + ) + + findInPageMenuBarItem.click() + + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." + ) + } + + func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] + XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") + optionsButton.click() + + let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] + XCTAssertTrue( + findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), + "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." + ) + findInPageMoreOptionsMenuItem.click() + + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." + ) + } + + func test_findInPage_canBeClosedWithEscape() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + app.typeKey(.escape, modifierFlags: []) + + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) + } + + func test_findInPage_canBeClosedWithShiftCommandF() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + app.typeKey("f", modifierFlags: [.command, .shift]) + + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) + } + + func test_findInPage_canBeClosedWithHideFindMenuItem() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] + XCTAssertTrue( + findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." + ) + findInPageDoneMenuBarItem.click() + + XCTAssertTrue( + findInPageCloseButton.waitForNonExistence(timeout: timeout), + "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." + ) + } + + func test_findInPage_showsCorrectNumberOfOccurrences() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. + } + + func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") + + let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInScreenshot = webViewWithSelectedWordsScreenshot.image.matchingPixels(of: .findHighlightColor) + XCTAssertGreaterThan( + highlightedPixelsInScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInScreenshot) matching pixels." + ) + } + + func test_findInPage_findNextGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") + let findInPageScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot + + let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] + XCTAssertTrue( + findNextMenuBarItem.waitForExistence(timeout: timeout), + "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." + ) + findNextMenuBarItem.click() + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") + let findNextScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindNextScreenshot = Set(findNextScreenshot.image + .matchingPixels(of: .findHighlightColor)) // Coordinates of highlighted pixels in the find next screenshot + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match for a \"Find next\" operation, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) + } + + func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") + let findInPageScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot + let findInPageNextButton = app.windows.buttons["FindInPageController.nextButton"] + XCTAssertTrue( + findInPageNextButton.waitForExistence(timeout: timeout), + "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." + ) + + findInPageNextButton.click() + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") + let findNextScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) + } + + func test_findInPage_commandGGoesToNextOccurrence() throws { + XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") + XCTAssertTrue( + loremIpsumWebView.waitForExistence(timeout: timeout), + "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("f", modifierFlags: .command) + XCTAssertTrue( + findInPageCloseButton.waitForExistence(timeout: timeout), + "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." + ) + app.typeText("maximus\r") + let statusField = app.textFields["FindInPageController.statusField"] + XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) + + // Note: the following is not a localized test element, but it should have a localization strategy. + XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") + let findInPageScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) + let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot + app.typeKey("g", modifierFlags: [.command]) + let updatedStatusField = app.textFields["FindInPageController.statusField"] + let updatedStatusFieldTextContent = updatedStatusField.value as! String + XCTAssertTrue( + updatedStatusField.waitForExistence(timeout: timeout), + "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." + ) + + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") + let findNextScreenshot = loremIpsumWebView.screenshot() + let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) + let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } + let pixelSetIntersection = findHighlightPoints + .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements + + XCTAssertGreaterThan( + highlightedPixelsInFindNextScreenshot.count, + minimumExpectedMatchingPixelsInFindHighlight, + "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInFindNextScreenshot) matching pixels." + ) + XCTAssertTrue( + pixelSetIntersection.count == 0, + "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." + ) + } } /// Helpers for the Find in Page tests -extension FindInPageTests { - /// A shared URL to reference the local HTML file - class var loremIpsumFileURL: URL { - let loremIpsumFileName = "lorem_ipsum.html" - XCTAssertNotNil( - FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, - "It wasn't possible to obtain a local file URL for the sandbox Documents directory." - ) - let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! - let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) - return loremIpsumHTMLFileURL - } - - /// Save a local HTML file for testing find behavor against - class func saveLocalHTML() { - let loremIpsumHTML = """ - - Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

- -

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

- -

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

- """ - let loremIpsumData = Data(loremIpsumHTML.utf8) - - do { - try loremIpsumData.write(to: loremIpsumFileURL, options: []) - } catch { - XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") - } - } - - /// Remove it when done - class func removeLocalHTML() { - do { - try FileManager.default.removeItem(at: loremIpsumFileURL) - } catch { - XCTFail("It wasn't possible to remove the required local HTML file for the tests: \(error.localizedDescription)") - } - } +private extension FindInPageTests { + /// A shared URL to reference the local HTML file + class var loremIpsumFileURL: URL { + let loremIpsumFileName = "lorem_ipsum.html" + XCTAssertNotNil( + FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, + "It wasn't possible to obtain a local file URL for the sandbox Documents directory." + ) + let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! + let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) + return loremIpsumHTMLFileURL + } + + /// Save a local HTML file for testing find behavor against + class func saveLocalHTML() { + let loremIpsumHTML = """ + + Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

+ +

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

+ +

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

+ """ + let loremIpsumData = Data(loremIpsumHTML.utf8) + + do { + try loremIpsumData.write(to: loremIpsumFileURL, options: []) + } catch { + XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") + } + } + + /// Remove it when done + class func removeLocalHTML() { + do { + try FileManager.default.removeItem(at: loremIpsumFileURL) + } catch { + XCTFail("It wasn't possible to remove the required local HTML file for the tests: \(error.localizedDescription)") + } + } } -extension NSImage { - /// Find matching pixels in an NSImage for a specific NSColor - /// - Parameter colorToMatch: the NSColor to match - /// - Returns: An array of Pixel structs - func matchingPixels(of colorToMatch: NSColor) -> [Pixel] { - - let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) - XCTAssertNotNil(cgImage, "It wasn't possible to obtain the CGImage of the NSImage.") - let bitmap = NSBitmapImageRep(cgImage: cgImage!) - let colorSpace = bitmap.colorSpace - - XCTAssertNotNil( - colorToMatch.usingColorSpace(colorSpace), - "It wasn't possible to get the color to match in the local colorspace." - ) - let colorToMatchWithColorSpace = colorToMatch - .usingColorSpace(colorSpace)! // We need to compare the color we want to look for in the image after it is in the same colorspace as the image - - XCTAssertNotNil(bitmap.bitmapData, "It wasn't possible to obtain the bitmapData of the bitmap.") - var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! - var redInImage, greenInImage, blueInImage, alphaInImage: UInt8 - - let redToMatch = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace - let greenToMatch = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) - let blueToMatch = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) - - var pixels: [Pixel] = [] - - for yPoint in 0 ..< bitmap.pixelsHigh { - for xPoint in 0 ..< bitmap.pixelsWide { - redInImage = bitmapData.pointee - bitmapData = bitmapData.advanced(by: 1) - greenInImage = bitmapData.pointee - bitmapData = bitmapData.advanced(by: 1) - blueInImage = bitmapData.pointee - bitmapData = bitmapData.advanced(by: 1) - alphaInImage = bitmapData.pointee - bitmapData = bitmapData.advanced(by: 1) - if redInImage == redToMatch, greenInImage == greenToMatch, blueInImage == blueToMatch { // We aren't matching alpha - pixels.append(Pixel(red: redInImage, green: greenInImage, blue: blueInImage, alpha: alphaInImage, point: CGPoint(x: xPoint, y: yPoint))) - } - } - } - return pixels - } +private extension NSImage { + /// Find matching pixels in an NSImage for a specific NSColor + /// - Parameter colorToMatch: the NSColor to match + /// - Returns: An array of Pixel structs + func matchingPixels(of colorToMatch: NSColor) -> [Pixel] { + + let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) + XCTAssertNotNil(cgImage, "It wasn't possible to obtain the CGImage of the NSImage.") + let bitmap = NSBitmapImageRep(cgImage: cgImage!) + let colorSpace = bitmap.colorSpace + + XCTAssertNotNil( + colorToMatch.usingColorSpace(colorSpace), + "It wasn't possible to get the color to match in the local colorspace." + ) + let colorToMatchWithColorSpace = colorToMatch + .usingColorSpace(colorSpace)! // We need to compare the color we want to look for in the image after it is in the same colorspace as the image + + XCTAssertNotNil(bitmap.bitmapData, "It wasn't possible to obtain the bitmapData of the bitmap.") + var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! + var redInImage, greenInImage, blueInImage, alphaInImage: UInt8 + + let redToMatch = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace + let greenToMatch = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) + let blueToMatch = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) + + var pixels: [Pixel] = [] + + for yPoint in 0 ..< bitmap.pixelsHigh { + for xPoint in 0 ..< bitmap.pixelsWide { + redInImage = bitmapData.pointee + bitmapData = bitmapData.advanced(by: 1) + greenInImage = bitmapData.pointee + bitmapData = bitmapData.advanced(by: 1) + blueInImage = bitmapData.pointee + bitmapData = bitmapData.advanced(by: 1) + alphaInImage = bitmapData.pointee + bitmapData = bitmapData.advanced(by: 1) + if redInImage == redToMatch, greenInImage == greenToMatch, blueInImage == blueToMatch { // We aren't matching alpha + pixels.append(Pixel(red: redInImage, green: greenInImage, blue: blueInImage, alpha: alphaInImage, point: CGPoint(x: xPoint, y: yPoint))) + } + } + } + return pixels + } } /// A struct of pixel color and coordinate values in 0-255 color values -struct Pixel: Hashable { - var red: UInt8 - var green: UInt8 - var blue: UInt8 - var alpha: UInt8 - var point: CGPoint +private struct Pixel: Hashable { + var red: UInt8 + var green: UInt8 + var blue: UInt8 + var alpha: UInt8 + var point: CGPoint } extension CGPoint: Hashable { - /// So we can do set operations with sets of CGPoints - public func hash(into hasher: inout Hasher) { - hasher.combine(x) - hasher.combine(y) - } + /// So we can do set operations with sets of CGPoints + public func hash(into hasher: inout Hasher) { + hasher.combine(x) + hasher.combine(y) + } } From c519aebf83e176e65cfa5d40f557885ce4b02036 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:27:21 +0100 Subject: [PATCH 049/108] More of this file's tabs and spaces linter warning saga --- UITests/FindInPageTests.swift | 105 ++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 806e18c04a..e53a29e9c2 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -25,15 +25,15 @@ class FindInPageTests: XCTestCase { var loremIpsumWebView: XCUIElement! var findInPageCloseButton: XCUIElement! let minimumExpectedMatchingPixelsInFindHighlight = 150 - + override class func setUp() { saveLocalHTML() } - + override class func tearDown() { removeLocalHTML() } - + override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() @@ -44,7 +44,7 @@ class FindInPageTests: XCTestCase { app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window app.typeKey("n", modifierFlags: .command) } - + func test_findInPage_canBeOpenedWithKeyCommand() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -52,15 +52,15 @@ class FindInPageTests: XCTestCase { loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) - + app.typeKey("f", modifierFlags: .command) - + XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) } - + func test_findInPage_canBeOpenedWithMenuBarItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -73,15 +73,15 @@ class FindInPageTests: XCTestCase { findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." ) - + findInPageMenuBarItem.click() - + XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." ) } - + func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -92,20 +92,20 @@ class FindInPageTests: XCTestCase { let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") optionsButton.click() - + let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] XCTAssertTrue( findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." ) findInPageMoreOptionsMenuItem.click() - + XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." ) } - + func test_findInPage_canBeClosedWithEscape() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -118,15 +118,15 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeKey(.escape, modifierFlags: []) - + XCTAssertTrue( findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } - + func test_findInPage_canBeClosedWithShiftCommandF() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -139,15 +139,15 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeKey("f", modifierFlags: [.command, .shift]) - + XCTAssertTrue( findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } - + func test_findInPage_canBeClosedWithHideFindMenuItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -160,20 +160,20 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] XCTAssertTrue( findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." ) findInPageDoneMenuBarItem.click() - + XCTAssertTrue( findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } - + func test_findInPage_showsCorrectNumberOfOccurrences() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -181,20 +181,20 @@ class FindInPageTests: XCTestCase { loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) - + app.typeKey("f", modifierFlags: .command) XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. } - + func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -207,14 +207,14 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - + let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() let highlightedPixelsInScreenshot = webViewWithSelectedWordsScreenshot.image.matchingPixels(of: .findHighlightColor) XCTAssertGreaterThan( @@ -223,7 +223,7 @@ class FindInPageTests: XCTestCase { "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInScreenshot) matching pixels." ) } - + func test_findInPage_findNextGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -245,7 +245,7 @@ class FindInPageTests: XCTestCase { let findInPageScreenshot = loremIpsumWebView.screenshot() let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot - + let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] XCTAssertTrue( findNextMenuBarItem.waitForExistence(timeout: timeout), @@ -265,7 +265,7 @@ class FindInPageTests: XCTestCase { let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } let pixelSetIntersection = findHighlightPoints .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - + XCTAssertGreaterThan( highlightedPixelsInFindNextScreenshot.count, minimumExpectedMatchingPixelsInFindHighlight, @@ -276,7 +276,7 @@ class FindInPageTests: XCTestCase { "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." ) } - + func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -303,7 +303,7 @@ class FindInPageTests: XCTestCase { findInPageNextButton.waitForExistence(timeout: timeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." ) - + findInPageNextButton.click() let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String @@ -317,7 +317,7 @@ class FindInPageTests: XCTestCase { let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } let pixelSetIntersection = findHighlightPoints .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - + XCTAssertGreaterThan( highlightedPixelsInFindNextScreenshot.count, minimumExpectedMatchingPixelsInFindHighlight, @@ -328,7 +328,7 @@ class FindInPageTests: XCTestCase { "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." ) } - + func test_findInPage_commandGGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -345,7 +345,7 @@ class FindInPageTests: XCTestCase { let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - + // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let findInPageScreenshot = loremIpsumWebView.screenshot() @@ -358,14 +358,14 @@ class FindInPageTests: XCTestCase { updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." ) - + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") let findNextScreenshot = loremIpsumWebView.screenshot() let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } let pixelSetIntersection = findHighlightPoints .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - + XCTAssertGreaterThan( highlightedPixelsInFindNextScreenshot.count, minimumExpectedMatchingPixelsInFindHighlight, @@ -391,26 +391,26 @@ private extension FindInPageTests { let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) return loremIpsumHTMLFileURL } - + /// Save a local HTML file for testing find behavor against class func saveLocalHTML() { let loremIpsumHTML = """ Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

- +

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

- +

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

""" let loremIpsumData = Data(loremIpsumHTML.utf8) - + do { try loremIpsumData.write(to: loremIpsumFileURL, options: []) } catch { XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") } } - + /// Remove it when done class func removeLocalHTML() { do { @@ -426,29 +426,28 @@ private extension NSImage { /// - Parameter colorToMatch: the NSColor to match /// - Returns: An array of Pixel structs func matchingPixels(of colorToMatch: NSColor) -> [Pixel] { - let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) XCTAssertNotNil(cgImage, "It wasn't possible to obtain the CGImage of the NSImage.") let bitmap = NSBitmapImageRep(cgImage: cgImage!) let colorSpace = bitmap.colorSpace - + XCTAssertNotNil( colorToMatch.usingColorSpace(colorSpace), "It wasn't possible to get the color to match in the local colorspace." ) let colorToMatchWithColorSpace = colorToMatch - .usingColorSpace(colorSpace)! // We need to compare the color we want to look for in the image after it is in the same colorspace as the image - + .usingColorSpace(colorSpace)! // Compare the color we want to look for in the image after it is in the same colorspace as the image + XCTAssertNotNil(bitmap.bitmapData, "It wasn't possible to obtain the bitmapData of the bitmap.") var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! var redInImage, greenInImage, blueInImage, alphaInImage: UInt8 - + let redToMatch = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace let greenToMatch = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) let blueToMatch = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) - + var pixels: [Pixel] = [] - + for yPoint in 0 ..< bitmap.pixelsHigh { for xPoint in 0 ..< bitmap.pixelsWide { redInImage = bitmapData.pointee @@ -460,7 +459,13 @@ private extension NSImage { alphaInImage = bitmapData.pointee bitmapData = bitmapData.advanced(by: 1) if redInImage == redToMatch, greenInImage == greenToMatch, blueInImage == blueToMatch { // We aren't matching alpha - pixels.append(Pixel(red: redInImage, green: greenInImage, blue: blueInImage, alpha: alphaInImage, point: CGPoint(x: xPoint, y: yPoint))) + pixels.append(Pixel( + red: redInImage, + green: greenInImage, + blue: blueInImage, + alpha: alphaInImage, + point: CGPoint(x: xPoint, y: yPoint) + )) } } } From f7a80a6fcbacc339c745851e21c5d6f4fe7faf96 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:28:36 +0100 Subject: [PATCH 050/108] Formatting fix --- UITests/FindInPageTests.swift | 105 ++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 806e18c04a..e53a29e9c2 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -25,15 +25,15 @@ class FindInPageTests: XCTestCase { var loremIpsumWebView: XCUIElement! var findInPageCloseButton: XCUIElement! let minimumExpectedMatchingPixelsInFindHighlight = 150 - + override class func setUp() { saveLocalHTML() } - + override class func tearDown() { removeLocalHTML() } - + override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() @@ -44,7 +44,7 @@ class FindInPageTests: XCTestCase { app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window app.typeKey("n", modifierFlags: .command) } - + func test_findInPage_canBeOpenedWithKeyCommand() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -52,15 +52,15 @@ class FindInPageTests: XCTestCase { loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) - + app.typeKey("f", modifierFlags: .command) - + XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) } - + func test_findInPage_canBeOpenedWithMenuBarItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -73,15 +73,15 @@ class FindInPageTests: XCTestCase { findInPageMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." ) - + findInPageMenuBarItem.click() - + XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." ) } - + func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -92,20 +92,20 @@ class FindInPageTests: XCTestCase { let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") optionsButton.click() - + let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] XCTAssertTrue( findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." ) findInPageMoreOptionsMenuItem.click() - + XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." ) } - + func test_findInPage_canBeClosedWithEscape() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -118,15 +118,15 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeKey(.escape, modifierFlags: []) - + XCTAssertTrue( findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } - + func test_findInPage_canBeClosedWithShiftCommandF() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -139,15 +139,15 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeKey("f", modifierFlags: [.command, .shift]) - + XCTAssertTrue( findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } - + func test_findInPage_canBeClosedWithHideFindMenuItem() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -160,20 +160,20 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] XCTAssertTrue( findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." ) findInPageDoneMenuBarItem.click() - + XCTAssertTrue( findInPageCloseButton.waitForNonExistence(timeout: timeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } - + func test_findInPage_showsCorrectNumberOfOccurrences() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -181,20 +181,20 @@ class FindInPageTests: XCTestCase { loremIpsumWebView.waitForExistence(timeout: timeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) - + app.typeKey("f", modifierFlags: .command) XCTAssertTrue( findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. } - + func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -207,14 +207,14 @@ class FindInPageTests: XCTestCase { findInPageCloseButton.waitForExistence(timeout: timeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) - + app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") - + let webViewWithSelectedWordsScreenshot = loremIpsumWebView.screenshot() let highlightedPixelsInScreenshot = webViewWithSelectedWordsScreenshot.image.matchingPixels(of: .findHighlightColor) XCTAssertGreaterThan( @@ -223,7 +223,7 @@ class FindInPageTests: XCTestCase { "There are expected to be more than \(minimumExpectedMatchingPixelsInFindHighlight) pixels of NSColor.findHighlightColor in a screenshot of a \"Find in Page\" search where there is a match, but this test found \(highlightedPixelsInScreenshot) matching pixels." ) } - + func test_findInPage_findNextGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -245,7 +245,7 @@ class FindInPageTests: XCTestCase { let findInPageScreenshot = loremIpsumWebView.screenshot() let highlightedPixelsInFindScreenshot = findInPageScreenshot.image.matchingPixels(of: .findHighlightColor) let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot - + let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] XCTAssertTrue( findNextMenuBarItem.waitForExistence(timeout: timeout), @@ -265,7 +265,7 @@ class FindInPageTests: XCTestCase { let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } let pixelSetIntersection = findHighlightPoints .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - + XCTAssertGreaterThan( highlightedPixelsInFindNextScreenshot.count, minimumExpectedMatchingPixelsInFindHighlight, @@ -276,7 +276,7 @@ class FindInPageTests: XCTestCase { "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." ) } - + func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -303,7 +303,7 @@ class FindInPageTests: XCTestCase { findInPageNextButton.waitForExistence(timeout: timeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." ) - + findInPageNextButton.click() let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String @@ -317,7 +317,7 @@ class FindInPageTests: XCTestCase { let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } let pixelSetIntersection = findHighlightPoints .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - + XCTAssertGreaterThan( highlightedPixelsInFindNextScreenshot.count, minimumExpectedMatchingPixelsInFindHighlight, @@ -328,7 +328,7 @@ class FindInPageTests: XCTestCase { "There should be no points in common for the highlighted pixels in the initial \"Find in Page\" operation, and the highlighted pixel coordinates in the \"Find Next\" operation." ) } - + func test_findInPage_commandGGoesToNextOccurrence() throws { XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") @@ -345,7 +345,7 @@ class FindInPageTests: XCTestCase { let statusField = app.textFields["FindInPageController.statusField"] XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) - + // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") let findInPageScreenshot = loremIpsumWebView.screenshot() @@ -358,14 +358,14 @@ class FindInPageTests: XCTestCase { updatedStatusField.waitForExistence(timeout: timeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." ) - + XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") let findNextScreenshot = loremIpsumWebView.screenshot() let highlightedPixelsInFindNextScreenshot = findNextScreenshot.image.matchingPixels(of: .findHighlightColor) let findNextHighlightPoints = highlightedPixelsInFindNextScreenshot.map { $0.point } let pixelSetIntersection = findHighlightPoints .intersection(findNextHighlightPoints) // If the highlighted text has moved as expected, this should have no elements - + XCTAssertGreaterThan( highlightedPixelsInFindNextScreenshot.count, minimumExpectedMatchingPixelsInFindHighlight, @@ -391,26 +391,26 @@ private extension FindInPageTests { let loremIpsumHTMLFileURL = documentsDirectory.appendingPathComponent(loremIpsumFileName) return loremIpsumHTMLFileURL } - + /// Save a local HTML file for testing find behavor against class func saveLocalHTML() { let loremIpsumHTML = """ Lorem Ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac sem nisi. Cras fermentum mi vitae turpis efficitur malesuada. Donec eget maxima ligula, et tincidunt sapien. Suspendisse posuere diam maxima, dignissim ex at, fringilla elit. Maecenas enim tellus, ornare non pretium a, sodales nec lectus. Vestibulum quis augue orci. Donec eget mi sed magna consequat auctor a a nulla. Etiam condimentum, neque at congue semper, arcu sem commodo tellus, venenatis finibus ex magna vitae erat. Nunc non enim sit amet mi posuere egestas. Donec nibh nisl, pretium sit amet aliquet, porta id nibh. Pellentesque ullamcorper mauris quam, semper hendrerit mi dictum non. Nullam pulvinar, nulla a maximus egestas, velit mi volutpat neque, vitae placerat eros sapien vitae tellus. Pellentesque malesuada accumsan dolor, ut feugiat enim. Curabitur nunc quam, maximus venenatis augue vel, accumsan eros.

- +

Donec consequat ultrices ante non maximus. Quisque eu semper diam. Nunc ullamcorper eget ex id luctus. Duis metus ex, dapibus sit amet vehicula eget, rhoncus eget lacus. Nulla maximus quis turpis vel pulvinar. Duis neque ligula, tristique et diam ut, fringilla sagittis arcu. Vestibulum suscipit semper lectus, quis placerat ex euismod eu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;

- +

Maecenas odio orci, eleifend et ipsum nec, interdum dictum turpis. Nunc nec velit diam. Sed nisl risus, imperdiet sit amet tempor ut, laoreet sed lorem. Aenean egestas ullamcorper sem. Sed accumsan vehicula augue, vitae tempor augue tincidunt id. Morbi ullamcorper posuere lacus id tempus. Ut vel tincidunt quam, quis consectetur velit. Mauris id lorem vitae odio consectetur vehicula. Vestibulum viverra scelerisque porta. Vestibulum eu consequat urna. Etiam dignissim ullamcorper faucibus.

""" let loremIpsumData = Data(loremIpsumHTML.utf8) - + do { try loremIpsumData.write(to: loremIpsumFileURL, options: []) } catch { XCTFail("It wasn't possible to write out the required local HTML file for the tests: \(error.localizedDescription)") } } - + /// Remove it when done class func removeLocalHTML() { do { @@ -426,29 +426,28 @@ private extension NSImage { /// - Parameter colorToMatch: the NSColor to match /// - Returns: An array of Pixel structs func matchingPixels(of colorToMatch: NSColor) -> [Pixel] { - let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) XCTAssertNotNil(cgImage, "It wasn't possible to obtain the CGImage of the NSImage.") let bitmap = NSBitmapImageRep(cgImage: cgImage!) let colorSpace = bitmap.colorSpace - + XCTAssertNotNil( colorToMatch.usingColorSpace(colorSpace), "It wasn't possible to get the color to match in the local colorspace." ) let colorToMatchWithColorSpace = colorToMatch - .usingColorSpace(colorSpace)! // We need to compare the color we want to look for in the image after it is in the same colorspace as the image - + .usingColorSpace(colorSpace)! // Compare the color we want to look for in the image after it is in the same colorspace as the image + XCTAssertNotNil(bitmap.bitmapData, "It wasn't possible to obtain the bitmapData of the bitmap.") var bitmapData: UnsafeMutablePointer = bitmap.bitmapData! var redInImage, greenInImage, blueInImage, alphaInImage: UInt8 - + let redToMatch = UInt8(colorToMatchWithColorSpace.redComponent * 255.999999) // color components in 0-255 values in this colorspace let greenToMatch = UInt8(colorToMatchWithColorSpace.greenComponent * 255.999999) let blueToMatch = UInt8(colorToMatchWithColorSpace.blueComponent * 255.999999) - + var pixels: [Pixel] = [] - + for yPoint in 0 ..< bitmap.pixelsHigh { for xPoint in 0 ..< bitmap.pixelsWide { redInImage = bitmapData.pointee @@ -460,7 +459,13 @@ private extension NSImage { alphaInImage = bitmapData.pointee bitmapData = bitmapData.advanced(by: 1) if redInImage == redToMatch, greenInImage == greenToMatch, blueInImage == blueToMatch { // We aren't matching alpha - pixels.append(Pixel(red: redInImage, green: greenInImage, blue: blueInImage, alpha: alphaInImage, point: CGPoint(x: xPoint, y: yPoint))) + pixels.append(Pixel( + red: redInImage, + green: greenInImage, + blue: blueInImage, + alpha: alphaInImage, + point: CGPoint(x: xPoint, y: yPoint) + )) } } } From 8e956616868d8338a4f44d2158ec536294c70eee Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:48:41 +0100 Subject: [PATCH 051/108] Update sync tests with new accessibility identifiers --- SyncE2EUITests/CriticalPathsTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SyncE2EUITests/CriticalPathsTests.swift b/SyncE2EUITests/CriticalPathsTests.swift index c59174b4fe..32a1115a2b 100644 --- a/SyncE2EUITests/CriticalPathsTests.swift +++ b/SyncE2EUITests/CriticalPathsTests.swift @@ -62,14 +62,14 @@ final class CriticalPathsTests: XCTestCase { debugMenuBarItem = menuBarsQuery.menuBarItems["Debug"] debugMenuBarItem.click() - let resetDataMenuItem = menuBarsQuery.menuItems["Reset Data"] + let resetDataMenuItem = menuBarsQuery.menuItems["MainMenu.resetData"] resetDataMenuItem.hover() - let resetBookMarksData = resetDataMenuItem.menuItems["Reset Bookmarks"] + let resetBookMarksData = resetDataMenuItem.menuItems["MainMenu.resetBookmarks"] resetBookMarksData.click() debugMenuBarItem.click() resetDataMenuItem.hover() - let resetAutofillData = resetDataMenuItem.menuItems["Reset Autofill Data"] + let resetAutofillData = resetDataMenuItem.menuItems["MainMenu.resetSecureVaultData"] resetAutofillData.click() } From e504083e3b27a85434eb0f3b82bed24ecfffb69c Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:49:04 +0100 Subject: [PATCH 052/108] Add accessibility identifier for adding bookmarks --- .../Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift b/DuckDuckGo/Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift index 7726516704..d55f6dd34f 100644 --- a/DuckDuckGo/Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift +++ b/DuckDuckGo/Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift @@ -42,7 +42,7 @@ struct BookmarkDialogButtonsView: View { actionButton(action: otherButtonAction, viewState: viewState) - actionButton(action: defaultButtonAction, viewState: viewState) + actionButton(action: defaultButtonAction, viewState: viewState).accessibilityIdentifier("BookmarkDialogButtonsView.defaultButton") } } From 961cbf23ac971a256551db338a42816055202a09 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:50:43 +0100 Subject: [PATCH 053/108] Add accessibility identifiers for resetting data menu items --- DuckDuckGo/Menus/MainMenu.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index e31d22b8d1..e6964e7785 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -570,8 +570,8 @@ import SubscriptionUI NSMenuItem(title: "Reset Data") { NSMenuItem(title: "Reset Default Browser Prompt", action: #selector(MainViewController.resetDefaultBrowserPrompt)) NSMenuItem(title: "Reset Default Grammar Checks", action: #selector(MainViewController.resetDefaultGrammarChecks)) - NSMenuItem(title: "Reset Autofill Data", action: #selector(MainViewController.resetSecureVaultData)) - NSMenuItem(title: "Reset Bookmarks", action: #selector(MainViewController.resetBookmarks)) + NSMenuItem(title: "Reset Autofill Data", action: #selector(MainViewController.resetSecureVaultData)).withAccessibilityIdentifier("MainMenu.resetSecureVaultData") + NSMenuItem(title: "Reset Bookmarks", action: #selector(MainViewController.resetBookmarks)).withAccessibilityIdentifier("MainMenu.resetBookmarks") NSMenuItem(title: "Reset Pinned Tabs", action: #selector(MainViewController.resetPinnedTabs)) NSMenuItem(title: "Reset YouTube Overlay Interactions", action: #selector(MainViewController.resetDuckPlayerOverlayInteractions)) NSMenuItem(title: "Reset MakeDuckDuckYours user settings", action: #selector(MainViewController.resetMakeDuckDuckGoYoursUserSettings)) @@ -586,7 +586,7 @@ import SubscriptionUI } NSMenuItem(title: "Reset Email Protection InContext Signup Prompt", action: #selector(MainViewController.resetEmailProtectionInContextPrompt)) NSMenuItem(title: "Reset Daily Pixels", action: #selector(MainViewController.resetDailyPixels)) - } + }.withAccessibilityIdentifier("MainMenu.resetData") NSMenuItem(title: "UI Triggers") { NSMenuItem(title: "Show Save Credentials Popover", action: #selector(MainViewController.showSaveCredentialsPopover)) NSMenuItem(title: "Show Credentials Saved Popover", action: #selector(MainViewController.showCredentialsSavedPopover)) From 807bf6c18e661ead3ff84109375d9a139287766c Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:28:08 +0100 Subject: [PATCH 054/108] Add accessibility identifier to suggestions tableView --- DuckDuckGo/Suggestions/View/SuggestionViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DuckDuckGo/Suggestions/View/SuggestionViewController.swift b/DuckDuckGo/Suggestions/View/SuggestionViewController.swift index 44442c1f86..b53239a6ac 100644 --- a/DuckDuckGo/Suggestions/View/SuggestionViewController.swift +++ b/DuckDuckGo/Suggestions/View/SuggestionViewController.swift @@ -87,6 +87,7 @@ final class SuggestionViewController: NSViewController { private func setupTableView() { tableView.style = .plain + tableView.setAccessibilityIdentifier("SuggestionViewController.tableView") } private func addTrackingArea() { From e3165331b8cf945a372263027542a9e1a12144a8 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:28:23 +0100 Subject: [PATCH 055/108] Passing autocomplete tests --- UITests/AutocompleteTests.swift | 170 +++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 4 deletions(-) diff --git a/UITests/AutocompleteTests.swift b/UITests/AutocompleteTests.swift index 8936e996a9..5dd7ed1641 100644 --- a/UITests/AutocompleteTests.swift +++ b/UITests/AutocompleteTests.swift @@ -20,17 +20,179 @@ import Common import XCTest class AutocompleteTests: XCTestCase { - let app = XCUIApplication() + var app: XCUIApplication! + var addBookmarkButton: XCUIElement! + var resetBookMarksDataMenuItem: XCUIElement! + var historyMenuBarItem: XCUIElement! + var clearAllHistory: XCUIElement! + var addressBarTextField: XCUIElement! + var tableView: XCUIElement! let timeout = 0.3 + let siteTitleForBookmarkedSite = String(UUID().uuidString.prefix(16)) + let siteTitleForHistorySite = String(UUID().uuidString.prefix(16)) override func setUpWithError() throws { continueAfterFailure = false + app = XCUIApplication() + addBookmarkButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] + resetBookMarksDataMenuItem = app.menuItems["MainMenu.resetBookmarks"] + historyMenuBarItem = app.menuBarItems["History"] + clearAllHistory = app.menuItems["HistoryMenu.clearAllHistory"] + addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + tableView = app.tables["SuggestionViewController.tableView"] app.launch() - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Enforce a single window app.typeKey("n", modifierFlags: .command) + resetAndArrangeBookmarksAndHistory() // Manually reset to a clean state } - func test_failingTest() throws { - XCTFail("Test should always fail") + func test_typingTitleOfBookmarkedPageShowsItInSuggestionsAsBookmark() throws { + app.typeText(siteTitleForBookmarkedSite) + XCTAssertTrue( + tableView.waitForExistence(timeout: timeout), + "Suggestions tableView didn't become available in a reasonable timeframe." + ) + + let suggestionCellWithBookmarkedSite = tableView.tableRows.cells.staticTexts["\(siteTitleForBookmarkedSite)"].firstMatch + XCTAssertTrue( // It should match something in suggestions + suggestionCellWithBookmarkedSite.waitForExistence(timeout: timeout), + "The expected table view cell with the suggestion for the bookmarked site didn't become available in a reasonable timeframe." + ) + let containerCellForBookmarkedSuggestion = app.tables.cells.containing(.any, identifier: suggestionCellWithBookmarkedSite.identifier) + .firstMatch + + XCTAssertTrue( + containerCellForBookmarkedSuggestion.waitForExistence(timeout: timeout), + "The enclosing cell for the suggestion cell for the bookmarked site didn't become available in a reasonable timeframe." + ) + // And that should be a bookmark suggestion + XCTAssertEqual(containerCellForBookmarkedSuggestion.images.firstMatch.label, "BookmarkSuggestion") + } + + func test_typingTitleOfHistoryPageShowsItInSuggestionsAsHistory() throws { + app.typeText(siteTitleForHistorySite) + XCTAssertTrue( + tableView.waitForExistence(timeout: timeout), + "Suggestions tableView didn't become available in a reasonable timeframe." + ) + + let suggestionCellWithHistorySite = tableView.tableRows.cells.staticTexts["\(siteTitleForHistorySite)"].firstMatch + XCTAssertTrue( // It should match something in suggestions + suggestionCellWithHistorySite.waitForExistence(timeout: timeout), + "The expected table view cell with the suggestion for the history site didn't become available in a reasonable timeframe." + ) + let containerCellForHistorySuggestion = app.tables.cells.containing(.any, identifier: suggestionCellWithHistorySite.identifier) + .firstMatch + + XCTAssertTrue( + containerCellForHistorySuggestion.waitForExistence(timeout: timeout), + "The enclosing cell for the suggestion cell for the history site didn't become available in a reasonable timeframe." + ) + // And that should be a history suggestion + XCTAssertEqual(containerCellForHistorySuggestion.images.firstMatch.label, "HistorySuggestion") + } + + func test_typingURLOfWebsiteNotInBookmarksOrHistoryShowsItInSuggestionsAsWebsite() throws { + let websiteURLString = "https://www.duckduckgo.com" + app.typeText(websiteURLString) + XCTAssertTrue( + tableView.waitForExistence(timeout: timeout), + "Suggestions tableView didn't become available in a reasonable timeframe." + ) + + let suggestionCellWithWebsite = tableView.tableRows.cells.staticTexts["\(websiteURLString)"].firstMatch + XCTAssertTrue( // It should match something in suggestions + suggestionCellWithWebsite.waitForExistence(timeout: timeout), + "The expected table view cell with the suggestion for the website didn't become available in a reasonable timeframe." + ) + let containerCellForWebsiteSuggestion = app.tables.cells.containing(.any, identifier: suggestionCellWithWebsite.identifier) + .firstMatch + + XCTAssertTrue( + containerCellForWebsiteSuggestion.waitForExistence(timeout: timeout), + "The enclosing cell for the suggestion cell for the website didn't become available in a reasonable timeframe." + ) + + // And that should be a website suggestion + XCTAssertEqual(containerCellForWebsiteSuggestion.images.firstMatch.label, "Web") + } +} + +private extension AutocompleteTests { + /// Make sure there is exactly one site in the history, and exactly one site in the bookmarks, and they aren't the same site. + func resetAndArrangeBookmarksAndHistory() { + XCTAssertTrue( + resetBookMarksDataMenuItem.waitForExistence(timeout: timeout), + "Reset bookmarks menu item didn't become available in a reasonable timeframe." + ) + + resetBookMarksDataMenuItem.click() + + let urlForBookmarks = locallyServedURL(pageTitle: siteTitleForBookmarkedSite) + + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: timeout), + "The address bar text field didn't become available in a reasonable timeframe." + ) + + addressBarTextField.typeText("\(urlForBookmarks.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews["\(siteTitleForBookmarkedSite)"].waitForExistence(timeout: timeout), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + + app.typeKey("d", modifierFlags: [.command]) // Bookmark the page + XCTAssertTrue( + addBookmarkButton.waitForExistence(timeout: timeout), + "Bookmark button didn't appear with the expected title in a reasonable timeframe." + ) + addBookmarkButton.click() + + XCTAssertTrue( + historyMenuBarItem.waitForExistence(timeout: timeout), + "History menu bar item didn't appear in a reasonable timeframe." + ) + historyMenuBarItem.click() + + XCTAssertTrue( + clearAllHistory.waitForExistence(timeout: timeout), + "Clear all history item didn't appear in a reasonable timeframe." + ) + clearAllHistory.click() + + XCTAssertTrue( + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: timeout), + "Clear all history item didn't appear in a reasonable timeframe." + ) + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And manually remove the history + + let urlForHistory = locallyServedURL(pageTitle: siteTitleForHistorySite) + + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: timeout), + "The address bar text field didn't become available in a reasonable timeframe." + ) + + addressBarTextField.typeText("\(urlForHistory.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews["\(siteTitleForHistorySite)"].waitForExistence(timeout: timeout), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + } + + func locallyServedURL(pageTitle: String) -> URL { + return URL.testsServer + .appendingTestParameters(data: """ + + + \(pageTitle) + + + Text + + + """.utf8data) } } From 0197f65190f16659d27580a7119c0fb6d881f39d Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:11:26 +0100 Subject: [PATCH 056/108] Standardize test style across all three test cases --- UITests/AutocompleteTests.swift | 98 ++++++++++-------- UITests/BrowsingHistoryTests.swift | 63 ++++++------ UITests/FindInPageTests.swift | 160 +++++++++++++++++++---------- 3 files changed, 188 insertions(+), 133 deletions(-) diff --git a/UITests/AutocompleteTests.swift b/UITests/AutocompleteTests.swift index 5dd7ed1641..4b48c6585e 100644 --- a/UITests/AutocompleteTests.swift +++ b/UITests/AutocompleteTests.swift @@ -20,26 +20,26 @@ import Common import XCTest class AutocompleteTests: XCTestCase { - var app: XCUIApplication! - var addBookmarkButton: XCUIElement! - var resetBookMarksDataMenuItem: XCUIElement! - var historyMenuBarItem: XCUIElement! - var clearAllHistory: XCUIElement! - var addressBarTextField: XCUIElement! - var tableView: XCUIElement! - let timeout = 0.3 - let siteTitleForBookmarkedSite = String(UUID().uuidString.prefix(16)) - let siteTitleForHistorySite = String(UUID().uuidString.prefix(16)) + private var app: XCUIApplication! + private var addBookmarkButton: XCUIElement! + private var resetBookMarksMenuItem: XCUIElement! + private var historyMenuBarItem: XCUIElement! + private var clearAllHistoryMenuItem: XCUIElement! + private var addressBarTextField: XCUIElement! + private var suggestionsTableView: XCUIElement! + private let elementExistenceTimeout = 0.3 + private let siteTitleForBookmarkedSite = String(UUID().uuidString.prefix(16)) + private let siteTitleForHistorySite = String(UUID().uuidString.prefix(16)) override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() addBookmarkButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] - resetBookMarksDataMenuItem = app.menuItems["MainMenu.resetBookmarks"] + resetBookMarksMenuItem = app.menuItems["MainMenu.resetBookmarks"] historyMenuBarItem = app.menuBarItems["History"] - clearAllHistory = app.menuItems["HistoryMenu.clearAllHistory"] + clearAllHistoryMenuItem = app.menuItems["HistoryMenu.clearAllHistory"] addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] - tableView = app.tables["SuggestionViewController.tableView"] + suggestionsTableView = app.tables["SuggestionViewController.tableView"] app.launch() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Enforce a single window app.typeKey("n", modifierFlags: .command) @@ -49,72 +49,84 @@ class AutocompleteTests: XCTestCase { func test_typingTitleOfBookmarkedPageShowsItInSuggestionsAsBookmark() throws { app.typeText(siteTitleForBookmarkedSite) XCTAssertTrue( - tableView.waitForExistence(timeout: timeout), + suggestionsTableView.waitForExistence(timeout: elementExistenceTimeout), "Suggestions tableView didn't become available in a reasonable timeframe." ) - - let suggestionCellWithBookmarkedSite = tableView.tableRows.cells.staticTexts["\(siteTitleForBookmarkedSite)"].firstMatch + + let suggestionCellWithBookmarkedSite = suggestionsTableView.tableRows.cells.staticTexts["\(siteTitleForBookmarkedSite)"].firstMatch XCTAssertTrue( // It should match something in suggestions - suggestionCellWithBookmarkedSite.waitForExistence(timeout: timeout), + suggestionCellWithBookmarkedSite.waitForExistence(timeout: elementExistenceTimeout), "The expected table view cell with the suggestion for the bookmarked site didn't become available in a reasonable timeframe." ) let containerCellForBookmarkedSuggestion = app.tables.cells.containing(.any, identifier: suggestionCellWithBookmarkedSite.identifier) .firstMatch XCTAssertTrue( - containerCellForBookmarkedSuggestion.waitForExistence(timeout: timeout), - "The enclosing cell for the suggestion cell for the bookmarked site didn't become available in a reasonable timeframe." + containerCellForBookmarkedSuggestion.waitForExistence(timeout: elementExistenceTimeout), + "The enclosing cell for the suggestion cell for the bookmarked site didn't become available in a reasonable timeframe. If this is unexpected, it could be due to a significant change in the layout of this interface." ) // And that should be a bookmark suggestion - XCTAssertEqual(containerCellForBookmarkedSuggestion.images.firstMatch.label, "BookmarkSuggestion") + XCTAssertEqual( + containerCellForBookmarkedSuggestion.images.firstMatch.label, + "BookmarkSuggestion", + "Although the suggestion was found, it didn't have the \"BookmarkSuggestion\" image next to it in suggestions." + ) } func test_typingTitleOfHistoryPageShowsItInSuggestionsAsHistory() throws { app.typeText(siteTitleForHistorySite) XCTAssertTrue( - tableView.waitForExistence(timeout: timeout), + suggestionsTableView.waitForExistence(timeout: elementExistenceTimeout), "Suggestions tableView didn't become available in a reasonable timeframe." ) - let suggestionCellWithHistorySite = tableView.tableRows.cells.staticTexts["\(siteTitleForHistorySite)"].firstMatch + let suggestionCellWithHistorySite = suggestionsTableView.tableRows.cells.staticTexts["\(siteTitleForHistorySite)"].firstMatch XCTAssertTrue( // It should match something in suggestions - suggestionCellWithHistorySite.waitForExistence(timeout: timeout), + suggestionCellWithHistorySite.waitForExistence(timeout: elementExistenceTimeout), "The expected table view cell with the suggestion for the history site didn't become available in a reasonable timeframe." ) let containerCellForHistorySuggestion = app.tables.cells.containing(.any, identifier: suggestionCellWithHistorySite.identifier) .firstMatch XCTAssertTrue( - containerCellForHistorySuggestion.waitForExistence(timeout: timeout), - "The enclosing cell for the suggestion cell for the history site didn't become available in a reasonable timeframe." + containerCellForHistorySuggestion.waitForExistence(timeout: elementExistenceTimeout), + "The enclosing cell for the suggestion cell for the history site didn't become available in a reasonable timeframe. If this is unexpected, it could be due to a significant change in the layout of this interface." ) // And that should be a history suggestion - XCTAssertEqual(containerCellForHistorySuggestion.images.firstMatch.label, "HistorySuggestion") + XCTAssertEqual( + containerCellForHistorySuggestion.images.firstMatch.label, + "HistorySuggestion", + "Although the suggestion was found, it didn't have the \"HistorySuggestion\" image next to it in suggestions." + ) } func test_typingURLOfWebsiteNotInBookmarksOrHistoryShowsItInSuggestionsAsWebsite() throws { let websiteURLString = "https://www.duckduckgo.com" app.typeText(websiteURLString) XCTAssertTrue( - tableView.waitForExistence(timeout: timeout), + suggestionsTableView.waitForExistence(timeout: elementExistenceTimeout), "Suggestions tableView didn't become available in a reasonable timeframe." ) - let suggestionCellWithWebsite = tableView.tableRows.cells.staticTexts["\(websiteURLString)"].firstMatch + let suggestionCellWithWebsite = suggestionsTableView.tableRows.cells.staticTexts["\(websiteURLString)"].firstMatch XCTAssertTrue( // It should match something in suggestions - suggestionCellWithWebsite.waitForExistence(timeout: timeout), + suggestionCellWithWebsite.waitForExistence(timeout: elementExistenceTimeout), "The expected table view cell with the suggestion for the website didn't become available in a reasonable timeframe." ) let containerCellForWebsiteSuggestion = app.tables.cells.containing(.any, identifier: suggestionCellWithWebsite.identifier) .firstMatch XCTAssertTrue( - containerCellForWebsiteSuggestion.waitForExistence(timeout: timeout), - "The enclosing cell for the suggestion cell for the website didn't become available in a reasonable timeframe." + containerCellForWebsiteSuggestion.waitForExistence(timeout: elementExistenceTimeout), + "The enclosing cell for the suggestion cell for the website didn't become available in a reasonable timeframe. If this is unexpected, it could be due to a significant change in the layout of this interface." ) // And that should be a website suggestion - XCTAssertEqual(containerCellForWebsiteSuggestion.images.firstMatch.label, "Web") + XCTAssertEqual( + containerCellForWebsiteSuggestion.images.firstMatch.label, + "Web", + "Although the suggestion was found, it didn't have the \"Web\" image next to it in suggestions." + ) } } @@ -122,46 +134,46 @@ private extension AutocompleteTests { /// Make sure there is exactly one site in the history, and exactly one site in the bookmarks, and they aren't the same site. func resetAndArrangeBookmarksAndHistory() { XCTAssertTrue( - resetBookMarksDataMenuItem.waitForExistence(timeout: timeout), + resetBookMarksMenuItem.waitForExistence(timeout: elementExistenceTimeout), "Reset bookmarks menu item didn't become available in a reasonable timeframe." ) - resetBookMarksDataMenuItem.click() + resetBookMarksMenuItem.click() let urlForBookmarks = locallyServedURL(pageTitle: siteTitleForBookmarkedSite) XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: timeout), + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The address bar text field didn't become available in a reasonable timeframe." ) addressBarTextField.typeText("\(urlForBookmarks.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(siteTitleForBookmarkedSite)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(siteTitleForBookmarkedSite)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) app.typeKey("d", modifierFlags: [.command]) // Bookmark the page XCTAssertTrue( - addBookmarkButton.waitForExistence(timeout: timeout), + addBookmarkButton.waitForExistence(timeout: elementExistenceTimeout), "Bookmark button didn't appear with the expected title in a reasonable timeframe." ) addBookmarkButton.click() XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() XCTAssertTrue( - clearAllHistory.waitForExistence(timeout: timeout), + clearAllHistoryMenuItem.waitForExistence(timeout: elementExistenceTimeout), "Clear all history item didn't appear in a reasonable timeframe." ) - clearAllHistory.click() + clearAllHistoryMenuItem.click() XCTAssertTrue( - app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: timeout), + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: elementExistenceTimeout), "Clear all history item didn't appear in a reasonable timeframe." ) app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And manually remove the history @@ -169,13 +181,13 @@ private extension AutocompleteTests { let urlForHistory = locallyServedURL(pageTitle: siteTitleForHistorySite) XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: timeout), + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The address bar text field didn't become available in a reasonable timeframe." ) addressBarTextField.typeText("\(urlForHistory.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(siteTitleForHistorySite)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(siteTitleForHistorySite)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) app.typeKey("w", modifierFlags: [.command, .option, .shift]) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 1a06f2af54..6832c34afd 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -20,62 +20,62 @@ import Common import XCTest class BrowsingHistoryTests: XCTestCase { - var app: XCUIApplication! - var historyMenuBarItem: XCUIElement! - var clearAllHistory: XCUIElement! - let lengthForRandomPageTitle = 8 - let timeout = 0.3 + private var app: XCUIApplication! + private var historyMenuBarItem: XCUIElement! + private var clearAllHistoryMenuItem: XCUIElement! + private let lengthForRandomPageTitle = 8 + private let elementExistenceTimeout = 0.3 override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() historyMenuBarItem = app.menuBarItems["History"] - clearAllHistory = app.menuItems["HistoryMenu.clearAllHistory"] + clearAllHistoryMenuItem = app.menuItems["HistoryMenu.clearAllHistory"] app.launch() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window app.typeKey("n", modifierFlags: .command) XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() XCTAssertTrue( - clearAllHistory.waitForExistence(timeout: timeout), + clearAllHistoryMenuItem.waitForExistence(timeout: elementExistenceTimeout), "Clear all history item didn't appear in a reasonable timeframe." ) - clearAllHistory.click() + clearAllHistoryMenuItem.click() XCTAssertTrue( - app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: timeout), + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: elementExistenceTimeout), "Clear all history item didn't appear in a reasonable timeframe." ) app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And manually remove the history } func test_visitedSiteIsAddedToRecentlyVisited() throws { - let historyPageTitleExpectedToBeFirstInRecentlyVisited = randomPageTitle(length: lengthForRandomPageTitle) + let historyPageTitleExpectedToBeFirstInRecentlyVisited = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let url = locallyServedURL(pageTitle: historyPageTitleExpectedToBeFirstInRecentlyVisited) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInRecentlyVisitedSection = app.menuItems["HistoryMenu.recentlyVisitedMenuItem.0"] XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: timeout), + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The address bar text field didn't become available in a reasonable timeframe." ) addressBarTextField.typeText("\(url.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(historyPageTitleExpectedToBeFirstInRecentlyVisited)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(historyPageTitleExpectedToBeFirstInRecentlyVisited)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. XCTAssertTrue( - firstSiteInRecentlyVisitedSection.waitForExistence(timeout: timeout), + firstSiteInRecentlyVisitedSection.waitForExistence(timeout: elementExistenceTimeout), "The first site in the recently visited section didn't appear in a reasonable timeframe." ) @@ -83,29 +83,29 @@ class BrowsingHistoryTests: XCTestCase { } func test_visitedSiteIsInHistoryAfterClosingAndReopeningWindow() throws { - let historyPageTitleExpectedToBeFirstInTodayHistory = randomPageTitle(length: lengthForRandomPageTitle) + let historyPageTitleExpectedToBeFirstInTodayHistory = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let url = locallyServedURL(pageTitle: historyPageTitleExpectedToBeFirstInTodayHistory) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInHistory = app.menuItems["HistoryMenu.historyMenuItem.Today.0"] XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: timeout), + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The address bar text field didn't become available in a reasonable timeframe." ) addressBarTextField.typeText("\(url.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(historyPageTitleExpectedToBeFirstInTodayHistory)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(historyPageTitleExpectedToBeFirstInTodayHistory)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close all windows app.typeKey("n", modifierFlags: .command) // New window XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. XCTAssertTrue( - firstSiteInHistory.waitForExistence(timeout: timeout), + firstSiteInHistory.waitForExistence(timeout: elementExistenceTimeout), "The first site in the recently visited section didn't appear in a reasonable timeframe." ) @@ -113,59 +113,54 @@ class BrowsingHistoryTests: XCTestCase { } func test_lastClosedWindowsTabsCanBeReopenedViaHistory() throws { - let titleOfFirstTabWhichShouldRestore = randomPageTitle(length: lengthForRandomPageTitle) - let titleOfSecondTabWhichShouldRestore = randomPageTitle(length: lengthForRandomPageTitle) + let titleOfFirstTabWhichShouldRestore = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) + let titleOfSecondTabWhichShouldRestore = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let urlForFirstTab = locallyServedURL(pageTitle: titleOfFirstTabWhichShouldRestore) let urlForSecondTab = locallyServedURL(pageTitle: titleOfSecondTabWhichShouldRestore) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: timeout), + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The address bar text field didn't become available in a reasonable timeframe." ) addressBarTextField.typeText("\(urlForFirstTab.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) app.typeKey("t", modifierFlags: .command) addressBarTextField.typeText("\(urlForSecondTab.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) let reopenLastClosedWindowMenuItem = app.menuItems["HistoryMenu.reopenLastClosedWindow"] app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close all windows app.typeKey("n", modifierFlags: .command) // New window XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. XCTAssertTrue( - reopenLastClosedWindowMenuItem.waitForExistence(timeout: timeout), + reopenLastClosedWindowMenuItem.waitForExistence(timeout: elementExistenceTimeout), "The \"Reopen Last Closed Window\" menu item didn't appear in a reasonable timeframe." ) reopenLastClosedWindowMenuItem.click() XCTAssertTrue( - app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: elementExistenceTimeout), "Restored visited tab 1 wasn't available with the expected title in a reasonable timeframe." ) app.typeKey("w", modifierFlags: [.command]) XCTAssertTrue( - app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: elementExistenceTimeout), "Restored visited tab 2 wasn't available with the expected title in a reasonable timeframe." ) } } private extension BrowsingHistoryTests { - - func randomPageTitle(length: Int) -> String { - return String(UUID().uuidString.prefix(length)) - } - func locallyServedURL(pageTitle: String) -> URL { return URL.testsServer .appendingTestParameters(data: """ diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index e53a29e9c2..7c491e09a8 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -19,12 +19,12 @@ import XCTest class FindInPageTests: XCTestCase { - var app: XCUIApplication! - let timeout = 0.3 - var addressBarTextField: XCUIElement! - var loremIpsumWebView: XCUIElement! - var findInPageCloseButton: XCUIElement! - let minimumExpectedMatchingPixelsInFindHighlight = 150 + private var app: XCUIApplication! + private let elementExistenceTimeout = 0.3 + private var addressBarTextField: XCUIElement! + private var loremIpsumWebView: XCUIElement! + private var findInPageCloseButton: XCUIElement! + private let minimumExpectedMatchingPixelsInFindHighlight = 150 override class func setUp() { saveLocalHTML() @@ -46,171 +46,201 @@ class FindInPageTests: XCTestCase { } func test_findInPage_canBeOpenedWithKeyCommand() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) } func test_findInPage_canBeOpenedWithMenuBarItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] XCTAssertTrue( - findInPageMenuBarItem.waitForExistence(timeout: timeout), + findInPageMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." ) findInPageMenuBarItem.click() XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." ) } func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] - XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") + XCTAssertTrue(optionsButton.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find options item in a reasonable timeframe.") optionsButton.click() let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] XCTAssertTrue( - findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), + findInPageMoreOptionsMenuItem.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." ) findInPageMoreOptionsMenuItem.click() XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." ) } func test_findInPage_canBeClosedWithEscape() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeKey(.escape, modifierFlags: []) XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), + findInPageCloseButton.waitForNonExistence(timeout: elementExistenceTimeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } func test_findInPage_canBeClosedWithShiftCommandF() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeKey("f", modifierFlags: [.command, .shift]) XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), + findInPageCloseButton.waitForNonExistence(timeout: elementExistenceTimeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } func test_findInPage_canBeClosedWithHideFindMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] XCTAssertTrue( - findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), + findInPageDoneMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." ) findInPageDoneMenuBarItem.click() XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), + findInPageCloseButton.waitForNonExistence(timeout: elementExistenceTimeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } func test_findInPage_showsCorrectNumberOfOccurrences() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. } func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") @@ -225,20 +255,26 @@ class FindInPageTests: XCTestCase { } func test_findInPage_findNextGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") @@ -248,14 +284,14 @@ class FindInPageTests: XCTestCase { let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] XCTAssertTrue( - findNextMenuBarItem.waitForExistence(timeout: timeout), + findNextMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." ) findNextMenuBarItem.click() let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), + updatedStatusField.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." ) XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") @@ -278,20 +314,26 @@ class FindInPageTests: XCTestCase { } func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") @@ -300,7 +342,7 @@ class FindInPageTests: XCTestCase { let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot let findInPageNextButton = app.windows.buttons["FindInPageController.nextButton"] XCTAssertTrue( - findInPageNextButton.waitForExistence(timeout: timeout), + findInPageNextButton.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." ) @@ -308,7 +350,7 @@ class FindInPageTests: XCTestCase { let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), + updatedStatusField.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." ) XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") @@ -330,20 +372,26 @@ class FindInPageTests: XCTestCase { } func test_findInPage_commandGGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. @@ -355,7 +403,7 @@ class FindInPageTests: XCTestCase { let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), + updatedStatusField.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." ) From 36d2b899bf8b7db046a89cedd795819d29394382 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:19:19 +0100 Subject: [PATCH 057/108] Standardize test style across test cases --- UITests/BrowsingHistoryTests.swift | 63 ++++++------ UITests/FindInPageTests.swift | 160 +++++++++++++++++++---------- 2 files changed, 133 insertions(+), 90 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 1a06f2af54..6832c34afd 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -20,62 +20,62 @@ import Common import XCTest class BrowsingHistoryTests: XCTestCase { - var app: XCUIApplication! - var historyMenuBarItem: XCUIElement! - var clearAllHistory: XCUIElement! - let lengthForRandomPageTitle = 8 - let timeout = 0.3 + private var app: XCUIApplication! + private var historyMenuBarItem: XCUIElement! + private var clearAllHistoryMenuItem: XCUIElement! + private let lengthForRandomPageTitle = 8 + private let elementExistenceTimeout = 0.3 override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() historyMenuBarItem = app.menuBarItems["History"] - clearAllHistory = app.menuItems["HistoryMenu.clearAllHistory"] + clearAllHistoryMenuItem = app.menuItems["HistoryMenu.clearAllHistory"] app.launch() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window app.typeKey("n", modifierFlags: .command) XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() XCTAssertTrue( - clearAllHistory.waitForExistence(timeout: timeout), + clearAllHistoryMenuItem.waitForExistence(timeout: elementExistenceTimeout), "Clear all history item didn't appear in a reasonable timeframe." ) - clearAllHistory.click() + clearAllHistoryMenuItem.click() XCTAssertTrue( - app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: timeout), + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: elementExistenceTimeout), "Clear all history item didn't appear in a reasonable timeframe." ) app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And manually remove the history } func test_visitedSiteIsAddedToRecentlyVisited() throws { - let historyPageTitleExpectedToBeFirstInRecentlyVisited = randomPageTitle(length: lengthForRandomPageTitle) + let historyPageTitleExpectedToBeFirstInRecentlyVisited = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let url = locallyServedURL(pageTitle: historyPageTitleExpectedToBeFirstInRecentlyVisited) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInRecentlyVisitedSection = app.menuItems["HistoryMenu.recentlyVisitedMenuItem.0"] XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: timeout), + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The address bar text field didn't become available in a reasonable timeframe." ) addressBarTextField.typeText("\(url.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(historyPageTitleExpectedToBeFirstInRecentlyVisited)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(historyPageTitleExpectedToBeFirstInRecentlyVisited)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. XCTAssertTrue( - firstSiteInRecentlyVisitedSection.waitForExistence(timeout: timeout), + firstSiteInRecentlyVisitedSection.waitForExistence(timeout: elementExistenceTimeout), "The first site in the recently visited section didn't appear in a reasonable timeframe." ) @@ -83,29 +83,29 @@ class BrowsingHistoryTests: XCTestCase { } func test_visitedSiteIsInHistoryAfterClosingAndReopeningWindow() throws { - let historyPageTitleExpectedToBeFirstInTodayHistory = randomPageTitle(length: lengthForRandomPageTitle) + let historyPageTitleExpectedToBeFirstInTodayHistory = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let url = locallyServedURL(pageTitle: historyPageTitleExpectedToBeFirstInTodayHistory) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInHistory = app.menuItems["HistoryMenu.historyMenuItem.Today.0"] XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: timeout), + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The address bar text field didn't become available in a reasonable timeframe." ) addressBarTextField.typeText("\(url.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(historyPageTitleExpectedToBeFirstInTodayHistory)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(historyPageTitleExpectedToBeFirstInTodayHistory)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close all windows app.typeKey("n", modifierFlags: .command) // New window XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. XCTAssertTrue( - firstSiteInHistory.waitForExistence(timeout: timeout), + firstSiteInHistory.waitForExistence(timeout: elementExistenceTimeout), "The first site in the recently visited section didn't appear in a reasonable timeframe." ) @@ -113,59 +113,54 @@ class BrowsingHistoryTests: XCTestCase { } func test_lastClosedWindowsTabsCanBeReopenedViaHistory() throws { - let titleOfFirstTabWhichShouldRestore = randomPageTitle(length: lengthForRandomPageTitle) - let titleOfSecondTabWhichShouldRestore = randomPageTitle(length: lengthForRandomPageTitle) + let titleOfFirstTabWhichShouldRestore = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) + let titleOfSecondTabWhichShouldRestore = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let urlForFirstTab = locallyServedURL(pageTitle: titleOfFirstTabWhichShouldRestore) let urlForSecondTab = locallyServedURL(pageTitle: titleOfSecondTabWhichShouldRestore) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: timeout), + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The address bar text field didn't become available in a reasonable timeframe." ) addressBarTextField.typeText("\(urlForFirstTab.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) app.typeKey("t", modifierFlags: .command) addressBarTextField.typeText("\(urlForSecondTab.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." ) let reopenLastClosedWindowMenuItem = app.menuItems["HistoryMenu.reopenLastClosedWindow"] app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close all windows app.typeKey("n", modifierFlags: .command) // New window XCTAssertTrue( - historyMenuBarItem.waitForExistence(timeout: timeout), + historyMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "History menu bar item didn't appear in a reasonable timeframe." ) historyMenuBarItem.click() // The visited sites identifiers will not be available until after the History menu has been accessed. XCTAssertTrue( - reopenLastClosedWindowMenuItem.waitForExistence(timeout: timeout), + reopenLastClosedWindowMenuItem.waitForExistence(timeout: elementExistenceTimeout), "The \"Reopen Last Closed Window\" menu item didn't appear in a reasonable timeframe." ) reopenLastClosedWindowMenuItem.click() XCTAssertTrue( - app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(titleOfFirstTabWhichShouldRestore)"].waitForExistence(timeout: elementExistenceTimeout), "Restored visited tab 1 wasn't available with the expected title in a reasonable timeframe." ) app.typeKey("w", modifierFlags: [.command]) XCTAssertTrue( - app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: timeout), + app.windows.webViews["\(titleOfSecondTabWhichShouldRestore)"].waitForExistence(timeout: elementExistenceTimeout), "Restored visited tab 2 wasn't available with the expected title in a reasonable timeframe." ) } } private extension BrowsingHistoryTests { - - func randomPageTitle(length: Int) -> String { - return String(UUID().uuidString.prefix(length)) - } - func locallyServedURL(pageTitle: String) -> URL { return URL.testsServer .appendingTestParameters(data: """ diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index e53a29e9c2..7c491e09a8 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -19,12 +19,12 @@ import XCTest class FindInPageTests: XCTestCase { - var app: XCUIApplication! - let timeout = 0.3 - var addressBarTextField: XCUIElement! - var loremIpsumWebView: XCUIElement! - var findInPageCloseButton: XCUIElement! - let minimumExpectedMatchingPixelsInFindHighlight = 150 + private var app: XCUIApplication! + private let elementExistenceTimeout = 0.3 + private var addressBarTextField: XCUIElement! + private var loremIpsumWebView: XCUIElement! + private var findInPageCloseButton: XCUIElement! + private let minimumExpectedMatchingPixelsInFindHighlight = 150 override class func setUp() { saveLocalHTML() @@ -46,171 +46,201 @@ class FindInPageTests: XCTestCase { } func test_findInPage_canBeOpenedWithKeyCommand() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) } func test_findInPage_canBeOpenedWithMenuBarItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) let findInPageMenuBarItem = app.menuItems["MainMenu.findInPage"] XCTAssertTrue( - findInPageMenuBarItem.waitForExistence(timeout: timeout), + findInPageMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find \"Find in Page\" main menu bar item in a reasonable timeframe." ) findInPageMenuBarItem.click() XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" via the menu items Edit->Find->\"Find in Page\", the elements of the \"Find in Page\" interface should exist." ) } func test_findInPage_canBeOpenedWithMoreOptionsMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) let optionsButton = app.windows.buttons["NavigationBarViewController.optionsButton"] - XCTAssertTrue(optionsButton.waitForExistence(timeout: timeout), "Couldn't find options item in a reasonable timeframe.") + XCTAssertTrue(optionsButton.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find options item in a reasonable timeframe.") optionsButton.click() let findInPageMoreOptionsMenuItem = app.menuItems["MoreOptionsMenu.findInPage"] XCTAssertTrue( - findInPageMoreOptionsMenuItem.waitForExistence(timeout: timeout), + findInPageMoreOptionsMenuItem.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find More Options \"Find in Page\" menu item in a reasonable timeframe." ) findInPageMoreOptionsMenuItem.click() XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" via the More Options \"Find in Page\" menu item, the elements of the \"Find in Page\" interface should exist." ) } func test_findInPage_canBeClosedWithEscape() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeKey(.escape, modifierFlags: []) XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), + findInPageCloseButton.waitForNonExistence(timeout: elementExistenceTimeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } func test_findInPage_canBeClosedWithShiftCommandF() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeKey("f", modifierFlags: [.command, .shift]) XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), + findInPageCloseButton.waitForNonExistence(timeout: elementExistenceTimeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } func test_findInPage_canBeClosedWithHideFindMenuItem() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) let findInPageDoneMenuBarItem = app.menuItems["MainMenu.findInPageDone"] XCTAssertTrue( - findInPageDoneMenuBarItem.waitForExistence(timeout: timeout), + findInPageDoneMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find \"Find in Page\" done main menu item in a reasonable timeframe." ) findInPageDoneMenuBarItem.click() XCTAssertTrue( - findInPageCloseButton.waitForNonExistence(timeout: timeout), + findInPageCloseButton.waitForNonExistence(timeout: elementExistenceTimeout), "After closing \"Find in Page\" with escape, the elements of the \"Find in Page\" interface should no longer exist." ) } func test_findInPage_showsCorrectNumberOfOccurrences() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) XCTAssertEqual(statusFieldTextContent, "1 of 4") // Note: this is not a localized test element, and it should have a localization strategy. } func test_findInPage_showsFocusAndOccurrenceHighlighting() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") @@ -225,20 +255,26 @@ class FindInPageTests: XCTestCase { } func test_findInPage_findNextGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") @@ -248,14 +284,14 @@ class FindInPageTests: XCTestCase { let findNextMenuBarItem = app.menuItems["MainMenu.findNext"] XCTAssertTrue( - findNextMenuBarItem.waitForExistence(timeout: timeout), + findNextMenuBarItem.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." ) findNextMenuBarItem.click() let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), + updatedStatusField.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." ) XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") @@ -278,20 +314,26 @@ class FindInPageTests: XCTestCase { } func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. XCTAssertEqual(statusFieldTextContent, "1 of 4", "Unexpected status field text content after a \"Find in Page\" operation.") @@ -300,7 +342,7 @@ class FindInPageTests: XCTestCase { let findHighlightPoints = Set(highlightedPixelsInFindScreenshot.map { $0.point }) // Coordinates of highlighted pixels in the find screenshot let findInPageNextButton = app.windows.buttons["FindInPageController.nextButton"] XCTAssertTrue( - findInPageNextButton.waitForExistence(timeout: timeout), + findInPageNextButton.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find \"Find Next\" main menu bar item in a reasonable timeframe." ) @@ -308,7 +350,7 @@ class FindInPageTests: XCTestCase { let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), + updatedStatusField.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." ) XCTAssertEqual(updatedStatusFieldTextContent, "2 of 4", "Unexpected status field text content after a \"Find Next\" operation.") @@ -330,20 +372,26 @@ class FindInPageTests: XCTestCase { } func test_findInPage_commandGGoesToNextOccurrence() throws { - XCTAssertTrue(addressBarTextField.waitForExistence(timeout: timeout), "The Address Bar text field does not exist when it is expected.") + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), + "The Address Bar text field did not exist when it was expected." + ) addressBarTextField.typeText("\(Self.loremIpsumFileURL.absoluteString)\r") XCTAssertTrue( - loremIpsumWebView.waitForExistence(timeout: timeout), + loremIpsumWebView.waitForExistence(timeout: elementExistenceTimeout), "Local \"Lorem Ipsum\" web page didn't load with the expected title in a reasonable timeframe." ) app.typeKey("f", modifierFlags: .command) XCTAssertTrue( - findInPageCloseButton.waitForExistence(timeout: timeout), + findInPageCloseButton.waitForExistence(timeout: elementExistenceTimeout), "After invoking \"Find in Page\" with command-f, the elements of the \"Find in Page\" interface should exist." ) app.typeText("maximus\r") let statusField = app.textFields["FindInPageController.statusField"] - XCTAssertTrue(statusField.waitForExistence(timeout: timeout), "Couldn't find \"Find in Page\" statusField in a reasonable timeframe.") + XCTAssertTrue( + statusField.waitForExistence(timeout: elementExistenceTimeout), + "Couldn't find \"Find in Page\" statusField in a reasonable timeframe." + ) let statusFieldTextContent = try XCTUnwrap(statusField.value as? String) // Note: the following is not a localized test element, but it should have a localization strategy. @@ -355,7 +403,7 @@ class FindInPageTests: XCTestCase { let updatedStatusField = app.textFields["FindInPageController.statusField"] let updatedStatusFieldTextContent = updatedStatusField.value as! String XCTAssertTrue( - updatedStatusField.waitForExistence(timeout: timeout), + updatedStatusField.waitForExistence(timeout: elementExistenceTimeout), "Couldn't find the updated \"Find in Page\" statusField in a reasonable timeframe." ) From 1384f3687210f29a91401c3e5b4e0cb01f755a7f Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:21:41 +0100 Subject: [PATCH 058/108] Add BookmarksBarTests --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++++ UITests/BookmarksBarTests.swift | 35 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 UITests/BookmarksBarTests.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index a5cb705ba7..83bf705ada 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3238,6 +3238,7 @@ EE66666F2B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; EE6666702B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; EE6666712B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; + EE707A7E2BAC69CC002DACA5 /* BookmarksBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE707A7D2BAC69CC002DACA5 /* BookmarksBarTests.swift */; }; EE7295E32A545B9A008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295E22A545B9A008C0991 /* NetworkProtection */; }; EE7295E72A545BBB008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295E62A545BBB008C0991 /* NetworkProtection */; }; EE7295E92A545BC4008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295E82A545BC4008C0991 /* NetworkProtection */; }; @@ -4664,6 +4665,7 @@ EE4F2C722BAAE50E00F2DA55 /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift"; sourceTree = ""; }; EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationsHostingViewController.swift; sourceTree = ""; }; + EE707A7D2BAC69CC002DACA5 /* BookmarksBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksBarTests.swift; sourceTree = ""; }; EE8BEF0D2BAAF5FA0093CB6D /* AutocompleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AutocompleteTests.swift; path = UITests/AutocompleteTests.swift; sourceTree = SOURCE_ROOT; }; EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNLocationViewModel.swift; sourceTree = ""; }; @@ -6421,6 +6423,7 @@ children = ( EE4F2C702BAAE50E00F2DA55 /* Common */, EE8BEF0D2BAAF5FA0093CB6D /* AutocompleteTests.swift */, + EE707A7D2BAC69CC002DACA5 /* BookmarksBarTests.swift */, EE4F2C6F2BAAE50D00F2DA55 /* BrowsingHistoryTests.swift */, EE4F2C722BAAE50E00F2DA55 /* FindInPageTests.swift */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, @@ -12021,6 +12024,7 @@ EE4F2C732BAAE50E00F2DA55 /* BrowsingHistoryTests.swift in Sources */, EE4F2C7D2BAAE5A900F2DA55 /* DataExtension.swift in Sources */, EE4F2C7A2BAAE57400F2DA55 /* TestsURLExtension.swift in Sources */, + EE707A7E2BAC69CC002DACA5 /* BookmarksBarTests.swift in Sources */, EE4F2C742BAAE50E00F2DA55 /* XCUIElementExtension.swift in Sources */, EE8BEF0E2BAAF5FB0093CB6D /* AutocompleteTests.swift in Sources */, ); diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift new file mode 100644 index 0000000000..5602f6f763 --- /dev/null +++ b/UITests/BookmarksBarTests.swift @@ -0,0 +1,35 @@ +// +// BookmarksBarTests.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Common +import XCTest + +class BookmarksBarTests: XCTestCase { + private var app: XCUIApplication! + + override func setUpWithError() throws { + continueAfterFailure = false + app = XCUIApplication() + + } + + func test_failingTest() throws { + XCTFail("Failing test") + } + +} From 91c6fb29bd1bf26059b327c0cae4bb471e3d27c2 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 21 Mar 2024 17:39:02 +0100 Subject: [PATCH 059/108] Adding accessibility identifiers --- DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift | 1 + DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift | 4 ++++ DuckDuckGo/Preferences/View/PreferencesSidebar.swift | 1 + 3 files changed, 6 insertions(+) diff --git a/DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift b/DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift index e298a6335e..b927a05751 100644 --- a/DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift +++ b/DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift @@ -82,6 +82,7 @@ final class BookmarksBarViewController: NSViewController { bookmarksBarCollectionView.collectionViewLayout = createCenteredCollectionViewLayout() view.postsFrameChangedNotifications = true + bookmarksBarCollectionView.setAccessibilityIdentifier("BookmarksBarViewController.bookmarksBarCollectionView") } private func addContextMenu() { diff --git a/DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift b/DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift index be30d68da2..a7b30dae90 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift @@ -115,15 +115,19 @@ extension Preferences { HStack { ToggleMenuItem(UserText.showBookmarksBarPreference, isOn: $model.showBookmarksBar) + .accessibilityIdentifier("Preferences.AppearanceView.showBookmarksBarPreferenceToggle") NSPopUpButtonView(selection: $model.bookmarksBarAppearance) { let button = NSPopUpButton() button.setContentHuggingPriority(.defaultHigh, for: .horizontal) + button.setAccessibilityIdentifier("Preferences.AppearanceView.showBookmarksBarPopUp") let alwaysOn = button.menu?.addItem(withTitle: UserText.showBookmarksBarAlways, action: nil, keyEquivalent: "") alwaysOn?.representedObject = BookmarksBarAppearance.alwaysOn + alwaysOn?.setAccessibilityIdentifier("Preferences.AppearanceView.showBookmarksBarAlways") let newTabOnly = button.menu?.addItem(withTitle: UserText.showBookmarksBarNewTabOnly, action: nil, keyEquivalent: "") newTabOnly?.representedObject = BookmarksBarAppearance.newTabOnly + newTabOnly?.setAccessibilityIdentifier("Preferences.AppearanceView.showBookmarksBarNewTabOnly") return button } diff --git a/DuckDuckGo/Preferences/View/PreferencesSidebar.swift b/DuckDuckGo/Preferences/View/PreferencesSidebar.swift index b6f65c0a44..1b234a1c49 100644 --- a/DuckDuckGo/Preferences/View/PreferencesSidebar.swift +++ b/DuckDuckGo/Preferences/View/PreferencesSidebar.swift @@ -66,6 +66,7 @@ extension Preferences { } } .buttonStyle(SidebarItemButtonStyle(isSelected: isSelected)) + .accessibilityIdentifier("PreferencesSidebar.\(pane.id.rawValue)Button") } } From adde31ec6244bcbdc377fed73a51e39b17e048f3 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 21 Mar 2024 17:39:13 +0100 Subject: [PATCH 060/108] First working test --- UITests/BookmarksBarTests.swift | 121 +++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index 5602f6f763..d39709c3f4 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -21,15 +21,132 @@ import XCTest class BookmarksBarTests: XCTestCase { private var app: XCUIApplication! + private let elementExistenceTimeout = 0.3 + private let pageTitle = String(UUID().uuidString.prefix(16)) + private var urlForBookmarksBar: URL! + private var settingsWindow: XCUIElement! + private var siteWindow: XCUIElement! + private var addBookmarkButton: XCUIElement! + private var resetBookMarksMenuItem: XCUIElement! + private var showBookmarksBarPreferenceToggle: XCUIElement! + private var showBookmarksBarPopup: XCUIElement! + private var showBookmarksBarAlways: XCUIElement! + private var showBookmarksBarNewTabOnly: XCUIElement! + private var bookmarksBarCollectionView: XCUIElement! override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() + addBookmarkButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] + showBookmarksBarPreferenceToggle = app.checkBoxes["Preferences.AppearanceView.showBookmarksBarPreferenceToggle"] + resetBookMarksMenuItem = app.menuItems["MainMenu.resetBookmarks"] + urlForBookmarksBar = locallyServedURL(pageTitle: pageTitle) + app.launch() + resetBookmarksAndAddOneBookmark() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows + openSettingsAndSetShowBookmarksBarToUnchecked() + settingsWindow = app.windows.containing(.checkBox, identifier: "Preferences.AppearanceView.showBookmarksBarPreferenceToggle").firstMatch + openSecondWindowAndVisitSite() + siteWindow = app.windows.containing(.webView, identifier: pageTitle).firstMatch + showBookmarksBarPopup = app.popUpButtons["Preferences.AppearanceView.showBookmarksBarPopUp"] + showBookmarksBarAlways = app.menuItems["Preferences.AppearanceView.showBookmarksBarAlways"] + showBookmarksBarNewTabOnly = app.collectionViews["Preferences.AppearanceView.showBookmarksBarNewTabOnly"] + bookmarksBarCollectionView = app.collectionViews["BookmarksBarViewController.bookmarksBarCollectionView"] } - func test_failingTest() throws { - XCTFail("Failing test") + func test_clickingShowBookmarksBarAlwaysShowCausesBookmarksBarToDynamicallyAppearOnWindow() throws { + settingsWindow.click() + let showBookmarksBarIsChecked = showBookmarksBarPreferenceToggle.value as? Bool + if showBookmarksBarIsChecked == false { + showBookmarksBarPreferenceToggle.click() + } + + XCTAssertTrue( + showBookmarksBarPopup.waitForExistence(timeout: elementExistenceTimeout), + "The \"show bookmarks bar\" popup button didn't become available in a reasonable timeframe." + ) + showBookmarksBarPopup.click() + + XCTAssertTrue( + showBookmarksBarAlways.waitForExistence(timeout: elementExistenceTimeout), + "The \"show bookmarks bar always\" button didn't become available in a reasonable timeframe." + ) + showBookmarksBarAlways.click() + siteWindow.click() + + XCTAssertTrue( + bookmarksBarCollectionView.waitForExistence(timeout: elementExistenceTimeout), + "The bookmarksBarCollectionView should exist on a website window when we have selected always show bookmarks bar in the settings" + ) + } +} + +private extension BookmarksBarTests { + func openSettingsAndSetShowBookmarksBarToUnchecked() { + app.typeKey(",", modifierFlags: [.command]) + + let settingsAppearanceButton = app.buttons["PreferencesSidebar.appearanceButton"] + XCTAssertTrue( + settingsAppearanceButton.waitForExistence(timeout: elementExistenceTimeout), + "The user settings appearance section button didn't become available in a reasonable timeframe." + ) + // TODO: this should just be a click() but there are states for this test where the first few clicks don't register here. + settingsAppearanceButton.click(forDuration: elementExistenceTimeout, thenDragTo: settingsAppearanceButton) + + XCTAssertTrue( + showBookmarksBarPreferenceToggle.waitForExistence(timeout: elementExistenceTimeout), + "The toggle for showing the bookmarks bar didn't become available in a reasonable timeframe." + ) + + let showBookmarksBarIsChecked = showBookmarksBarPreferenceToggle.value as? Bool + if showBookmarksBarIsChecked == true { + showBookmarksBarPreferenceToggle.click() + } + } + + func openSecondWindowAndVisitSite() { + app.typeKey("n", modifierFlags: [.command]) + app.typeText("\(locallyServedURL(pageTitle: pageTitle).absoluteString)\r") + } + + func locallyServedURL(pageTitle: String) -> URL { + return URL.testsServer + .appendingTestParameters(data: """ + + + \(pageTitle) + + +

Sample text

+ + + """.utf8data) } + func resetBookmarksAndAddOneBookmark() { + XCTAssertTrue( + resetBookMarksMenuItem.waitForExistence(timeout: elementExistenceTimeout), + "Reset bookmarks menu item didn't become available in a reasonable timeframe." + ) + + resetBookMarksMenuItem.click() + + let urlForBookmarks = locallyServedURL(pageTitle: pageTitle) + + app.typeText("\(urlForBookmarks.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews["\(pageTitle)"].waitForExistence(timeout: elementExistenceTimeout), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + + app.typeKey("d", modifierFlags: [.command]) // Bookmark the page + + XCTAssertTrue( + addBookmarkButton.waitForExistence(timeout: 1.0), + "Bookmark button didn't appear with the expected title in a reasonable timeframe." + ) + + addBookmarkButton.click() + } } From 5bb2b44d3ab502b00f02b5d36d02108269ae9fd6 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:35:05 +0100 Subject: [PATCH 061/108] All tests, one failing --- UITests/BookmarksBarTests.swift | 68 ++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index d39709c3f4..bec0a9f94b 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -51,7 +51,7 @@ class BookmarksBarTests: XCTestCase { siteWindow = app.windows.containing(.webView, identifier: pageTitle).firstMatch showBookmarksBarPopup = app.popUpButtons["Preferences.AppearanceView.showBookmarksBarPopUp"] showBookmarksBarAlways = app.menuItems["Preferences.AppearanceView.showBookmarksBarAlways"] - showBookmarksBarNewTabOnly = app.collectionViews["Preferences.AppearanceView.showBookmarksBarNewTabOnly"] + showBookmarksBarNewTabOnly = app.menuItems["Preferences.AppearanceView.showBookmarksBarNewTabOnly"] bookmarksBarCollectionView = app.collectionViews["BookmarksBarViewController.bookmarksBarCollectionView"] } @@ -80,6 +80,63 @@ class BookmarksBarTests: XCTestCase { "The bookmarksBarCollectionView should exist on a website window when we have selected always show bookmarks bar in the settings" ) } + + func test_ifShowBookmarksBarIsUncheckedWindowsAndTabsNeverShowBookmarksBar() throws { + // These tests begin in the state that show bookmarks bar is unchecked + + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows + app.typeKey("n", modifierFlags: [.command]) // Open new window + + XCTAssertTrue( // TODO: fatal error implicitly found nil while unwrapping an optional value + bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), + "The bookmarksBarCollectionView should not exist on a new window when we have unchecked \"show bookmarks bar\" in the settings" + ) + app.typeKey("t", modifierFlags: [.command]) // Open new tab + XCTAssertTrue( + bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), + "The bookmarksBarCollectionView should not exist on a new tab when we have unchecked \"show bookmarks bar\" in the settings" + ) + app.typeKey("l", modifierFlags: [.command]) // Get address bar focus + app.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( + bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), + "The bookmarksBarCollectionView should not exist on a new tab that has been directed to a site when we have unchecked \"show bookmarks bar\" in the settings" + ) + } + + func test_settingShowBookmarksBarNewTabShowsBookmarksBarOnNewTabOnlyUntilASiteIsLoadedThere() throws { + settingsWindow.click() + let showBookmarksBarIsChecked = showBookmarksBarPreferenceToggle.value as? Bool + if showBookmarksBarIsChecked == false { + showBookmarksBarPreferenceToggle.click() + } + + XCTAssertTrue( + showBookmarksBarPopup.waitForExistence(timeout: elementExistenceTimeout), + "The \"show bookmarks bar\" popup button didn't become available in a reasonable timeframe." + ) + showBookmarksBarPopup.click() + + XCTAssertTrue( + showBookmarksBarNewTabOnly.waitForExistence(timeout: elementExistenceTimeout), + "The \"show bookmarks bar always\" button didn't become available in a reasonable timeframe." + ) + showBookmarksBarNewTabOnly.click() + + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows + app.typeKey("n", modifierFlags: [.command]) // open one new window + XCTAssertTrue( + bookmarksBarCollectionView.waitForExistence(timeout: elementExistenceTimeout), + "The bookmarksBarCollectionView should exist on a new tab into which no site has been typed yet." + ) + app.typeKey("l", modifierFlags: [.command]) // Get address bar focus without addressing multiple address bars by identifier + app.typeText("\(urlForBookmarksBar.absoluteString)\r") + + XCTAssertTrue( + bookmarksBarCollectionView.waitForNonExistence(timeout: 10.0), + "The bookmarksBarCollectionView should not exist on a tab that has been directed to a site, and is no longer new, when we have selected show bookmarks bar \"new tab only\" in the settings" + ) + } } private extension BookmarksBarTests { @@ -107,7 +164,8 @@ private extension BookmarksBarTests { func openSecondWindowAndVisitSite() { app.typeKey("n", modifierFlags: [.command]) - app.typeText("\(locallyServedURL(pageTitle: pageTitle).absoluteString)\r") + app.typeKey("l", modifierFlags: [.command]) // Get address bar focus without addressing multiple address bars by identifier + app.typeText("\(urlForBookmarksBar.absoluteString)\r") } func locallyServedURL(pageTitle: String) -> URL { @@ -131,10 +189,8 @@ private extension BookmarksBarTests { ) resetBookMarksMenuItem.click() - - let urlForBookmarks = locallyServedURL(pageTitle: pageTitle) - - app.typeText("\(urlForBookmarks.absoluteString)\r") + app.typeKey("l", modifierFlags: [.command]) // Get address bar focus without addressing multiple address bars by identifier + app.typeText("\(urlForBookmarksBar.absoluteString)\r") XCTAssertTrue( app.windows.webViews["\(pageTitle)"].waitForExistence(timeout: elementExistenceTimeout), "Visited site didn't load with the expected title in a reasonable timeframe." From 14c8448fff935793098b17652aa829a0132ff879 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:04:24 +0100 Subject: [PATCH 062/108] Add URL extension for UI tests --- UITests/Common/UITestsURLExtension.swift | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 UITests/Common/UITestsURLExtension.swift diff --git a/UITests/Common/UITestsURLExtension.swift b/UITests/Common/UITestsURLExtension.swift new file mode 100644 index 0000000000..1ef7c8ab24 --- /dev/null +++ b/UITests/Common/UITestsURLExtension.swift @@ -0,0 +1,34 @@ +// +// UITestsURLExtension.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +import Foundation + +extension URL { + static func simpleServedPage(titled title: String) -> URL { + return Self.testsServer + .appendingTestParameters(data: """ + + + \(title) + + +

Sample text

+ + + """.utf8data) + } +} From 25dd363229c51c2017b0003d4b5f3dc8b6f34ebc Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:04:35 +0100 Subject: [PATCH 063/108] Add extension to project --- DuckDuckGo.xcodeproj/project.pbxproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 2b7cf5689d..136214f608 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3270,6 +3270,7 @@ EEC8EB402982CD550065AA39 /* JSAlertViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF53E172950CED5002D78F4 /* JSAlertViewModelTests.swift */; }; EECE10E529DD77E60044D027 /* FeatureFlag.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECE10E429DD77E60044D027 /* FeatureFlag.swift */; }; EECE10E629DD77E60044D027 /* FeatureFlag.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECE10E429DD77E60044D027 /* FeatureFlag.swift */; }; + EED350412BADA7CC009484E3 /* UITestsURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EED350402BADA7CC009484E3 /* UITestsURLExtension.swift */; }; EEDE50112BA360C80017F3C4 /* NetworkProtection+VPNAgentConvenienceInitializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEDE50102BA360C80017F3C4 /* NetworkProtection+VPNAgentConvenienceInitializers.swift */; }; EEDE50122BA360C80017F3C4 /* NetworkProtection+VPNAgentConvenienceInitializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEDE50102BA360C80017F3C4 /* NetworkProtection+VPNAgentConvenienceInitializers.swift */; }; EEF12E6F2A2111880023E6BF /* MacPacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF12E6D2A2111880023E6BF /* MacPacketTunnelProvider.swift */; }; @@ -4672,6 +4673,7 @@ EEC4A66C2B2C894F00F7C0AA /* VPNLocationPreferenceItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationPreferenceItemModel.swift; sourceTree = ""; }; EEC4A6702B2C90AB00F7C0AA /* VPNLocationPreferenceItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationPreferenceItem.swift; sourceTree = ""; }; EECE10E429DD77E60044D027 /* FeatureFlag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlag.swift; sourceTree = ""; }; + EED350402BADA7CC009484E3 /* UITestsURLExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITestsURLExtension.swift; sourceTree = ""; }; EEDE50102BA360C80017F3C4 /* NetworkProtection+VPNAgentConvenienceInitializers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkProtection+VPNAgentConvenienceInitializers.swift"; sourceTree = ""; }; EEF12E6D2A2111880023E6BF /* MacPacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacPacketTunnelProvider.swift; sourceTree = ""; }; EEF53E172950CED5002D78F4 /* JSAlertViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSAlertViewModelTests.swift; sourceTree = ""; }; @@ -8625,6 +8627,7 @@ EE4F2C702BAAE50E00F2DA55 /* Common */ = { isa = PBXGroup; children = ( + EED350402BADA7CC009484E3 /* UITestsURLExtension.swift */, EE4F2C712BAAE50E00F2DA55 /* XCUIElementExtension.swift */, ); path = Common; @@ -12017,6 +12020,7 @@ EE4F2C7C2BAAE5A200F2DA55 /* StringExtension.swift in Sources */, EE4F2C732BAAE50E00F2DA55 /* BrowsingHistoryTests.swift in Sources */, EE4F2C7D2BAAE5A900F2DA55 /* DataExtension.swift in Sources */, + EED350412BADA7CC009484E3 /* UITestsURLExtension.swift in Sources */, EE4F2C7A2BAAE57400F2DA55 /* TestsURLExtension.swift in Sources */, EE4F2C742BAAE50E00F2DA55 /* XCUIElementExtension.swift in Sources */, ); @@ -15011,4 +15015,4 @@ /* End XCVersionGroup section */ }; rootObject = AA585D76248FD31100E9A3E2 /* Project object */; -} \ No newline at end of file +} From 0a353414c4f81d4b31bd1693a59168988b00eade Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:05:40 +0100 Subject: [PATCH 064/108] Use extension for all simple locally-served pages --- UITests/BrowsingHistoryTests.swift | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 6832c34afd..66fc888e22 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -56,7 +56,7 @@ class BrowsingHistoryTests: XCTestCase { func test_visitedSiteIsAddedToRecentlyVisited() throws { let historyPageTitleExpectedToBeFirstInRecentlyVisited = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) - let url = locallyServedURL(pageTitle: historyPageTitleExpectedToBeFirstInRecentlyVisited) + let url = URL.simpleServedPage(titled: historyPageTitleExpectedToBeFirstInRecentlyVisited) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInRecentlyVisitedSection = app.menuItems["HistoryMenu.recentlyVisitedMenuItem.0"] XCTAssertTrue( @@ -84,7 +84,7 @@ class BrowsingHistoryTests: XCTestCase { func test_visitedSiteIsInHistoryAfterClosingAndReopeningWindow() throws { let historyPageTitleExpectedToBeFirstInTodayHistory = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) - let url = locallyServedURL(pageTitle: historyPageTitleExpectedToBeFirstInTodayHistory) + let url = URL.simpleServedPage(titled: historyPageTitleExpectedToBeFirstInTodayHistory) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] let firstSiteInHistory = app.menuItems["HistoryMenu.historyMenuItem.Today.0"] XCTAssertTrue( @@ -115,8 +115,8 @@ class BrowsingHistoryTests: XCTestCase { func test_lastClosedWindowsTabsCanBeReopenedViaHistory() throws { let titleOfFirstTabWhichShouldRestore = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let titleOfSecondTabWhichShouldRestore = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) - let urlForFirstTab = locallyServedURL(pageTitle: titleOfFirstTabWhichShouldRestore) - let urlForSecondTab = locallyServedURL(pageTitle: titleOfSecondTabWhichShouldRestore) + let urlForFirstTab = URL.simpleServedPage(titled: titleOfFirstTabWhichShouldRestore) + let urlForSecondTab = URL.simpleServedPage(titled: titleOfSecondTabWhichShouldRestore) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] XCTAssertTrue( addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), @@ -159,19 +159,3 @@ class BrowsingHistoryTests: XCTestCase { ) } } - -private extension BrowsingHistoryTests { - func locallyServedURL(pageTitle: String) -> URL { - return URL.testsServer - .appendingTestParameters(data: """ - - - \(pageTitle) - - -

Sample text

- - - """.utf8data) - } -} From 7ea6aa5ca30e3316cb864ae24a406b87695e2b03 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:17:01 +0100 Subject: [PATCH 065/108] Bring over find in page test name fixes --- UITests/FindInPageTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 7c491e09a8..714ee05300 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -254,7 +254,7 @@ class FindInPageTests: XCTestCase { ) } - func test_findInPage_findNextGoesToNextOccurrence() throws { + func test_findNext_menuItemGoesToNextOccurrence() throws { XCTAssertTrue( addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The Address Bar text field did not exist when it was expected." @@ -313,7 +313,7 @@ class FindInPageTests: XCTestCase { ) } - func test_findInPage_findNextNextArrowGoesToNextOccurrence() throws { + func test_findNext_nextArrowGoesToNextOccurrence() throws { XCTAssertTrue( addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The Address Bar text field did not exist when it was expected." @@ -371,7 +371,7 @@ class FindInPageTests: XCTestCase { ) } - func test_findInPage_commandGGoesToNextOccurrence() throws { + func test_findNext_commandGGoesToNextOccurrence() throws { XCTAssertTrue( addressBarTextField.waitForExistence(timeout: elementExistenceTimeout), "The Address Bar text field did not exist when it was expected." From 6a57cb1575eb2527464e1ae6c4038e81ab168e23 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:22:54 +0100 Subject: [PATCH 066/108] Fix test names --- UITests/BrowsingHistoryTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 66fc888e22..ee63e527ab 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -32,7 +32,7 @@ class BrowsingHistoryTests: XCTestCase { historyMenuBarItem = app.menuBarItems["History"] clearAllHistoryMenuItem = app.menuItems["HistoryMenu.clearAllHistory"] app.launch() - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Enforce a single window app.typeKey("n", modifierFlags: .command) XCTAssertTrue( @@ -51,10 +51,10 @@ class BrowsingHistoryTests: XCTestCase { app.buttons["ClearAllHistoryAndDataAlert.clearButton"].waitForExistence(timeout: elementExistenceTimeout), "Clear all history item didn't appear in a reasonable timeframe." ) - app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // And manually remove the history + app.buttons["ClearAllHistoryAndDataAlert.clearButton"].click() // Manually remove the history } - func test_visitedSiteIsAddedToRecentlyVisited() throws { + func test_recentlyVisited_showsLastVisitedSite() throws { let historyPageTitleExpectedToBeFirstInRecentlyVisited = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let url = URL.simpleServedPage(titled: historyPageTitleExpectedToBeFirstInRecentlyVisited) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] @@ -82,7 +82,7 @@ class BrowsingHistoryTests: XCTestCase { XCTAssertEqual(historyPageTitleExpectedToBeFirstInRecentlyVisited, firstSiteInRecentlyVisitedSection.title) } - func test_visitedSiteIsInHistoryAfterClosingAndReopeningWindow() throws { + func test_history_showsVisitedSiteAfterClosingAndReopeningWindow() throws { let historyPageTitleExpectedToBeFirstInTodayHistory = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let url = URL.simpleServedPage(titled: historyPageTitleExpectedToBeFirstInTodayHistory) let addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] @@ -112,7 +112,7 @@ class BrowsingHistoryTests: XCTestCase { XCTAssertEqual(historyPageTitleExpectedToBeFirstInTodayHistory, firstSiteInHistory.title) } - func test_lastClosedWindowsTabsCanBeReopenedViaHistory() throws { + func test_reopenLastClosedWindowMenuItem_canReopenTabsOfLastClosedWindow() throws { let titleOfFirstTabWhichShouldRestore = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let titleOfSecondTabWhichShouldRestore = String(UUID().uuidString.prefix(lengthForRandomPageTitle)) let urlForFirstTab = URL.simpleServedPage(titled: titleOfFirstTabWhichShouldRestore) From 6ea92d8220b263b1809236ad73826a297ece3973 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:12:22 +0100 Subject: [PATCH 067/108] Add files back into project to avoid pbxproj merge badness --- DuckDuckGo.xcodeproj/project.pbxproj | 37 +++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 53cb0bc1eb..9c1600745c 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3229,6 +3229,12 @@ EE0629742B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; EE0629752B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; EE0629762B90EE8C00D868B4 /* AccountManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */; }; + EE2833752BADE33000C4CCF4 /* BrowsingHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2833742BADE33000C4CCF4 /* BrowsingHistoryTests.swift */; }; + EE2833772BADE33F00C4CCF4 /* UITestsURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2833762BADE33F00C4CCF4 /* UITestsURLExtension.swift */; }; + EE2833782BADE35E00C4CCF4 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8EDF2624923EC70071C2E8 /* StringExtension.swift */; }; + EE2833792BADE36600C4CCF4 /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AC3AF625D5DBFD00C7D2AA /* DataExtension.swift */; }; + EE28337A2BADE37100C4CCF4 /* TestsURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EC37FB29B83E99001ACE79 /* TestsURLExtension.swift */; }; + EE28337E2BADE3A500C4CCF4 /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = EE28337D2BADE3A500C4CCF4 /* BrowserServicesKit */; }; EE2F9C5B2B90F2FF00D45FC9 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = EE2F9C5A2B90F2FF00D45FC9 /* Subscription */; }; EE339228291BDEFD009F62C1 /* JSAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE339227291BDEFD009F62C1 /* JSAlertController.swift */; }; EE3424602BA0853900173B1B /* VPNUninstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE34245D2BA0853900173B1B /* VPNUninstaller.swift */; }; @@ -3402,6 +3408,13 @@ remoteGlobalIDString = B6EC37E729B5DA2A001ACE79; remoteInfo = "tests-server"; }; + EE28337B2BADE39300C4CCF4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = AA585D76248FD31100E9A3E2 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B6EC37E729B5DA2A001ACE79; + remoteInfo = "tests-server"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -4661,6 +4674,8 @@ EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = clickToLoad.js; sourceTree = ""; }; EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; EE0629712B90EE8C00D868B4 /* AccountManagerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManagerExtension.swift; sourceTree = ""; }; + EE2833742BADE33000C4CCF4 /* BrowsingHistoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowsingHistoryTests.swift; sourceTree = ""; }; + EE2833762BADE33F00C4CCF4 /* UITestsURLExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITestsURLExtension.swift; sourceTree = ""; }; EE339227291BDEFD009F62C1 /* JSAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSAlertController.swift; sourceTree = ""; }; EE34245D2BA0853900173B1B /* VPNUninstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNUninstaller.swift; sourceTree = ""; }; EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift"; sourceTree = ""; }; @@ -4879,6 +4894,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + EE28337E2BADE3A500C4CCF4 /* BrowserServicesKit in Frameworks */, B64E42872B908501006C1346 /* SnapshotTesting in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -6424,8 +6440,9 @@ isa = PBXGroup; children = ( EEBCE6802BA444FA00B9DF00 /* Common */, - 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, + EE2833742BADE33000C4CCF4 /* BrowsingHistoryTests.swift */, EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, + 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, ); path = UITests; sourceTree = ""; @@ -8653,6 +8670,7 @@ EEBCE6802BA444FA00B9DF00 /* Common */ = { isa = PBXGroup; children = ( + EE2833762BADE33F00C4CCF4 /* UITestsURLExtension.swift */, EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */, ); path = Common; @@ -9067,11 +9085,13 @@ buildRules = ( ); dependencies = ( + EE28337C2BADE39300C4CCF4 /* PBXTargetDependency */, B6F997BD2B8F35EF00476735 /* PBXTargetDependency */, ); name = "UI Tests"; packageProductDependencies = ( B65CD8D22B316E1700A595BB /* SnapshotTesting */, + EE28337D2BADE3A500C4CCF4 /* BrowserServicesKit */, ); productName = "UI Tests"; productReference = 7B4CE8DA26F02108009134B1 /* UI Tests.xctest */; @@ -12036,7 +12056,12 @@ EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */, EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, + EE28337A2BADE37100C4CCF4 /* TestsURLExtension.swift in Sources */, + EE2833752BADE33000C4CCF4 /* BrowsingHistoryTests.swift in Sources */, + EE2833782BADE35E00C4CCF4 /* StringExtension.swift in Sources */, EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */, + EE2833772BADE33F00C4CCF4 /* UITestsURLExtension.swift in Sources */, + EE2833792BADE36600C4CCF4 /* DataExtension.swift in Sources */, EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -13268,6 +13293,11 @@ isa = PBXTargetDependency; productRef = B6F997C02B8F35F800476735 /* SwiftLintPlugin */; }; + EE28337C2BADE39300C4CCF4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B6EC37E729B5DA2A001ACE79 /* tests-server */; + targetProxy = EE28337B2BADE39300C4CCF4 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -14867,6 +14897,11 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; + EE28337D2BADE3A500C4CCF4 /* BrowserServicesKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = BrowserServicesKit; + }; EE2F9C5A2B90F2FF00D45FC9 /* Subscription */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; From 0784ee79bf6bd862eae39630f4c039816b1662b5 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:21:23 +0100 Subject: [PATCH 068/108] More clarity in makeMenuItems --- DuckDuckGo/Menus/HistoryMenu.swift | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/DuckDuckGo/Menus/HistoryMenu.swift b/DuckDuckGo/Menus/HistoryMenu.swift index 0a091ab6a4..77d744b307 100644 --- a/DuckDuckGo/Menus/HistoryMenu.swift +++ b/DuckDuckGo/Menus/HistoryMenu.swift @@ -121,13 +121,15 @@ final class HistoryMenu: NSMenu { private func addRecentlyVisited() { recentlyVisitedMenuItems = [recentlyVisitedHeaderMenuItem] + let recentVisits = historyCoordinator.getRecentVisits(maxCount: 14) - recentlyVisitedMenuItems.append(contentsOf: recentVisits - .map { - VisitMenuItem(visitViewModel: VisitViewModel(visit: $0)) - // An accessibility identifier with a negative index will cause the relevant UI tests to fail, correctly - .withAccessibilityIdentifier("HistoryMenu.recentlyVisitedMenuItem.\(recentVisits.firstIndex(of: $0) ?? -1)") - }) + for (index, visit) in zip( + recentVisits.indices, recentVisits + ) { + let visitMenuItem = VisitMenuItem(visitViewModel: VisitViewModel(visit: visit)) + visitMenuItem.setAccessibilityIdentifier("HistoryMenu.recentlyVisitedMenuItem.\(index)") + recentlyVisitedMenuItems.append(visitMenuItem) + } for recentlyVisitedMenuItem in recentlyVisitedMenuItems { addItem(recentlyVisitedMenuItem) } @@ -202,12 +204,15 @@ final class HistoryMenu: NSMenu { let date = grouping.date let isToday = NSCalendar.current.isDateInToday(date) let visits = grouping.visits - return visits.map { visit in - VisitMenuItem(visitViewModel: VisitViewModel(visit: visit)) - .withAccessibilityIdentifier( // An identifier with a negative index will cause the relevant UI tests to fail, correctly - "HistoryMenu.historyMenuItem.\(isToday ? "Today" : "\(date)").\(visits.firstIndex(of: visit) ?? -1)" - ) + var menuItems = [NSMenuItem]() + for (index, visit) in zip( + visits.indices, visits + ) { + let menuItem = VisitMenuItem(visitViewModel: VisitViewModel(visit: visit)) + menuItem.setAccessibilityIdentifier("HistoryMenu.historyMenuItem.\(isToday ? "Today" : "\(date)").\(index)") + menuItems.append(menuItem) } + return menuItems } private func makeTitle(for grouping: HistoryGrouping) -> (String, String) { From cd56bcbafe30c9fc26ede890ad02831c7a4ebbe0 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:33:21 +0100 Subject: [PATCH 069/108] Added UITEST_MODE env var --- UITests/BrowsingHistoryTests.swift | 1 + UITests/FindInPageTests.swift | 1 + UITests/TabBarTests.swift | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index ee63e527ab..97e2e19f7d 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -29,6 +29,7 @@ class BrowsingHistoryTests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() + app.launchEnvironment["UITEST_MODE"] = "1" historyMenuBarItem = app.menuBarItems["History"] clearAllHistoryMenuItem = app.menuItems["HistoryMenu.clearAllHistory"] app.launch() diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index 2b022313dd..68ada92e6a 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -37,6 +37,7 @@ class FindInPageTests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() + app.launchEnvironment["UITEST_MODE"] = "1" addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] loremIpsumWebView = app.windows.webViews["Lorem Ipsum"] findInPageCloseButton = app.windows.buttons["FindInPageController.closeButton"] diff --git a/UITests/TabBarTests.swift b/UITests/TabBarTests.swift index d1c70e925d..2480e2f71f 100644 --- a/UITests/TabBarTests.swift +++ b/UITests/TabBarTests.swift @@ -19,9 +19,13 @@ import XCTest class TabBarTests: XCTestCase { + private var app: XCUIApplication! + override func setUpWithError() throws { continueAfterFailure = false - XCUIApplication().launch() + app = XCUIApplication() + app.launchEnvironment["UITEST_MODE"] = "1" + app.launch() } func testWhenClickingAddTab_ThenTabsOpen() throws { From 5e9ac28706a3c37578723bddede190777bb8c278 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:40:38 +0100 Subject: [PATCH 070/108] Don't change menu items more than needed --- DuckDuckGo/Menus/HistoryMenu.swift | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/DuckDuckGo/Menus/HistoryMenu.swift b/DuckDuckGo/Menus/HistoryMenu.swift index 77d744b307..af12e210e5 100644 --- a/DuckDuckGo/Menus/HistoryMenu.swift +++ b/DuckDuckGo/Menus/HistoryMenu.swift @@ -31,13 +31,11 @@ final class HistoryMenu: NSMenu { private let reopenLastClosedMenuItem = NSMenuItem(title: UserText.reopenLastClosedTab, action: #selector(AppDelegate.reopenLastClosedTab)) private let reopenAllWindowsFromLastSessionMenuItem = NSMenuItem(title: UserText.mainMenuHistoryReopenAllWindowsFromLastSession, action: #selector(AppDelegate.reopenAllWindowsFromLastSession)) - private let clearAllHistoryMenuItem: NSMenuItem = { - let item = NSMenuItem(title: UserText.mainMenuHistoryClearAllHistory, - action: #selector(MainViewController.clearAllHistory), - keyEquivalent: [.command, .shift, .backspace]) - item.setAccessibilityIdentifier("HistoryMenu.clearAllHistory") - return item - }() + + private let clearAllHistoryMenuItem = NSMenuItem(title: UserText.mainMenuHistoryClearAllHistory, + action: #selector(MainViewController.clearAllHistory), + keyEquivalent: [.command, .shift, .backspace]) + .withAccessibilityIdentifier("HistoryMenu.clearAllHistory") private let clearAllHistorySeparator = NSMenuItem.separator() private let historyCoordinator: HistoryCoordinating From 4b71bc82e89581317b32d4bce541f1dfd4d89e82 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:46:26 +0100 Subject: [PATCH 071/108] Remove added linebreaks --- DuckDuckGo/Menus/HistoryMenu.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/DuckDuckGo/Menus/HistoryMenu.swift b/DuckDuckGo/Menus/HistoryMenu.swift index af12e210e5..b99c7a867f 100644 --- a/DuckDuckGo/Menus/HistoryMenu.swift +++ b/DuckDuckGo/Menus/HistoryMenu.swift @@ -31,7 +31,6 @@ final class HistoryMenu: NSMenu { private let reopenLastClosedMenuItem = NSMenuItem(title: UserText.reopenLastClosedTab, action: #selector(AppDelegate.reopenLastClosedTab)) private let reopenAllWindowsFromLastSessionMenuItem = NSMenuItem(title: UserText.mainMenuHistoryReopenAllWindowsFromLastSession, action: #selector(AppDelegate.reopenAllWindowsFromLastSession)) - private let clearAllHistoryMenuItem = NSMenuItem(title: UserText.mainMenuHistoryClearAllHistory, action: #selector(MainViewController.clearAllHistory), keyEquivalent: [.command, .shift, .backspace]) @@ -119,7 +118,6 @@ final class HistoryMenu: NSMenu { private func addRecentlyVisited() { recentlyVisitedMenuItems = [recentlyVisitedHeaderMenuItem] - let recentVisits = historyCoordinator.getRecentVisits(maxCount: 14) for (index, visit) in zip( recentVisits.indices, recentVisits From 8f007b8e94ffef4e5f32ecf6e7cb5a40a756e8eb Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 25 Mar 2024 15:56:33 +0100 Subject: [PATCH 072/108] Don't use utf8String in TestsURLExtension --- IntegrationTests/Common/TestsURLExtension.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IntegrationTests/Common/TestsURLExtension.swift b/IntegrationTests/Common/TestsURLExtension.swift index b6ae3eaf88..95da2cd8ca 100644 --- a/IntegrationTests/Common/TestsURLExtension.swift +++ b/IntegrationTests/Common/TestsURLExtension.swift @@ -54,7 +54,7 @@ extension URL { let value = URL(string: "/")!.appendingParameters(headers).query! url = url.appendingParameter(name: "headers", value: value) } - if let dataStr = data?.utf8String() { + if let data, let dataStr = String(data: data, encoding: .utf8) { url = url.appendingParameter(name: "data", value: dataStr) } else if let data { url = url.appendingParameter(name: "data", value: data.base64EncodedString()) From 5393069678f0435cac0e5e00436c342b773a172d Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:32:28 +0100 Subject: [PATCH 073/108] Remove unused DataExtension.swift from target --- DuckDuckGo.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 541a5317ab..5524c86f83 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3234,7 +3234,6 @@ EE2833752BADE33000C4CCF4 /* BrowsingHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2833742BADE33000C4CCF4 /* BrowsingHistoryTests.swift */; }; EE2833772BADE33F00C4CCF4 /* UITestsURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2833762BADE33F00C4CCF4 /* UITestsURLExtension.swift */; }; EE2833782BADE35E00C4CCF4 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8EDF2624923EC70071C2E8 /* StringExtension.swift */; }; - EE2833792BADE36600C4CCF4 /* DataExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AC3AF625D5DBFD00C7D2AA /* DataExtension.swift */; }; EE28337A2BADE37100C4CCF4 /* TestsURLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6EC37FB29B83E99001ACE79 /* TestsURLExtension.swift */; }; EE28337E2BADE3A500C4CCF4 /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = EE28337D2BADE3A500C4CCF4 /* BrowserServicesKit */; }; EE2F9C5B2B90F2FF00D45FC9 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = EE2F9C5A2B90F2FF00D45FC9 /* Subscription */; }; @@ -12074,7 +12073,6 @@ EE2833782BADE35E00C4CCF4 /* StringExtension.swift in Sources */, EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */, EE2833772BADE33F00C4CCF4 /* UITestsURLExtension.swift in Sources */, - EE2833792BADE36600C4CCF4 /* DataExtension.swift in Sources */, EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; From d95c05e3bd716f47b2841265e86b83f46bee7b85 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 25 Mar 2024 18:05:59 +0100 Subject: [PATCH 074/108] Remove unnecessary import --- UITests/BrowsingHistoryTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/UITests/BrowsingHistoryTests.swift b/UITests/BrowsingHistoryTests.swift index 97e2e19f7d..aec75db1fe 100644 --- a/UITests/BrowsingHistoryTests.swift +++ b/UITests/BrowsingHistoryTests.swift @@ -16,7 +16,6 @@ // limitations under the License. // -import Common import XCTest class BrowsingHistoryTests: XCTestCase { From 521a0f75550bc794055887c6bf327294259e2cb8 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:45:29 +0100 Subject: [PATCH 075/108] Re-add bookmark bar tests after safety-first pbxproj merge --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 59541b8ec8..2bec20d16b 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3253,6 +3253,7 @@ EEA3EEB12B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; EEA3EEB32B24EC0600E8333A /* VPNLocationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */; }; EEAD7A7C2A1D3E20002A24E7 /* AppLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */; }; + EEAF1A9A2BB2DEAF00788E61 /* BookmarksBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAF1A992BB2DEAF00788E61 /* BookmarksBarTests.swift */; }; EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */; }; EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B139AFC26B60BD800894F82 /* NSImageExtensions.swift */; }; EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC5E4E325D6BA9C007F5990 /* NSSizeExtension.swift */; }; @@ -4685,6 +4686,7 @@ EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNLocationViewModel.swift; sourceTree = ""; }; EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLauncher.swift; sourceTree = ""; }; + EEAF1A992BB2DEAF00788E61 /* BookmarksBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksBarTests.swift; sourceTree = ""; }; EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCUIElementExtension.swift; sourceTree = ""; }; EEC111E3294D06020086524F /* JSAlert.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = JSAlert.storyboard; sourceTree = ""; }; EEC111E5294D06290086524F /* JSAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSAlertViewModel.swift; sourceTree = ""; }; @@ -6450,6 +6452,7 @@ isa = PBXGroup; children = ( EEBCE6802BA444FA00B9DF00 /* Common */, + EEAF1A992BB2DEAF00788E61 /* BookmarksBarTests.swift */, EE2833742BADE33000C4CCF4 /* BrowsingHistoryTests.swift */, EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, @@ -12068,6 +12071,7 @@ EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */, EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, + EEAF1A9A2BB2DEAF00788E61 /* BookmarksBarTests.swift in Sources */, EE28337A2BADE37100C4CCF4 /* TestsURLExtension.swift in Sources */, EE2833752BADE33000C4CCF4 /* BrowsingHistoryTests.swift in Sources */, EE2833782BADE35E00C4CCF4 /* StringExtension.swift in Sources */, From 0d5ef8572b7531567dbd17031f5f5c42a6db2096 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:13:10 +0100 Subject: [PATCH 076/108] Add accessibility ID for other button on bookmark dialog --- .../Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift b/DuckDuckGo/Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift index d55f6dd34f..80e9320dc2 100644 --- a/DuckDuckGo/Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift +++ b/DuckDuckGo/Bookmarks/View/Dialog/BookmarkDialogButtonsView.swift @@ -40,7 +40,7 @@ struct BookmarkDialogButtonsView: View { Spacer() } - actionButton(action: otherButtonAction, viewState: viewState) + actionButton(action: otherButtonAction, viewState: viewState).accessibilityIdentifier("BookmarkDialogButtonsView.otherButton") actionButton(action: defaultButtonAction, viewState: viewState).accessibilityIdentifier("BookmarkDialogButtonsView.defaultButton") } From 4133f391835c662d4c5b294599c839aa5e190a93 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:13:45 +0100 Subject: [PATCH 077/108] Test improvements --- UITests/BookmarksBarTests.swift | 71 +++++++++++++++++---------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index bec0a9f94b..8ca173894c 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -21,12 +21,12 @@ import XCTest class BookmarksBarTests: XCTestCase { private var app: XCUIApplication! - private let elementExistenceTimeout = 0.3 - private let pageTitle = String(UUID().uuidString.prefix(16)) + private let elementExistenceTimeout = 2.0 + private let pageTitle = String(UUID().uuidString.prefix(12)) private var urlForBookmarksBar: URL! private var settingsWindow: XCUIElement! private var siteWindow: XCUIElement! - private var addBookmarkButton: XCUIElement! + private var defaultBookmarkDialogButton: XCUIElement! private var resetBookMarksMenuItem: XCUIElement! private var showBookmarksBarPreferenceToggle: XCUIElement! private var showBookmarksBarPopup: XCUIElement! @@ -37,7 +37,7 @@ class BookmarksBarTests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() - addBookmarkButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] + defaultBookmarkDialogButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] showBookmarksBarPreferenceToggle = app.checkBoxes["Preferences.AppearanceView.showBookmarksBarPreferenceToggle"] resetBookMarksMenuItem = app.menuItems["MainMenu.resetBookmarks"] @@ -55,8 +55,9 @@ class BookmarksBarTests: XCTestCase { bookmarksBarCollectionView = app.collectionViews["BookmarksBarViewController.bookmarksBarCollectionView"] } - func test_clickingShowBookmarksBarAlwaysShowCausesBookmarksBarToDynamicallyAppearOnWindow() throws { - settingsWindow.click() + func test_bookmarksBar_whenShowBookmarksBarAlwaysIsSelected_alwaysDynamicallyAppearsOnWindow() throws { + app.typeKey("`", modifierFlags: [.command]) // Swap windows + let showBookmarksBarIsChecked = showBookmarksBarPreferenceToggle.value as? Bool if showBookmarksBarIsChecked == false { showBookmarksBarPreferenceToggle.click() @@ -73,7 +74,8 @@ class BookmarksBarTests: XCTestCase { "The \"show bookmarks bar always\" button didn't become available in a reasonable timeframe." ) showBookmarksBarAlways.click() - siteWindow.click() + + app.typeKey("`", modifierFlags: [.command]) // Swap windows XCTAssertTrue( bookmarksBarCollectionView.waitForExistence(timeout: elementExistenceTimeout), @@ -81,31 +83,9 @@ class BookmarksBarTests: XCTestCase { ) } - func test_ifShowBookmarksBarIsUncheckedWindowsAndTabsNeverShowBookmarksBar() throws { - // These tests begin in the state that show bookmarks bar is unchecked - - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows - app.typeKey("n", modifierFlags: [.command]) // Open new window - - XCTAssertTrue( // TODO: fatal error implicitly found nil while unwrapping an optional value - bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should not exist on a new window when we have unchecked \"show bookmarks bar\" in the settings" - ) - app.typeKey("t", modifierFlags: [.command]) // Open new tab - XCTAssertTrue( - bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should not exist on a new tab when we have unchecked \"show bookmarks bar\" in the settings" - ) - app.typeKey("l", modifierFlags: [.command]) // Get address bar focus - app.typeText("\(urlForBookmarksBar.absoluteString)\r") - XCTAssertTrue( - bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should not exist on a new tab that has been directed to a site when we have unchecked \"show bookmarks bar\" in the settings" - ) - } + func test_bookmarksBar_whenShowBookmarksNewTabOnlyIsSelected_onlyAppearsOnANewTabUntilASiteIsLoaded() throws { + app.typeKey("`", modifierFlags: [.command]) // Swap windows - func test_settingShowBookmarksBarNewTabShowsBookmarksBarOnNewTabOnlyUntilASiteIsLoadedThere() throws { - settingsWindow.click() let showBookmarksBarIsChecked = showBookmarksBarPreferenceToggle.value as? Bool if showBookmarksBarIsChecked == false { showBookmarksBarPreferenceToggle.click() @@ -133,10 +113,33 @@ class BookmarksBarTests: XCTestCase { app.typeText("\(urlForBookmarksBar.absoluteString)\r") XCTAssertTrue( - bookmarksBarCollectionView.waitForNonExistence(timeout: 10.0), + bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), "The bookmarksBarCollectionView should not exist on a tab that has been directed to a site, and is no longer new, when we have selected show bookmarks bar \"new tab only\" in the settings" ) } + + func test_bookmarksBar_whenShowBookmarksBarIsUnchecked_isNeverShownInWindowsAndTabs() throws { + // This tests begins in the state that "show bookmarks bar" is unchecked, so that isn't set within the test + + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows + app.typeKey("n", modifierFlags: [.command]) // Open new window + + XCTAssertTrue( + bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), + "The bookmarksBarCollectionView should not exist on a new window when we have unchecked \"show bookmarks bar\" in the settings" + ) + app.typeKey("t", modifierFlags: [.command]) // Open new tab + XCTAssertTrue( + bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), + "The bookmarksBarCollectionView should not exist on a new tab when we have unchecked \"show bookmarks bar\" in the settings" + ) + app.typeKey("l", modifierFlags: [.command]) // Get address bar focus + app.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( + bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), + "The bookmarksBarCollectionView should not exist on a new tab that has been directed to a site when we have unchecked \"show bookmarks bar\" in the settings" + ) + } } private extension BookmarksBarTests { @@ -199,10 +202,10 @@ private extension BookmarksBarTests { app.typeKey("d", modifierFlags: [.command]) // Bookmark the page XCTAssertTrue( - addBookmarkButton.waitForExistence(timeout: 1.0), + defaultBookmarkDialogButton.waitForExistence(timeout: elementExistenceTimeout), "Bookmark button didn't appear with the expected title in a reasonable timeframe." ) - addBookmarkButton.click() + defaultBookmarkDialogButton.click() } } From ce1f84215a149dbc40553a4b8c44e16dd391c7f0 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 09:20:56 +0100 Subject: [PATCH 078/108] Commit fixes for test scheme building review --- .../xcshareddata/xcschemes/UI Tests.xcscheme | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/UI Tests.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/UI Tests.xcscheme index ce215006ce..5ded4d9621 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/UI Tests.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/UI Tests.xcscheme @@ -23,9 +23,9 @@ From a09ef6b2eda789d29ca7ba4374435f387596de64 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 09:21:59 +0100 Subject: [PATCH 079/108] Update tests to current style --- UITests/BookmarksBarTests.swift | 74 +++++++++++++-------------------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index 8ca173894c..f7edd65d69 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -21,7 +21,6 @@ import XCTest class BookmarksBarTests: XCTestCase { private var app: XCUIApplication! - private let elementExistenceTimeout = 2.0 private let pageTitle = String(UUID().uuidString.prefix(12)) private var urlForBookmarksBar: URL! private var settingsWindow: XCUIElement! @@ -37,11 +36,11 @@ class BookmarksBarTests: XCTestCase { override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() + app.launchEnvironment["UITEST_MODE"] = "1" defaultBookmarkDialogButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] - showBookmarksBarPreferenceToggle = app.checkBoxes["Preferences.AppearanceView.showBookmarksBarPreferenceToggle"] resetBookMarksMenuItem = app.menuItems["MainMenu.resetBookmarks"] - urlForBookmarksBar = locallyServedURL(pageTitle: pageTitle) + urlForBookmarksBar = UITests.simpleServedPage(titled: pageTitle) app.launch() resetBookmarksAndAddOneBookmark() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows @@ -64,22 +63,22 @@ class BookmarksBarTests: XCTestCase { } XCTAssertTrue( - showBookmarksBarPopup.waitForExistence(timeout: elementExistenceTimeout), - "The \"show bookmarks bar\" popup button didn't become available in a reasonable timeframe." + showBookmarksBarPopup.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Show Bookmarks Bar\" popup button didn't become available in a reasonable timeframe." ) showBookmarksBarPopup.click() XCTAssertTrue( - showBookmarksBarAlways.waitForExistence(timeout: elementExistenceTimeout), - "The \"show bookmarks bar always\" button didn't become available in a reasonable timeframe." + showBookmarksBarAlways.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Show Bookmarks Bar Always\" button didn't become available in a reasonable timeframe." ) showBookmarksBarAlways.click() app.typeKey("`", modifierFlags: [.command]) // Swap windows XCTAssertTrue( - bookmarksBarCollectionView.waitForExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should exist on a website window when we have selected always show bookmarks bar in the settings" + bookmarksBarCollectionView.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarksBarCollectionView should exist on a website window when we have selected \"Always Show Bookmarks Bar\" in the settings" ) } @@ -92,29 +91,29 @@ class BookmarksBarTests: XCTestCase { } XCTAssertTrue( - showBookmarksBarPopup.waitForExistence(timeout: elementExistenceTimeout), - "The \"show bookmarks bar\" popup button didn't become available in a reasonable timeframe." + showBookmarksBarPopup.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Show Bookmarks Bar\" popup button didn't become available in a reasonable timeframe." ) showBookmarksBarPopup.click() XCTAssertTrue( - showBookmarksBarNewTabOnly.waitForExistence(timeout: elementExistenceTimeout), - "The \"show bookmarks bar always\" button didn't become available in a reasonable timeframe." + showBookmarksBarNewTabOnly.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Show Bookmarks Bar Always\" button didn't become available in a reasonable timeframe." ) showBookmarksBarNewTabOnly.click() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows app.typeKey("n", modifierFlags: [.command]) // open one new window XCTAssertTrue( - bookmarksBarCollectionView.waitForExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should exist on a new tab into which no site has been typed yet." + bookmarksBarCollectionView.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarksBarCollectionView should exist on a new tab into which no site name or location has been typed yet." ) app.typeKey("l", modifierFlags: [.command]) // Get address bar focus without addressing multiple address bars by identifier app.typeText("\(urlForBookmarksBar.absoluteString)\r") XCTAssertTrue( - bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should not exist on a tab that has been directed to a site, and is no longer new, when we have selected show bookmarks bar \"new tab only\" in the settings" + bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarksBarCollectionView should not exist on a tab that has been directed to a site, and is no longer new, when we have selected show bookmarks bar \"New Tab Only\" in the settings" ) } @@ -125,19 +124,19 @@ class BookmarksBarTests: XCTestCase { app.typeKey("n", modifierFlags: [.command]) // Open new window XCTAssertTrue( - bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should not exist on a new window when we have unchecked \"show bookmarks bar\" in the settings" + bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarksBarCollectionView should not exist on a new window when we have unchecked \"Show Bookmarks Bar\" in the settings" ) app.typeKey("t", modifierFlags: [.command]) // Open new tab XCTAssertTrue( - bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should not exist on a new tab when we have unchecked \"show bookmarks bar\" in the settings" + bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarksBarCollectionView should not exist on a new tab when we have unchecked \"Show Bookmarks Bar\" in the settings" ) app.typeKey("l", modifierFlags: [.command]) // Get address bar focus app.typeText("\(urlForBookmarksBar.absoluteString)\r") XCTAssertTrue( - bookmarksBarCollectionView.waitForNonExistence(timeout: elementExistenceTimeout), - "The bookmarksBarCollectionView should not exist on a new tab that has been directed to a site when we have unchecked \"show bookmarks bar\" in the settings" + bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarksBarCollectionView should not exist on a new tab that has been directed to a site when we have unchecked \"Show Bookmarks Bar\" in the settings" ) } } @@ -148,14 +147,14 @@ private extension BookmarksBarTests { let settingsAppearanceButton = app.buttons["PreferencesSidebar.appearanceButton"] XCTAssertTrue( - settingsAppearanceButton.waitForExistence(timeout: elementExistenceTimeout), + settingsAppearanceButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The user settings appearance section button didn't become available in a reasonable timeframe." ) - // TODO: this should just be a click() but there are states for this test where the first few clicks don't register here. - settingsAppearanceButton.click(forDuration: elementExistenceTimeout, thenDragTo: settingsAppearanceButton) + // This should just be a click(), but there are states for this test where the first few clicks don't register here. + settingsAppearanceButton.click(forDuration: UITests.Timeouts.elementExistence, thenDragTo: settingsAppearanceButton) XCTAssertTrue( - showBookmarksBarPreferenceToggle.waitForExistence(timeout: elementExistenceTimeout), + showBookmarksBarPreferenceToggle.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The toggle for showing the bookmarks bar didn't become available in a reasonable timeframe." ) @@ -171,23 +170,10 @@ private extension BookmarksBarTests { app.typeText("\(urlForBookmarksBar.absoluteString)\r") } - func locallyServedURL(pageTitle: String) -> URL { - return URL.testsServer - .appendingTestParameters(data: """ - - - \(pageTitle) - - -

Sample text

- - - """.utf8data) - } - func resetBookmarksAndAddOneBookmark() { + app.typeKey("n", modifierFlags: [.command]) // Can't use debug menu without a window XCTAssertTrue( - resetBookMarksMenuItem.waitForExistence(timeout: elementExistenceTimeout), + resetBookMarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), "Reset bookmarks menu item didn't become available in a reasonable timeframe." ) @@ -195,14 +181,14 @@ private extension BookmarksBarTests { app.typeKey("l", modifierFlags: [.command]) // Get address bar focus without addressing multiple address bars by identifier app.typeText("\(urlForBookmarksBar.absoluteString)\r") XCTAssertTrue( - app.windows.webViews["\(pageTitle)"].waitForExistence(timeout: elementExistenceTimeout), + app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), "Visited site didn't load with the expected title in a reasonable timeframe." ) app.typeKey("d", modifierFlags: [.command]) // Bookmark the page XCTAssertTrue( - defaultBookmarkDialogButton.waitForExistence(timeout: elementExistenceTimeout), + defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), "Bookmark button didn't appear with the expected title in a reasonable timeframe." ) From eae14a7e4cc6092240ada3627bd0a721e54461e2 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 09:22:12 +0100 Subject: [PATCH 080/108] Add tests back in after safety-first merge --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 7e472b8d99..18fa1ea414 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3245,6 +3245,7 @@ EE7295E92A545BC4008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295E82A545BC4008C0991 /* NetworkProtection */; }; EE7295ED2A545C0A008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295EC2A545C0A008C0991 /* NetworkProtection */; }; EE7295EF2A545C12008C0991 /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE7295EE2A545C12008C0991 /* NetworkProtection */; }; + EE7F74912BB5D76600CD9456 /* BookmarksBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE7F74902BB5D76600CD9456 /* BookmarksBarTests.swift */; }; EEA3EEB12B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; EEA3EEB32B24EC0600E8333A /* VPNLocationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */; }; EEAD7A7C2A1D3E20002A24E7 /* AppLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */; }; @@ -4677,6 +4678,7 @@ EE34245D2BA0853900173B1B /* VPNUninstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNUninstaller.swift; sourceTree = ""; }; EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift"; sourceTree = ""; }; EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationsHostingViewController.swift; sourceTree = ""; }; + EE7F74902BB5D76600CD9456 /* BookmarksBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksBarTests.swift; sourceTree = ""; }; EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNLocationViewModel.swift; sourceTree = ""; }; EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLauncher.swift; sourceTree = ""; }; @@ -6446,6 +6448,7 @@ children = ( EEBCE6802BA444FA00B9DF00 /* Common */, EED735352BB46B6000F173D6 /* AutocompleteTests.swift */, + EE7F74902BB5D76600CD9456 /* BookmarksBarTests.swift */, EE02D41B2BB460A600DBE6B3 /* BrowsingHistoryTests.swift */, EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, @@ -12058,6 +12061,7 @@ buildActionMask = 2147483647; files = ( EEBCE6842BA4643200B9DF00 /* NSSizeExtension.swift in Sources */, + EE7F74912BB5D76600CD9456 /* BookmarksBarTests.swift in Sources */, EE02D41C2BB460A600DBE6B3 /* BrowsingHistoryTests.swift in Sources */, EE02D41A2BB4609900DBE6B3 /* UITests.swift in Sources */, EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, From 80a3050a7dc46d5877133feab558d609f9a451ab Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 09:24:27 +0100 Subject: [PATCH 081/108] Make pixel matching more forgiving --- UITests/FindInPageTests.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index f3337f0cc7..fe1418fe50 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -469,6 +469,21 @@ private extension FindInPageTests { } } +private extension UInt8 { + func isCloseTo(_ integer: UInt8) -> Bool { + // Overflow-safe creation of range +/- 1 around value + let lowerBound: UInt8 = self != 0 ? self &- 1 : 0 + let upperBound: UInt8 = self != 255 ? self &+ 1 : 255 + + switch integer { + case lowerBound ... upperBound: + return true + default: + return false + } + } +} + private extension NSImage { /// Find matching pixels in an NSImage for a specific NSColor /// - Parameter colorToMatch: the NSColor to match @@ -506,7 +521,9 @@ private extension NSImage { bitmapData = bitmapData.advanced(by: 1) alphaInImage = bitmapData.pointee bitmapData = bitmapData.advanced(by: 1) - if redInImage == redToMatch, greenInImage == greenToMatch, blueInImage == blueToMatch { // We aren't matching alpha + if redInImage.isCloseTo(redToMatch), greenInImage.isCloseTo(greenToMatch), + blueInImage.isCloseTo(blueToMatch) + { // We aren't matching alpha pixels.append(Pixel( red: redInImage, green: greenInImage, From 559967b05da8655015b262b991afc55207d34739 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 10:21:51 +0100 Subject: [PATCH 082/108] Make setup easier to follow, arrange act assert spacing --- UITests/BookmarksBarTests.swift | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index f7edd65d69..9a2502eda0 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -40,6 +40,10 @@ class BookmarksBarTests: XCTestCase { defaultBookmarkDialogButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] showBookmarksBarPreferenceToggle = app.checkBoxes["Preferences.AppearanceView.showBookmarksBarPreferenceToggle"] resetBookMarksMenuItem = app.menuItems["MainMenu.resetBookmarks"] + showBookmarksBarPopup = app.popUpButtons["Preferences.AppearanceView.showBookmarksBarPopUp"] + showBookmarksBarAlways = app.menuItems["Preferences.AppearanceView.showBookmarksBarAlways"] + showBookmarksBarNewTabOnly = app.menuItems["Preferences.AppearanceView.showBookmarksBarNewTabOnly"] + bookmarksBarCollectionView = app.collectionViews["BookmarksBarViewController.bookmarksBarCollectionView"] urlForBookmarksBar = UITests.simpleServedPage(titled: pageTitle) app.launch() resetBookmarksAndAddOneBookmark() @@ -48,32 +52,25 @@ class BookmarksBarTests: XCTestCase { settingsWindow = app.windows.containing(.checkBox, identifier: "Preferences.AppearanceView.showBookmarksBarPreferenceToggle").firstMatch openSecondWindowAndVisitSite() siteWindow = app.windows.containing(.webView, identifier: pageTitle).firstMatch - showBookmarksBarPopup = app.popUpButtons["Preferences.AppearanceView.showBookmarksBarPopUp"] - showBookmarksBarAlways = app.menuItems["Preferences.AppearanceView.showBookmarksBarAlways"] - showBookmarksBarNewTabOnly = app.menuItems["Preferences.AppearanceView.showBookmarksBarNewTabOnly"] - bookmarksBarCollectionView = app.collectionViews["BookmarksBarViewController.bookmarksBarCollectionView"] } func test_bookmarksBar_whenShowBookmarksBarAlwaysIsSelected_alwaysDynamicallyAppearsOnWindow() throws { app.typeKey("`", modifierFlags: [.command]) // Swap windows - let showBookmarksBarIsChecked = showBookmarksBarPreferenceToggle.value as? Bool if showBookmarksBarIsChecked == false { showBookmarksBarPreferenceToggle.click() } - XCTAssertTrue( showBookmarksBarPopup.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The \"Show Bookmarks Bar\" popup button didn't become available in a reasonable timeframe." ) showBookmarksBarPopup.click() - XCTAssertTrue( showBookmarksBarAlways.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The \"Show Bookmarks Bar Always\" button didn't become available in a reasonable timeframe." ) + showBookmarksBarAlways.click() - app.typeKey("`", modifierFlags: [.command]) // Swap windows XCTAssertTrue( @@ -84,12 +81,10 @@ class BookmarksBarTests: XCTestCase { func test_bookmarksBar_whenShowBookmarksNewTabOnlyIsSelected_onlyAppearsOnANewTabUntilASiteIsLoaded() throws { app.typeKey("`", modifierFlags: [.command]) // Swap windows - let showBookmarksBarIsChecked = showBookmarksBarPreferenceToggle.value as? Bool if showBookmarksBarIsChecked == false { showBookmarksBarPreferenceToggle.click() } - XCTAssertTrue( showBookmarksBarPopup.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The \"Show Bookmarks Bar\" popup button didn't become available in a reasonable timeframe." @@ -101,7 +96,6 @@ class BookmarksBarTests: XCTestCase { "The \"Show Bookmarks Bar Always\" button didn't become available in a reasonable timeframe." ) showBookmarksBarNewTabOnly.click() - app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows app.typeKey("n", modifierFlags: [.command]) // open one new window XCTAssertTrue( @@ -122,11 +116,11 @@ class BookmarksBarTests: XCTestCase { app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows app.typeKey("n", modifierFlags: [.command]) // Open new window - XCTAssertTrue( bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), "The bookmarksBarCollectionView should not exist on a new window when we have unchecked \"Show Bookmarks Bar\" in the settings" ) + app.typeKey("t", modifierFlags: [.command]) // Open new tab XCTAssertTrue( bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), @@ -134,6 +128,7 @@ class BookmarksBarTests: XCTestCase { ) app.typeKey("l", modifierFlags: [.command]) // Get address bar focus app.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), "The bookmarksBarCollectionView should not exist on a new tab that has been directed to a site when we have unchecked \"Show Bookmarks Bar\" in the settings" From 957b281393c7884269503df02c94ac4b878f0fa6 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 10:29:18 +0100 Subject: [PATCH 083/108] SwiftFormat --- UITests/BookmarksBarTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index 9a2502eda0..4ef6347b88 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -69,7 +69,7 @@ class BookmarksBarTests: XCTestCase { showBookmarksBarAlways.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The \"Show Bookmarks Bar Always\" button didn't become available in a reasonable timeframe." ) - + showBookmarksBarAlways.click() app.typeKey("`", modifierFlags: [.command]) // Swap windows @@ -120,7 +120,7 @@ class BookmarksBarTests: XCTestCase { bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), "The bookmarksBarCollectionView should not exist on a new window when we have unchecked \"Show Bookmarks Bar\" in the settings" ) - + app.typeKey("t", modifierFlags: [.command]) // Open new tab XCTAssertTrue( bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), @@ -128,7 +128,7 @@ class BookmarksBarTests: XCTestCase { ) app.typeKey("l", modifierFlags: [.command]) // Get address bar focus app.typeText("\(urlForBookmarksBar.absoluteString)\r") - + XCTAssertTrue( bookmarksBarCollectionView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), "The bookmarksBarCollectionView should not exist on a new tab that has been directed to a site when we have unchecked \"Show Bookmarks Bar\" in the settings" From 5fbabe1c8d94c1766e41f2bebd3bf033973c2ce2 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 10:39:26 +0100 Subject: [PATCH 084/108] Keeps reappearing --- UITests/Common/UITestsURLExtension.swift | 34 ------------------------ 1 file changed, 34 deletions(-) delete mode 100644 UITests/Common/UITestsURLExtension.swift diff --git a/UITests/Common/UITestsURLExtension.swift b/UITests/Common/UITestsURLExtension.swift deleted file mode 100644 index 1ef7c8ab24..0000000000 --- a/UITests/Common/UITestsURLExtension.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// UITestsURLExtension.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -import Foundation - -extension URL { - static func simpleServedPage(titled title: String) -> URL { - return Self.testsServer - .appendingTestParameters(data: """ - - - \(title) - - -

Sample text

- - - """.utf8data) - } -} From af2e3d029c41af89fd3efced8355e32d05124a3e Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 11:11:06 +0100 Subject: [PATCH 085/108] Better naming --- UITests/FindInPageTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UITests/FindInPageTests.swift b/UITests/FindInPageTests.swift index fe1418fe50..edb0c40276 100644 --- a/UITests/FindInPageTests.swift +++ b/UITests/FindInPageTests.swift @@ -470,12 +470,12 @@ private extension FindInPageTests { } private extension UInt8 { - func isCloseTo(_ integer: UInt8) -> Bool { + func isCloseTo(_ colorValue: UInt8) -> Bool { // Overflow-safe creation of range +/- 1 around value let lowerBound: UInt8 = self != 0 ? self &- 1 : 0 let upperBound: UInt8 = self != 255 ? self &+ 1 : 255 - switch integer { + switch colorValue { case lowerBound ... upperBound: return true default: From 5df0aa9b41bdbe1c6f34d0d335708cb727fc104e Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 11:28:39 +0100 Subject: [PATCH 086/108] Remove extraneous unwraps --- UITests/AutocompleteTests.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/UITests/AutocompleteTests.swift b/UITests/AutocompleteTests.swift index 2b02369b58..04507caa71 100644 --- a/UITests/AutocompleteTests.swift +++ b/UITests/AutocompleteTests.swift @@ -54,7 +54,6 @@ class AutocompleteTests: XCTestCase { } func test_suggestions_showsTypedTitleOfBookmarkedPageAsBookmark() throws { - let siteTitleForBookmarkedSite = try XCTUnwrap(siteTitleForBookmarkedSite) app.typeText(siteTitleForBookmarkedSite) XCTAssertTrue( suggestionsTableView.waitForExistence(timeout: UITests.Timeouts.elementExistence), @@ -82,7 +81,6 @@ class AutocompleteTests: XCTestCase { } func test_suggestions_showsTypedTitleOfHistoryPageAsHistory() throws { - let siteTitleForHistorySite = try XCTUnwrap(siteTitleForHistorySite) app.typeText(siteTitleForHistorySite) XCTAssertTrue( suggestionsTableView.waitForExistence(timeout: UITests.Timeouts.elementExistence), @@ -145,9 +143,6 @@ class AutocompleteTests: XCTestCase { private extension AutocompleteTests { /// Make sure there is exactly one site in the history, and exactly one site in the bookmarks, and they aren't the same site. func resetAndArrangeBookmarksAndHistory() throws { - let siteTitleForHistorySite = try XCTUnwrap(siteTitleForHistorySite) - let siteTitleForBookmarkedSite = try XCTUnwrap(siteTitleForBookmarkedSite) - XCTAssertTrue( resetBookMarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), "Reset bookmarks menu item didn't become available in a reasonable timeframe." From 9bec32d032eb3916b3cf35877a89c5f37b007ddf Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 11:28:58 +0100 Subject: [PATCH 087/108] Standardize title string creation --- UITests/BookmarksBarTests.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index 4ef6347b88..e865bd18ba 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -21,7 +21,7 @@ import XCTest class BookmarksBarTests: XCTestCase { private var app: XCUIApplication! - private let pageTitle = String(UUID().uuidString.prefix(12)) + private var pageTitle: String! private var urlForBookmarksBar: URL! private var settingsWindow: XCUIElement! private var siteWindow: XCUIElement! @@ -32,6 +32,7 @@ class BookmarksBarTests: XCTestCase { private var showBookmarksBarAlways: XCUIElement! private var showBookmarksBarNewTabOnly: XCUIElement! private var bookmarksBarCollectionView: XCUIElement! + private let titleStringLength = 12 override func setUpWithError() throws { continueAfterFailure = false @@ -44,6 +45,7 @@ class BookmarksBarTests: XCTestCase { showBookmarksBarAlways = app.menuItems["Preferences.AppearanceView.showBookmarksBarAlways"] showBookmarksBarNewTabOnly = app.menuItems["Preferences.AppearanceView.showBookmarksBarNewTabOnly"] bookmarksBarCollectionView = app.collectionViews["BookmarksBarViewController.bookmarksBarCollectionView"] + pageTitle = UITests.randomPageTitle(length: titleStringLength) urlForBookmarksBar = UITests.simpleServedPage(titled: pageTitle) app.launch() resetBookmarksAndAddOneBookmark() From 8ad4d66b4e3c0a7f79b70d939e927eca1e97c476 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 14:50:55 +0100 Subject: [PATCH 088/108] Add tests --- DuckDuckGo.xcodeproj/project.pbxproj | 4 + UITests/BookmarksAndFavoritesTests.swift | 100 +++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 UITests/BookmarksAndFavoritesTests.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 18fa1ea414..ad7f4577e9 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3248,6 +3248,7 @@ EE7F74912BB5D76600CD9456 /* BookmarksBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE7F74902BB5D76600CD9456 /* BookmarksBarTests.swift */; }; EEA3EEB12B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */; }; EEA3EEB32B24EC0600E8333A /* VPNLocationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */; }; + EEA90B3B2BB6DB670009C62F /* BookmarksAndFavoritesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEA90B3A2BB6DB670009C62F /* BookmarksAndFavoritesTests.swift */; }; EEAD7A7C2A1D3E20002A24E7 /* AppLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */; }; EEBCE6822BA444FA00B9DF00 /* XCUIElementExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */; }; EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B139AFC26B60BD800894F82 /* NSImageExtensions.swift */; }; @@ -4681,6 +4682,7 @@ EE7F74902BB5D76600CD9456 /* BookmarksBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksBarTests.swift; sourceTree = ""; }; EEA3EEB02B24EBD000E8333A /* NetworkProtectionVPNCountryLabelsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNCountryLabelsModel.swift; sourceTree = ""; }; EEA3EEB22B24EC0600E8333A /* VPNLocationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNLocationViewModel.swift; sourceTree = ""; }; + EEA90B3A2BB6DB670009C62F /* BookmarksAndFavoritesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksAndFavoritesTests.swift; sourceTree = ""; }; EEAD7A6E2A1D3E1F002A24E7 /* AppLauncher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppLauncher.swift; sourceTree = ""; }; EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCUIElementExtension.swift; sourceTree = ""; }; EEC111E3294D06020086524F /* JSAlert.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = JSAlert.storyboard; sourceTree = ""; }; @@ -6448,6 +6450,7 @@ children = ( EEBCE6802BA444FA00B9DF00 /* Common */, EED735352BB46B6000F173D6 /* AutocompleteTests.swift */, + EEA90B3A2BB6DB670009C62F /* BookmarksAndFavoritesTests.swift */, EE7F74902BB5D76600CD9456 /* BookmarksBarTests.swift */, EE02D41B2BB460A600DBE6B3 /* BrowsingHistoryTests.swift */, EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, @@ -12066,6 +12069,7 @@ EE02D41A2BB4609900DBE6B3 /* UITests.swift in Sources */, EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, EE02D4212BB460FE00DBE6B3 /* StringExtension.swift in Sources */, + EEA90B3B2BB6DB670009C62F /* BookmarksAndFavoritesTests.swift in Sources */, EE02D4222BB4611A00DBE6B3 /* TestsURLExtension.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */, diff --git a/UITests/BookmarksAndFavoritesTests.swift b/UITests/BookmarksAndFavoritesTests.swift new file mode 100644 index 0000000000..0cb7ef2475 --- /dev/null +++ b/UITests/BookmarksAndFavoritesTests.swift @@ -0,0 +1,100 @@ +// +// BookmarksAndFavoritesTests.swift +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +class BookmarksAndFavoritesTests: XCTestCase { + private var app: XCUIApplication! + private var resetBookMarksMenuItem: XCUIElement! + private var pageTitle: String! + private var urlForBookmarksBar: URL! + private let titleStringLength = 12 + private var defaultBookmarkDialogButton: XCUIElement! + private var addressBarTextField: XCUIElement! + private var bookmarkPageContextMenuItem: XCUIElement! + private var bookmarksMenu: XCUIElement! + private var addressBarBookmarkButton: XCUIElement! + + override func setUpWithError() throws { + continueAfterFailure = false + app = XCUIApplication() + app.launchEnvironment["UITEST_MODE"] = "1" + resetBookMarksMenuItem = app.menuItems["MainMenu.resetBookmarks"] + pageTitle = UITests.randomPageTitle(length: titleStringLength) + urlForBookmarksBar = UITests.simpleServedPage(titled: pageTitle) + defaultBookmarkDialogButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] + addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + bookmarkPageContextMenuItem = app.menuItems["ContextMenuManager.bookmarkPageMenuItem"] + bookmarksMenu = app.menuBarItems["Bookmarks"] + addressBarBookmarkButton = app.buttons["AddressBarButtonsViewController.bookmarkButton"] + app.launch() + } + + func resetBookmarks() { + app.typeKey("n", modifierFlags: [.command]) // Can't use debug menu without a window + XCTAssertTrue( + resetBookMarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Reset bookmarks menu item didn't become available in a reasonable timeframe." + ) + + resetBookMarksMenuItem.click() + } + + func test_bookmarks_canBeAddedTo_withContextClickBookmarkThisPage() { + resetBookmarks() + addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + app.windows.webViews[pageTitle].rightClick() + XCTAssertTrue( + bookmarkPageContextMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "\"Add bookmark\" context menu item didn't appear in a reasonable timeframe." + ) + + bookmarkPageContextMenuItem.click() + XCTAssertTrue( + defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." + ) + XCTAssertTrue( + addressBarBookmarkButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar bookmark button didn't appear with the expected title in a reasonable timeframe." + ) + let addressBarBookmarkButtonValue = try? XCTUnwrap( + addressBarBookmarkButton.value as? String, + "It wasn't possible to get the value of the address bar bookmark button as String" + ) + + XCTAssertEqual( // The bookmark icon is already in a filled state and it isn't necessary to click the add button + addressBarBookmarkButtonValue, + "Bookmarked", + "The accessibility value of the address bar bookmark button must be \"Bookmarked\", which indicates the icon in the filled state." + ) + XCTAssertTrue( + bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Bookmarks\" menu bar item didn't appear with the expected title in a reasonable timeframe." + ) + bookmarksMenu.click() + XCTAssertTrue( // And the bookmark is found in the Bookmarks menu + app.menuItems[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmark in the \"Bookmarks\" menu with the title of the test page didn't appear with the expected title in a reasonable timeframe." + ) + } +} From 5a7e8cda13fee7455c76e810cd132051bb6f9533 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 14:56:03 +0100 Subject: [PATCH 089/108] Remove extraneous imports --- UITests/AutocompleteTests.swift | 1 - UITests/BookmarksBarTests.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/UITests/AutocompleteTests.swift b/UITests/AutocompleteTests.swift index 04507caa71..ff2c286546 100644 --- a/UITests/AutocompleteTests.swift +++ b/UITests/AutocompleteTests.swift @@ -16,7 +16,6 @@ // limitations under the License. // -import Common import XCTest class AutocompleteTests: XCTestCase { diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index e865bd18ba..0a31c6dc63 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -16,7 +16,6 @@ // limitations under the License. // -import Common import XCTest class BookmarksBarTests: XCTestCase { From eb10b9f9eb2fa512d30db17cb095b5e795cdf355 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 14:56:30 +0100 Subject: [PATCH 090/108] Add accessibility IDs and values --- .../View/AddressBarButtonsViewController.swift | 7 +++++-- DuckDuckGo/Tab/TabExtensions/ContextMenuManager.swift | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/NavigationBar/View/AddressBarButtonsViewController.swift b/DuckDuckGo/NavigationBar/View/AddressBarButtonsViewController.swift index 2457def59b..96a15277f0 100644 --- a/DuckDuckGo/NavigationBar/View/AddressBarButtonsViewController.swift +++ b/DuckDuckGo/NavigationBar/View/AddressBarButtonsViewController.swift @@ -272,7 +272,7 @@ final class AddressBarButtonsViewController: NSViewController { private func updateBookmarkButtonVisibility() { guard view.window?.isPopUpWindow == false else { return } - bookmarkButton.setAccessibilityIdentifier("Bookmarks Button") + bookmarkButton.setAccessibilityIdentifier("AddressBarButtonsViewController.bookmarkButton") let hasEmptyAddressBar = textFieldValue?.isEmpty ?? true var showBookmarkButton: Bool { guard let tabViewModel, tabViewModel.canBeBookmarked else { return false } @@ -723,15 +723,18 @@ final class AddressBarButtonsViewController: NSViewController { private func updateBookmarkButtonImage(isUrlBookmarked: Bool = false) { if let url = tabViewModel?.tab.content.url, - isUrlBookmarked || bookmarkManager.isUrlBookmarked(url: url) { + isUrlBookmarked || bookmarkManager.isUrlBookmarked(url: url) + { bookmarkButton.image = .bookmarkFilled bookmarkButton.mouseOverTintColor = NSColor.bookmarkFilledTint bookmarkButton.toolTip = UserText.editBookmarkTooltip + bookmarkButton.setAccessibilityValue("Bookmarked") } else { bookmarkButton.mouseOverTintColor = nil bookmarkButton.image = .bookmark bookmarkButton.contentTintColor = nil bookmarkButton.toolTip = UserText.addBookmarkTooltip + bookmarkButton.setAccessibilityValue("Unbookmarked") } } diff --git a/DuckDuckGo/Tab/TabExtensions/ContextMenuManager.swift b/DuckDuckGo/Tab/TabExtensions/ContextMenuManager.swift index 787ac89300..e966c8b7a0 100644 --- a/DuckDuckGo/Tab/TabExtensions/ContextMenuManager.swift +++ b/DuckDuckGo/Tab/TabExtensions/ContextMenuManager.swift @@ -234,7 +234,7 @@ private extension ContextMenuManager { } func bookmarkPageMenuItem() -> NSMenuItem { - NSMenuItem(title: UserText.bookmarkPage, action: #selector(MainViewController.bookmarkThisPage), target: nil, keyEquivalent: "") + NSMenuItem(title: UserText.bookmarkPage, action: #selector(MainViewController.bookmarkThisPage), target: nil, keyEquivalent: "").withAccessibilityIdentifier("ContextMenuManager.bookmarkPageMenuItem") } func openLinkInNewWindowMenuItem(from item: NSMenuItem) -> NSMenuItem { From a7834fe446f6b82a4ec5bd3b007633e221e5dd66 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 16:04:32 +0100 Subject: [PATCH 091/108] Add accessibiity IDs --- DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift index 4598e190e4..ee9499d7b8 100644 --- a/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift +++ b/DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift @@ -298,7 +298,7 @@ final class MoreOptionsMenu: NSMenu { .targetting(self) .withImage(.bookmarks) .withSubmenu(bookmarksSubMenu) - + .withAccessibilityIdentifier("MoreOptionsMenu.openBookmarks") addItem(withTitle: UserText.downloads, action: #selector(openDownloads), keyEquivalent: "j") .targetting(self) .withImage(.downloads) @@ -675,6 +675,7 @@ final class BookmarksSubMenu: NSMenu { let bookmarkPageItem = addItem(withTitle: UserText.bookmarkThisPage, action: #selector(MoreOptionsMenu.bookmarkPage(_:)), keyEquivalent: "d") .withModifierMask([.command]) .targetting(target) + .withAccessibilityIdentifier("MoreOptionsMenu.bookmarkPage") bookmarkPageItem.isEnabled = tabCollectionViewModel.selectedTabViewModel?.canBeBookmarked == true From 15b29fe699d896f266115cc36cd8e86191d7f670 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 29 Mar 2024 16:04:47 +0100 Subject: [PATCH 092/108] First three pasing tests --- UITests/BookmarksAndFavoritesTests.swift | 156 ++++++++++++++++++++++- 1 file changed, 154 insertions(+), 2 deletions(-) diff --git a/UITests/BookmarksAndFavoritesTests.swift b/UITests/BookmarksAndFavoritesTests.swift index 0cb7ef2475..7ef820937f 100644 --- a/UITests/BookmarksAndFavoritesTests.swift +++ b/UITests/BookmarksAndFavoritesTests.swift @@ -29,6 +29,8 @@ class BookmarksAndFavoritesTests: XCTestCase { private var bookmarkPageContextMenuItem: XCUIElement! private var bookmarksMenu: XCUIElement! private var addressBarBookmarkButton: XCUIElement! + private var optionsButton: XCUIElement! + private var bookmarkDialogBookmarkFolderDropdown: XCUIElement! override func setUpWithError() throws { continueAfterFailure = false @@ -42,6 +44,8 @@ class BookmarksAndFavoritesTests: XCTestCase { bookmarkPageContextMenuItem = app.menuItems["ContextMenuManager.bookmarkPageMenuItem"] bookmarksMenu = app.menuBarItems["Bookmarks"] addressBarBookmarkButton = app.buttons["AddressBarButtonsViewController.bookmarkButton"] + optionsButton = app.buttons["NavigationBarViewController.optionsButton"] + bookmarkDialogBookmarkFolderDropdown = app.popUpButtons["bookmark.add.folder.dropdown"] app.launch() } @@ -51,25 +55,99 @@ class BookmarksAndFavoritesTests: XCTestCase { resetBookMarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), "Reset bookmarks menu item didn't become available in a reasonable timeframe." ) - resetBookMarksMenuItem.click() } func test_bookmarks_canBeAddedTo_withContextClickBookmarkThisPage() { resetBookmarks() + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar text field didn't become available in a reasonable timeframe." + ) addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") XCTAssertTrue( app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), "Visited site didn't load with the expected title in a reasonable timeframe." ) app.windows.webViews[pageTitle].rightClick() - XCTAssertTrue( + XCTAssertTrue( // Bookmark the loaded page via context menu bookmarkPageContextMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), "\"Add bookmark\" context menu item didn't appear in a reasonable timeframe." ) bookmarkPageContextMenuItem.click() + XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it + defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." + ) + XCTAssertTrue( + addressBarBookmarkButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar bookmark button didn't appear with the expected title in a reasonable timeframe." + ) + XCTAssertTrue( + bookmarkDialogBookmarkFolderDropdown.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog's bookmark folder dropdown didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarkDialogBookmarkFolderDropdownValue = try? XCTUnwrap( // Bookmark dialog must default to "Bookmarks" folder + bookmarkDialogBookmarkFolderDropdown.value as? String, + "It wasn't possible to get the value of the \"Add bookmark\" dialog's bookmark folder dropdown as String" + ) + XCTAssertEqual( + bookmarkDialogBookmarkFolderDropdownValue, + "Bookmarks", + "The accessibility value of the \"Add bookmark\" dialog's bookmark folder dropdown must be \"Bookmarks\"." + ) + let addressBarBookmarkButtonValue = try? XCTUnwrap( + addressBarBookmarkButton.value as? String, + "It wasn't possible to get the value of the address bar bookmark button as String" + ) + + XCTAssertEqual( // The bookmark icon is already in a filled state and it isn't necessary to click the add button + addressBarBookmarkButtonValue, + "Bookmarked", + "The accessibility value of the address bar bookmark button must be \"Bookmarked\", which indicates the icon in the filled state." + ) + XCTAssertTrue( + bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Bookmarks\" menu bar item didn't appear with the expected title in a reasonable timeframe." + ) + bookmarksMenu.click() + XCTAssertTrue( // And the bookmark is found in the Bookmarks menu + app.menuItems[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmark in the \"Bookmarks\" menu with the title of the test page didn't appear with the expected title in a reasonable timeframe." + ) + } + + func test_bookmarks_canBeAddedTo_withSettingsItemBookmarkThisPage() { + let openBookmarksMenuItem = app.menuItems["MoreOptionsMenu.openBookmarks"] + let bookmarkPageMenuItem = app.menuItems["MoreOptionsMenu.bookmarkPage"] + resetBookmarks() + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar text field didn't become available in a reasonable timeframe." + ) + addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + + XCTAssertTrue( // Bookmark the loaded page via settings menu + optionsButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The options button didn't appear with the expected title in a reasonable timeframe." + ) + optionsButton.click() + XCTAssertTrue( + openBookmarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The settings \"Bookmarks\" menu item didn't appear with the expected title in a reasonable timeframe." + ) + openBookmarksMenuItem.hover() XCTAssertTrue( + bookmarkPageMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The settings \"Bookmark page\" menu item didn't appear with the expected title in a reasonable timeframe." + ) + bookmarkPageMenuItem.click() + XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." ) @@ -77,6 +155,19 @@ class BookmarksAndFavoritesTests: XCTestCase { addressBarBookmarkButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The address bar bookmark button didn't appear with the expected title in a reasonable timeframe." ) + XCTAssertTrue( + bookmarkDialogBookmarkFolderDropdown.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog's bookmark folder dropdown didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarkDialogBookmarkFolderDropdownValue = try? XCTUnwrap( + bookmarkDialogBookmarkFolderDropdown.value as? String, + "It wasn't possible to get the value of the \"Add bookmark\" dialog's bookmark folder dropdown as String" + ) + XCTAssertEqual( // Bookmark dialog must default to "Bookmarks" folder + bookmarkDialogBookmarkFolderDropdownValue, + "Bookmarks", + "The accessibility value of the \"Add bookmark\" dialog's bookmark folder dropdown must be \"Bookmarks\"." + ) let addressBarBookmarkButtonValue = try? XCTUnwrap( addressBarBookmarkButton.value as? String, "It wasn't possible to get the value of the address bar bookmark button as String" @@ -97,4 +188,65 @@ class BookmarksAndFavoritesTests: XCTestCase { "The bookmark in the \"Bookmarks\" menu with the title of the test page didn't appear with the expected title in a reasonable timeframe." ) } + + func test_bookmarks_canBeAddedTo_byClickingBookmarksButtonInAddressBar() { + resetBookmarks() + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar text field didn't become available in a reasonable timeframe." + ) + addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + + // In order to directly click the bookmark button in the address bar, we need to hover over something in the bar area + XCTAssertTrue( // We already have access to the optionsButton so it is OK to use for this hover + optionsButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The options button didn't appear with the expected title in a reasonable timeframe." + ) + optionsButton.hover() + XCTAssertTrue( // Now the button will exist so we can check for it and use it + addressBarBookmarkButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar bookmark button didn't appear with the expected title in a reasonable timeframe." + ) + addressBarBookmarkButton.click() + XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it + defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." + ) + XCTAssertTrue( + bookmarkDialogBookmarkFolderDropdown.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog's bookmark folder dropdown didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarkDialogBookmarkFolderDropdownValue = try? XCTUnwrap( + bookmarkDialogBookmarkFolderDropdown.value as? String, + "It wasn't possible to get the value of the \"Add bookmark\" dialog's bookmark folder dropdown as String" + ) + + XCTAssertEqual( // Bookmark dialog must default to "Bookmarks" folder + bookmarkDialogBookmarkFolderDropdownValue, + "Bookmarks", + "The accessibility value of the \"Add bookmark\" dialog's bookmark folder dropdown must be \"Bookmarks\"." + ) + let addressBarBookmarkButtonValue = try? XCTUnwrap( + addressBarBookmarkButton.value as? String, + "It wasn't possible to get the value of the address bar bookmark button as String" + ) + XCTAssertEqual( // The bookmark icon is already in a filled state and it isn't necessary to click the add button + addressBarBookmarkButtonValue, + "Bookmarked", + "The accessibility value of the address bar bookmark button must be \"Bookmarked\", which indicates the icon in the filled state." + ) + XCTAssertTrue( + bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Bookmarks\" menu bar item didn't appear with the expected title in a reasonable timeframe." + ) + bookmarksMenu.click() + XCTAssertTrue( // And the bookmark is found in the Bookmarks menu + app.menuItems[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmark in the \"Bookmarks\" menu with the title of the test page didn't appear with the expected title in a reasonable timeframe." + ) + } } From b9737693989de0b79845399896942b8829b7fd01 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:49:20 +0200 Subject: [PATCH 093/108] New accessibility function on menu items --- DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift b/DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift index b3f0870252..dfe76b5092 100644 --- a/DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift +++ b/DuckDuckGo/Common/Extensions/NSMenuItemExtension.swift @@ -110,6 +110,11 @@ extension NSMenuItem { return self } + func withAccessibilityValue(_ accessibilityValue: String) -> NSMenuItem { + self.setAccessibilityValue(accessibilityValue) + return self + } + @discardableResult func withImage(_ image: NSImage?) -> NSMenuItem { self.image = image From dd47d32be2e75f5eb319fdf2e3967eeab3eb3f1e Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:49:59 +0200 Subject: [PATCH 094/108] Accessibility IDs and values --- DuckDuckGo/Bookmarks/Services/ContextualMenu.swift | 2 ++ DuckDuckGo/Menus/MainMenu.swift | 3 ++- DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift b/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift index f79e265d96..786cd417cd 100644 --- a/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift +++ b/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift @@ -167,7 +167,9 @@ private extension ContextualMenu { static func addBookmarksToFavoritesMenuItem(bookmarks: [Bookmark], allFavorites: Bool) -> NSMenuItem { let title = allFavorites ? UserText.removeFromFavorites : UserText.addToFavorites + let accessibilityValue = allFavorites ? "Favorited" : "Unfavorited" return menuItem(title, #selector(BookmarkMenuItemSelectors.toggleBookmarkAsFavorite(_:)), bookmarks) + .withAccessibilityIdentifier("ContextualMenu.addBookmarksToFavoritesMenuItem").withAccessibilityValue(accessibilityValue) } static func editBookmarkMenuItem(bookmark: Bookmark?) -> NSMenuItem { diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index 15e6e5b723..84662a9344 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -72,7 +72,7 @@ import SubscriptionUI var forwardMenuItem: NSMenuItem { historyMenu.forwardMenuItem } // MARK: Bookmarks - let manageBookmarksMenuItem = NSMenuItem(title: UserText.mainMenuHistoryManageBookmarks, action: #selector(MainViewController.showManageBookmarks)) + let manageBookmarksMenuItem = NSMenuItem(title: UserText.mainMenuHistoryManageBookmarks, action: #selector(MainViewController.showManageBookmarks)).withAccessibilityIdentifier("MainMenu.manageBookmarksMenuItem") var bookmarksMenuToggleBookmarksBarMenuItem = NSMenuItem(title: "BookmarksBarMenuPlaceholder", action: #selector(MainViewController.toggleBookmarksBarFromMenu), keyEquivalent: "B") let importBookmarksMenuItem = NSMenuItem(title: UserText.importBookmarks, action: #selector(AppDelegate.openImportBrowserDataWindow)) let bookmarksMenu = NSMenu(title: UserText.bookmarks) @@ -313,6 +313,7 @@ import SubscriptionUI .submenu(favoritesMenu.buildItems { NSMenuItem(title: UserText.mainMenuHistoryFavoriteThisPage, action: #selector(MainViewController.favoriteThisPage)) .withImage(.favorite) + .withAccessibilityIdentifier("MainMenu.favoriteThisPage") NSMenuItem.separator() }) .withImage(.favorite) diff --git a/DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift b/DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift index a7b30dae90..c44ff7c3bd 100644 --- a/DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift +++ b/DuckDuckGo/Preferences/View/PreferencesAppearanceView.swift @@ -106,7 +106,7 @@ extension Preferences { if model.isContinueSetUpAvailable { ToggleMenuItem(UserText.newTabSetUpSectionTitle, isOn: $model.isContinueSetUpVisible) } - ToggleMenuItem(UserText.newTabFavoriteSectionTitle, isOn: $model.isFavoriteVisible) + ToggleMenuItem(UserText.newTabFavoriteSectionTitle, isOn: $model.isFavoriteVisible).accessibilityIdentifier("Preferences.AppearanceView.showFavoritesToggle") ToggleMenuItem(UserText.newTabRecentActivitySectionTitle, isOn: $model.isRecentActivityVisible) } From dd6e0f676894a8620bbfd79d8e54f46f4c0a7fe6 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 3 Apr 2024 09:50:41 +0200 Subject: [PATCH 095/108] Favorites tests --- UITests/BookmarksAndFavoritesTests.swift | 179 ++++++++++++++++++++++- 1 file changed, 176 insertions(+), 3 deletions(-) diff --git a/UITests/BookmarksAndFavoritesTests.swift b/UITests/BookmarksAndFavoritesTests.swift index 7ef820937f..e437630d53 100644 --- a/UITests/BookmarksAndFavoritesTests.swift +++ b/UITests/BookmarksAndFavoritesTests.swift @@ -31,6 +31,12 @@ class BookmarksAndFavoritesTests: XCTestCase { private var addressBarBookmarkButton: XCUIElement! private var optionsButton: XCUIElement! private var bookmarkDialogBookmarkFolderDropdown: XCUIElement! + private var favoriteThisPageMenuItem: XCUIElement! + private var addBookmarksToFavoritesMenuItem: XCUIElement! + private var bookmarksDialogAddToFavoritesCheckbox: XCUIElement! + private var manageBookmarksMenuItem: XCUIElement! + private var settingsAppearanceButton: XCUIElement! + private var showFavoritesPreferenceToggle: XCUIElement! override func setUpWithError() throws { continueAfterFailure = false @@ -46,7 +52,16 @@ class BookmarksAndFavoritesTests: XCTestCase { addressBarBookmarkButton = app.buttons["AddressBarButtonsViewController.bookmarkButton"] optionsButton = app.buttons["NavigationBarViewController.optionsButton"] bookmarkDialogBookmarkFolderDropdown = app.popUpButtons["bookmark.add.folder.dropdown"] + favoriteThisPageMenuItem = app.menuItems["MainMenu.favoriteThisPage"] + addBookmarksToFavoritesMenuItem = app.menuItems["ContextualMenu.addBookmarksToFavoritesMenuItem"] + bookmarksDialogAddToFavoritesCheckbox = app.checkBoxes["bookmark.add.add.to.favorites.button"] + manageBookmarksMenuItem = app.menuItems["MainMenu.manageBookmarksMenuItem"] + settingsAppearanceButton = app.buttons["PreferencesSidebar.appearanceButton"] + showFavoritesPreferenceToggle = app.checkBoxes["Preferences.AppearanceView.showFavoritesToggle"] app.launch() + resetBookmarks() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window + app.typeKey("n", modifierFlags: .command) } func resetBookmarks() { @@ -59,7 +74,6 @@ class BookmarksAndFavoritesTests: XCTestCase { } func test_bookmarks_canBeAddedTo_withContextClickBookmarkThisPage() { - resetBookmarks() XCTAssertTrue( addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The address bar text field didn't become available in a reasonable timeframe." @@ -121,7 +135,7 @@ class BookmarksAndFavoritesTests: XCTestCase { func test_bookmarks_canBeAddedTo_withSettingsItemBookmarkThisPage() { let openBookmarksMenuItem = app.menuItems["MoreOptionsMenu.openBookmarks"] let bookmarkPageMenuItem = app.menuItems["MoreOptionsMenu.bookmarkPage"] - resetBookmarks() + XCTAssertTrue( addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The address bar text field didn't become available in a reasonable timeframe." @@ -190,7 +204,6 @@ class BookmarksAndFavoritesTests: XCTestCase { } func test_bookmarks_canBeAddedTo_byClickingBookmarksButtonInAddressBar() { - resetBookmarks() XCTAssertTrue( addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The address bar text field didn't become available in a reasonable timeframe." @@ -249,4 +262,164 @@ class BookmarksAndFavoritesTests: XCTestCase { "The bookmark in the \"Bookmarks\" menu with the title of the test page didn't appear with the expected title in a reasonable timeframe." ) } + + func test_favorites_canBeAddedTo_byClickingFavoriteThisPageMenuBarItem() { + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar text field didn't become available in a reasonable timeframe." + ) + addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + XCTAssertTrue( + bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Bookmarks menu didn't load with the expected title in a reasonable timeframe." + ) + bookmarksMenu.click() + + XCTAssertTrue( + favoriteThisPageMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Favorite this page menu item didn't load with the expected title in a reasonable timeframe." + ) + favoriteThisPageMenuItem.click() + + XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it + defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." + ) + let addressBarBookmarkButtonValue = try? XCTUnwrap( + addressBarBookmarkButton.value as? String, + "It wasn't possible to get the value of the address bar bookmark button as String" + ) + XCTAssertEqual( // The bookmark icon is already in a filled state and it isn't necessary to click the add button + addressBarBookmarkButtonValue, + "Bookmarked", + "The accessibility value of the address bar bookmark button must be \"Bookmarked\", which indicates the icon in the filled state." + ) + XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it + defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." + ) + XCTAssertTrue( + bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + XCTAssertEqual( // The favorite checkbox in the dialog is already checked + bookmarksDialogAddToFavoritesCheckboxValue, + true, + "The the value of the bookmarks dialog's add to favorites checkbox must be checked, which indicates that the item has been favorited." + ) + } + + func test_favorites_canBeAddedTo_byClickingAddFavoriteInAddBookmarkPopover() { + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar text field didn't become available in a reasonable timeframe." + ) + addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + + // In order to directly click the bookmark button in the address bar, we need to hover over something in the bar area + XCTAssertTrue( // We already have access to the optionsButton so it is OK to use for this hover + optionsButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The options button didn't appear with the expected title in a reasonable timeframe." + ) + optionsButton.hover() + XCTAssertTrue( // Now the button will exist so we can check for it and use it + addressBarBookmarkButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar bookmark button didn't appear with the expected title in a reasonable timeframe." + ) + addressBarBookmarkButton.click() + XCTAssertTrue( // Check Add Bookmark dialog for existence before adding to favorites + defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." + ) + + XCTAssertTrue( + bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." + ) + bookmarksDialogAddToFavoritesCheckbox.click() + let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + XCTAssertEqual( // The favorite checkbox in the dialog is already checked + bookmarksDialogAddToFavoritesCheckboxValue, + true, + "The the value of the bookmarks dialog's add to favorites checkbox must be checked, which indicates that the item has been favorited." + ) + } + + func test_favorites_canBeManuallyAddedTo_byClickingAddFavoriteInNewTabPage() throws { + app.typeKey(",", modifierFlags: [.command]) // Open settings + + XCTAssertTrue( + settingsAppearanceButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The user settings appearance section button didn't become available in a reasonable timeframe." + ) + settingsAppearanceButton.click(forDuration: 0.5, thenDragTo: settingsAppearanceButton) + + XCTAssertTrue( + showFavoritesPreferenceToggle.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The user settings appearance section show favorites toggle didn't become available in a reasonable timeframe." + ) + let showFavoritesPreferenceToggleIsChecked = showFavoritesPreferenceToggle.value as? Bool + if showFavoritesPreferenceToggleIsChecked == false { // If untoggled, + showFavoritesPreferenceToggle.click() // Toggle "show favorites" + } + + app.typeKey("n", modifierFlags: [.command]) + let addFavoriteInNewTabButton = app.staticTexts["Add Favorite"] + XCTAssertTrue( + addFavoriteInNewTabButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The add favorite button in the new tab webview did not become available in a reasonable timeframe." + ) + addFavoriteInNewTabButton.click() + let pageTitleForAddFavoriteDialog: String = try XCTUnwrap(pageTitle, "Couldn't unwrap page title") + let urlForAddFavoriteDialog = try XCTUnwrap(urlForBookmarksBar, "Couldn't unwrap page url") + app.typeText("\(pageTitleForAddFavoriteDialog)\t") + app.typeText("\(urlForAddFavoriteDialog)\r") + let newFavorite = app.otherElements.staticTexts[pageTitleForAddFavoriteDialog] + + XCTAssertTrue( + newFavorite.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The new favorite on the new tab page did not become available in a reasonable timeframe." + ) + } + + func test_favorites_canBeAddedToFromManageBookmarksView() { + XCTAssertTrue( + addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The address bar text field didn't become available in a reasonable timeframe." + ) + addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") + XCTAssertTrue( + app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Visited site didn't load with the expected title in a reasonable timeframe." + ) + app.typeKey("d", modifierFlags: [.command]) // Add bookmark + app.typeKey(.escape, modifierFlags: []) // Exit dialog + XCTAssertTrue( + bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Bookmarks menu didn't load with the expected title in a reasonable timeframe." + ) + bookmarksMenu.click() + XCTAssertTrue( + manageBookmarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Manage bookmarks menu item didn't load with the expected title in a reasonable timeframe." + ) + manageBookmarksMenuItem.click() + app.staticTexts[pageTitle].hover() + + } } From d4cedbe6d4d23e67c74d2c164cfad8249c5cb656 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Wed, 3 Apr 2024 12:27:46 +0200 Subject: [PATCH 096/108] Set accessibility IDs --- DuckDuckGo/Bookmarks/View/BookmarkOutlineCellView.swift | 1 + DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/DuckDuckGo/Bookmarks/View/BookmarkOutlineCellView.swift b/DuckDuckGo/Bookmarks/View/BookmarkOutlineCellView.swift index 603849bbbf..06b868c826 100644 --- a/DuckDuckGo/Bookmarks/View/BookmarkOutlineCellView.swift +++ b/DuckDuckGo/Bookmarks/View/BookmarkOutlineCellView.swift @@ -82,6 +82,7 @@ final class BookmarkOutlineCellView: NSTableCellView { faviconImageView.imageScaling = .scaleProportionallyDown faviconImageView.wantsLayer = true faviconImageView.layer?.cornerRadius = 2.0 + faviconImageView.setAccessibilityIdentifier("BookmarkOutlineCellView.favIconImageView") titleLabel.translatesAutoresizingMaskIntoConstraints = false titleLabel.isEditable = false diff --git a/DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift b/DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift index 8bfdc3c4fb..91c85dc6d7 100644 --- a/DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift +++ b/DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift @@ -129,6 +129,7 @@ final class BookmarkTableCellView: NSTableCellView { menuButton.translatesAutoresizingMaskIntoConstraints = false menuButton.isBordered = false menuButton.isHidden = true + menuButton.setAccessibilityIdentifier("BookmarkTableCellView.menuButton") } private func setupLayout() { From 5179582305314b884af99b15d4a191b391abd0a8 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:25:38 +0200 Subject: [PATCH 097/108] Re-add test case after safety-first merge --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 3269bcf0a5..7b0e2e3c1f 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -3283,6 +3283,7 @@ EE339228291BDEFD009F62C1 /* JSAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE339227291BDEFD009F62C1 /* JSAlertController.swift */; }; EE3424602BA0853900173B1B /* VPNUninstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE34245D2BA0853900173B1B /* VPNUninstaller.swift */; }; EE3424612BA0853900173B1B /* VPNUninstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE34245D2BA0853900173B1B /* VPNUninstaller.swift */; }; + EE54F7B32BBFEA49006218DB /* BookmarksAndFavoritesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE54F7B22BBFEA48006218DB /* BookmarksAndFavoritesTests.swift */; }; EE66418C2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */; }; EE66418D2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */; }; EE66666F2B56EDE4001D898D /* VPNLocationsHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */; }; @@ -4761,6 +4762,7 @@ EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; EE339227291BDEFD009F62C1 /* JSAlertController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSAlertController.swift; sourceTree = ""; }; EE34245D2BA0853900173B1B /* VPNUninstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNUninstaller.swift; sourceTree = ""; }; + EE54F7B22BBFEA48006218DB /* BookmarksAndFavoritesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksAndFavoritesTests.swift; sourceTree = ""; }; EE66418B2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift"; sourceTree = ""; }; EE66666E2B56EDE4001D898D /* VPNLocationsHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNLocationsHostingViewController.swift; sourceTree = ""; }; EE7F74902BB5D76600CD9456 /* BookmarksBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksBarTests.swift; sourceTree = ""; }; @@ -6543,6 +6545,7 @@ children = ( EEBCE6802BA444FA00B9DF00 /* Common */, EED735352BB46B6000F173D6 /* AutocompleteTests.swift */, + EE54F7B22BBFEA48006218DB /* BookmarksAndFavoritesTests.swift */, EE7F74902BB5D76600CD9456 /* BookmarksBarTests.swift */, EE02D41B2BB460A600DBE6B3 /* BrowsingHistoryTests.swift */, EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */, @@ -12260,6 +12263,7 @@ EE02D41A2BB4609900DBE6B3 /* UITests.swift in Sources */, EE0429E02BA31D2F009EB20F /* FindInPageTests.swift in Sources */, EE02D4212BB460FE00DBE6B3 /* StringExtension.swift in Sources */, + EE54F7B32BBFEA49006218DB /* BookmarksAndFavoritesTests.swift in Sources */, EE02D4222BB4611A00DBE6B3 /* TestsURLExtension.swift in Sources */, 7B4CE8E726F02135009134B1 /* TabBarTests.swift in Sources */, EEBCE6832BA463DD00B9DF00 /* NSImageExtensions.swift in Sources */, From 831d2f91b57297c2448a456cc4976c6a1103d7e6 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Fri, 5 Apr 2024 17:08:59 +0200 Subject: [PATCH 098/108] Add accessibility IDs and values --- DuckDuckGo/Bookmarks/Services/ContextualMenu.swift | 3 ++- .../View/BookmarkManagementSidebarViewController.swift | 1 + DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift | 3 +++ DuckDuckGo/HomePage/View/FavoritesView.swift | 6 +++--- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift b/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift index 786cd417cd..f086e09039 100644 --- a/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift +++ b/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift @@ -162,7 +162,8 @@ private extension ContextualMenu { static func addBookmarkToFavoritesMenuItem(isFavorite: Bool, bookmark: Bookmark?) -> NSMenuItem { let title = isFavorite ? UserText.removeFromFavorites : UserText.addToFavorites - return menuItem(title, #selector(BookmarkMenuItemSelectors.toggleBookmarkAsFavorite(_:)), bookmark) + let accessibilityValue = isFavorite ? "Favorited" : "Unfavorited" + return menuItem(title, #selector(BookmarkMenuItemSelectors.toggleBookmarkAsFavorite(_:)), bookmark).withAccessibilityIdentifier("ContextualMenu.addBookmarkToFavoritesMenuItem").withAccessibilityValue(accessibilityValue) } static func addBookmarksToFavoritesMenuItem(bookmarks: [Bookmark], allFavorites: Bool) -> NSMenuItem { diff --git a/DuckDuckGo/Bookmarks/View/BookmarkManagementSidebarViewController.swift b/DuckDuckGo/Bookmarks/View/BookmarkManagementSidebarViewController.swift index 53e502383b..f567a9f124 100644 --- a/DuckDuckGo/Bookmarks/View/BookmarkManagementSidebarViewController.swift +++ b/DuckDuckGo/Bookmarks/View/BookmarkManagementSidebarViewController.swift @@ -89,6 +89,7 @@ final class BookmarkManagementSidebarViewController: NSViewController { tabSwitcherButton.menu = NSMenu { for content in Tab.TabContent.displayableTabTypes { NSMenuItem(title: content.title!, representedObject: content) + .withAccessibilityIdentifier("BookmarkManagementSidebarViewController.\(content.title!)") } } diff --git a/DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift b/DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift index 91c85dc6d7..3a3d3ea0d9 100644 --- a/DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift +++ b/DuckDuckGo/Bookmarks/View/BookmarkTableCellView.swift @@ -210,11 +210,14 @@ final class BookmarkTableCellView: NSTableCellView { faviconImageView.image = bookmark.favicon(.small) ?? .bookmarkDefaultFavicon + faviconImageView.setAccessibilityIdentifier("BookmarkTableCellView.favIconImageView") if bookmark.isFavorite { accessoryImageView.isHidden = false } accessoryImageView.image = bookmark.isFavorite ? .favoriteFilledBorder : nil + accessoryImageView.setAccessibilityIdentifier("BookmarkTableCellView.accessoryImageView") + accessoryImageView.setAccessibilityValue(bookmark.isFavorite ? "Favorited" : "Unfavorited") titleLabel.stringValue = bookmark.title primaryTitleLabelValue = bookmark.title tertiaryTitleLabelValue = bookmark.url diff --git a/DuckDuckGo/HomePage/View/FavoritesView.swift b/DuckDuckGo/HomePage/View/FavoritesView.swift index 52e7f585d1..5ad5546ef8 100644 --- a/DuckDuckGo/HomePage/View/FavoritesView.swift +++ b/DuckDuckGo/HomePage/View/FavoritesView.swift @@ -344,13 +344,13 @@ extension HomePage.Models.FavoriteModel { var favoriteView: some View { switch favoriteType { case .bookmark(let bookmark): - HomePage.Views.Favorite(bookmark: bookmark) + HomePage.Views.Favorite(bookmark: bookmark)?.accessibilityIdentifier("HomePage.Models.FavoriteModel.\(bookmark.title)") case .addButton: - HomePage.Views.FavoritesGridAddButton() + HomePage.Views.FavoritesGridAddButton().accessibilityIdentifier("HomePage.Models.FavoriteModel.addButton") case .ghostButton: - HomePage.Views.FavoritesGridGhostButton() + HomePage.Views.FavoritesGridGhostButton().accessibilityIdentifier("HomePage.Models.FavoriteModel.ghostButton") } } } From 5ac85d61db57d482ae2658489b35661a28e97d42 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:00:41 +0200 Subject: [PATCH 099/108] Remove unused id --- UITests/BookmarksBarTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/UITests/BookmarksBarTests.swift b/UITests/BookmarksBarTests.swift index 55eecbe52b..eacea0c9aa 100644 --- a/UITests/BookmarksBarTests.swift +++ b/UITests/BookmarksBarTests.swift @@ -55,7 +55,6 @@ class BookmarksBarTests: XCTestCase { resetBookmarksAndAddOneBookmark() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close windows openSettingsAndSetShowBookmarksBarToUnchecked() - settingsWindow = app.windows.containing(.checkBox, identifier: "Preferences.AppearanceView.showBookmarksBarPreferenceToggle").firstMatch openSecondWindowAndVisitSite() siteWindow = app.windows.containing(.webView, identifier: pageTitle).firstMatch } From f37c372f205b94fa87389a87ef82d5c45b648140 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:00:56 +0200 Subject: [PATCH 100/108] Abstract click and hover after existence is proven --- UITests/Common/XCUIElementExtension.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/UITests/Common/XCUIElementExtension.swift b/UITests/Common/XCUIElementExtension.swift index 7e72c41a02..e0667e958a 100644 --- a/UITests/Common/XCUIElementExtension.swift +++ b/UITests/Common/XCUIElementExtension.swift @@ -62,4 +62,20 @@ extension XCUIElement { self.typeText("\r") } } + + func clickAfterExistenceTestSucceeds() { + XCTAssertTrue( + self.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "\(self.debugDescription) didn't load with the expected title in a reasonable timeframe." + ) + self.click() + } + + func hoverAfterExistenceTestSucceeds() { + XCTAssertTrue( + self.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "\(self.debugDescription) didn't load with the expected title in a reasonable timeframe." + ) + self.hover() + } } From 430cf0db9fd41096c02e726db90f34e6662b90bf Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:01:05 +0200 Subject: [PATCH 101/108] Increase timeout --- UITests/Common/UITests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UITests/Common/UITests.swift b/UITests/Common/UITests.swift index 40f28936f9..438cf5be4d 100644 --- a/UITests/Common/UITests.swift +++ b/UITests/Common/UITests.swift @@ -24,7 +24,7 @@ enum UITests { /// Timeout constants for different test requirements enum Timeouts { /// Mostly, we use timeouts to wait for element existence. This is about 3x longer than needed, for CI resilience - static let elementExistence: Double = 2.5 + static let elementExistence: Double = 5.0 /// The fire animation time has environmental dependencies, so we want to wait for completion so we don't try to type into it static let fireAnimation: Double = 30.0 } From 3496bc8591a52d1e959a20f61951898a57f081cd Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:01:21 +0200 Subject: [PATCH 102/108] Add IDs --- DuckDuckGo/Bookmarks/Services/ContextualMenu.swift | 6 ++++-- DuckDuckGo/HomePage/View/FavoritesView.swift | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift b/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift index f086e09039..eb77fd956f 100644 --- a/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift +++ b/DuckDuckGo/Bookmarks/Services/ContextualMenu.swift @@ -162,8 +162,9 @@ private extension ContextualMenu { static func addBookmarkToFavoritesMenuItem(isFavorite: Bool, bookmark: Bookmark?) -> NSMenuItem { let title = isFavorite ? UserText.removeFromFavorites : UserText.addToFavorites - let accessibilityValue = isFavorite ? "Favorited" : "Unfavorited" - return menuItem(title, #selector(BookmarkMenuItemSelectors.toggleBookmarkAsFavorite(_:)), bookmark).withAccessibilityIdentifier("ContextualMenu.addBookmarkToFavoritesMenuItem").withAccessibilityValue(accessibilityValue) + return menuItem(title, #selector(BookmarkMenuItemSelectors.toggleBookmarkAsFavorite(_:)), bookmark) + .withAccessibilityIdentifier(isFavorite == false ? "ContextualMenu.addBookmarkToFavoritesMenuItem" : + "ContextualMenu.removeBookmarkFromFavoritesMenuItem") } static func addBookmarksToFavoritesMenuItem(bookmarks: [Bookmark], allFavorites: Bool) -> NSMenuItem { @@ -183,6 +184,7 @@ private extension ContextualMenu { static func deleteBookmarkMenuItem(bookmark: Bookmark?) -> NSMenuItem { menuItem(UserText.bookmarksBarContextMenuDelete, #selector(BookmarkMenuItemSelectors.deleteBookmark(_:)), bookmark) + .withAccessibilityIdentifier("ContextualMenu.deleteBookmark") } static func moveToEndMenuItem(entity: BaseBookmarkEntity?, parent: BookmarkFolder?) -> NSMenuItem { diff --git a/DuckDuckGo/HomePage/View/FavoritesView.swift b/DuckDuckGo/HomePage/View/FavoritesView.swift index 5ad5546ef8..39f74b58cf 100644 --- a/DuckDuckGo/HomePage/View/FavoritesView.swift +++ b/DuckDuckGo/HomePage/View/FavoritesView.swift @@ -324,12 +324,12 @@ struct Favorite: View { .link { model.open(bookmark) }.contextMenu(ContextMenu(menuItems: { - Button(UserText.openInNewTab, action: { model.openInNewTab(bookmark) }) - Button(UserText.openInNewWindow, action: { model.openInNewWindow(bookmark) }) + Button(UserText.openInNewTab, action: { model.openInNewTab(bookmark) }).accessibilityIdentifier("HomePage.Views.openInNewTab") + Button(UserText.openInNewWindow, action: { model.openInNewWindow(bookmark) }).accessibilityIdentifier("HomePage.Views.openInNewWindow") Divider() - Button(UserText.edit, action: { model.edit(bookmark) }) - Button(UserText.removeFavorite, action: { model.removeFavorite(bookmark) }) - Button(UserText.deleteBookmark, action: { model.deleteBookmark(bookmark) }) + Button(UserText.edit, action: { model.edit(bookmark) }).accessibilityIdentifier("HomePage.Views.editBookmark") + Button(UserText.removeFavorite, action: { model.removeFavorite(bookmark) }).accessibilityIdentifier("HomePage.Views.removeFavorite") + Button(UserText.deleteBookmark, action: { model.deleteBookmark(bookmark) }).accessibilityIdentifier("HomePage.Views.deleteBookmark") })) } From e78f06001c9ed0869f87cacce19f4773e2e6e644 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:01:39 +0200 Subject: [PATCH 103/108] Finalize tests --- UITests/BookmarksAndFavoritesTests.swift | 620 +++++++++++++++++------ 1 file changed, 452 insertions(+), 168 deletions(-) diff --git a/UITests/BookmarksAndFavoritesTests.swift b/UITests/BookmarksAndFavoritesTests.swift index e437630d53..2e5e842277 100644 --- a/UITests/BookmarksAndFavoritesTests.swift +++ b/UITests/BookmarksAndFavoritesTests.swift @@ -20,44 +20,76 @@ import XCTest class BookmarksAndFavoritesTests: XCTestCase { private var app: XCUIApplication! - private var resetBookMarksMenuItem: XCUIElement! private var pageTitle: String! private var urlForBookmarksBar: URL! private let titleStringLength = 12 - private var defaultBookmarkDialogButton: XCUIElement! + + private var addressBarBookmarkButton: XCUIElement! private var addressBarTextField: XCUIElement! + private var bookmarkDialogBookmarkFolderDropdown: XCUIElement! private var bookmarkPageContextMenuItem: XCUIElement! + private var bookmarkPageMenuItem: XCUIElement! + private var bookmarksBarCollectionView: XCUIElement! + private var bookmarksDialogAddToFavoritesCheckbox: XCUIElement! + private var bookmarksManagementAccessoryImageView: XCUIElement! private var bookmarksMenu: XCUIElement! - private var addressBarBookmarkButton: XCUIElement! - private var optionsButton: XCUIElement! - private var bookmarkDialogBookmarkFolderDropdown: XCUIElement! + private var bookmarksTabPopup: XCUIElement! + private var bookmarkTableCellViewFavIconImageView: XCUIElement! + private var bookmarkTableCellViewMenuButton: XCUIElement! + private var contextualMenuAddBookmarkToFavoritesMenuItem: XCUIElement! + private var contextualMenuDeleteBookmarkMenuItem: XCUIElement! + private var contextualMenuRemoveBookmarkFromFavoritesMenuItem: XCUIElement! + private var defaultBookmarkDialogButton: XCUIElement! + private var defaultBookmarkOtherButton: XCUIElement! + private var favoriteGridAddFavoriteButton: XCUIElement! private var favoriteThisPageMenuItem: XCUIElement! - private var addBookmarksToFavoritesMenuItem: XCUIElement! - private var bookmarksDialogAddToFavoritesCheckbox: XCUIElement! private var manageBookmarksMenuItem: XCUIElement! + private var openBookmarksMenuItem: XCUIElement! + private var optionsButton: XCUIElement! + private var removeFavoritesContextMenuItem: XCUIElement! + private var resetBookMarksMenuItem: XCUIElement! private var settingsAppearanceButton: XCUIElement! + private var showBookmarksBarPreferenceToggle: XCUIElement! + private var showBookmarksBarAlways: XCUIElement! + private var showBookmarksBarPopup: XCUIElement! private var showFavoritesPreferenceToggle: XCUIElement! override func setUpWithError() throws { continueAfterFailure = false app = XCUIApplication() app.launchEnvironment["UITEST_MODE"] = "1" - resetBookMarksMenuItem = app.menuItems["MainMenu.resetBookmarks"] pageTitle = UITests.randomPageTitle(length: titleStringLength) urlForBookmarksBar = UITests.simpleServedPage(titled: pageTitle) - defaultBookmarkDialogButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] + addressBarBookmarkButton = app.buttons["AddressBarButtonsViewController.bookmarkButton"] addressBarTextField = app.windows.textFields["AddressBarViewController.addressBarTextField"] + bookmarkDialogBookmarkFolderDropdown = app.popUpButtons["bookmark.add.folder.dropdown"] bookmarkPageContextMenuItem = app.menuItems["ContextMenuManager.bookmarkPageMenuItem"] + bookmarkPageMenuItem = app.menuItems["MoreOptionsMenu.bookmarkPage"] + bookmarksBarCollectionView = app.collectionViews["BookmarksBarViewController.bookmarksBarCollectionView"] + bookmarksDialogAddToFavoritesCheckbox = app.checkBoxes["bookmark.add.add.to.favorites.button"] + bookmarksManagementAccessoryImageView = app.images["BookmarkTableCellView.accessoryImageView"] bookmarksMenu = app.menuBarItems["Bookmarks"] - addressBarBookmarkButton = app.buttons["AddressBarButtonsViewController.bookmarkButton"] - optionsButton = app.buttons["NavigationBarViewController.optionsButton"] - bookmarkDialogBookmarkFolderDropdown = app.popUpButtons["bookmark.add.folder.dropdown"] + bookmarksTabPopup = app.popUpButtons["Bookmarks"] + bookmarkTableCellViewFavIconImageView = app.images["BookmarkTableCellView.favIconImageView"] + bookmarkTableCellViewMenuButton = app.buttons["BookmarkTableCellView.menuButton"] + contextualMenuAddBookmarkToFavoritesMenuItem = app.menuItems["ContextualMenu.addBookmarkToFavoritesMenuItem"] + contextualMenuDeleteBookmarkMenuItem = app.menuItems["ContextualMenu.deleteBookmark"] + contextualMenuRemoveBookmarkFromFavoritesMenuItem = app.menuItems["ContextualMenu.removeBookmarkFromFavoritesMenuItem"] + defaultBookmarkDialogButton = app.buttons["BookmarkDialogButtonsView.defaultButton"] + defaultBookmarkOtherButton = app.buttons["BookmarkDialogButtonsView.otherButton"] + favoriteGridAddFavoriteButton = app.staticTexts["HomePage.Models.FavoriteModel.addButton"] favoriteThisPageMenuItem = app.menuItems["MainMenu.favoriteThisPage"] - addBookmarksToFavoritesMenuItem = app.menuItems["ContextualMenu.addBookmarksToFavoritesMenuItem"] - bookmarksDialogAddToFavoritesCheckbox = app.checkBoxes["bookmark.add.add.to.favorites.button"] manageBookmarksMenuItem = app.menuItems["MainMenu.manageBookmarksMenuItem"] + openBookmarksMenuItem = app.menuItems["MoreOptionsMenu.openBookmarks"] + optionsButton = app.buttons["NavigationBarViewController.optionsButton"] + removeFavoritesContextMenuItem = app.menuItems["HomePage.Views.removeFavorite"] + resetBookMarksMenuItem = app.menuItems["MainMenu.resetBookmarks"] settingsAppearanceButton = app.buttons["PreferencesSidebar.appearanceButton"] + showBookmarksBarAlways = app.menuItems["Preferences.AppearanceView.showBookmarksBarAlways"] + showBookmarksBarPopup = app.popUpButtons["Preferences.AppearanceView.showBookmarksBarPopUp"] + showBookmarksBarPreferenceToggle = app.checkBoxes["Preferences.AppearanceView.showBookmarksBarPreferenceToggle"] showFavoritesPreferenceToggle = app.checkBoxes["Preferences.AppearanceView.showFavoritesToggle"] + app.launch() resetBookmarks() app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Let's enforce a single window @@ -74,22 +106,9 @@ class BookmarksAndFavoritesTests: XCTestCase { } func test_bookmarks_canBeAddedTo_withContextClickBookmarkThisPage() { - XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The address bar text field didn't become available in a reasonable timeframe." - ) - addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") - XCTAssertTrue( - app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Visited site didn't load with the expected title in a reasonable timeframe." - ) + openSiteToBookmark(bookmarkingViaDialog: false, escapingDialog: false) app.windows.webViews[pageTitle].rightClick() - XCTAssertTrue( // Bookmark the loaded page via context menu - bookmarkPageContextMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "\"Add bookmark\" context menu item didn't appear in a reasonable timeframe." - ) - - bookmarkPageContextMenuItem.click() + bookmarkPageContextMenuItem.clickAfterExistenceTestSucceeds() XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." @@ -121,11 +140,8 @@ class BookmarksAndFavoritesTests: XCTestCase { "Bookmarked", "The accessibility value of the address bar bookmark button must be \"Bookmarked\", which indicates the icon in the filled state." ) - XCTAssertTrue( - bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The \"Bookmarks\" menu bar item didn't appear with the expected title in a reasonable timeframe." - ) - bookmarksMenu.click() + + bookmarksMenu.clickAfterExistenceTestSucceeds() XCTAssertTrue( // And the bookmark is found in the Bookmarks menu app.menuItems[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), "The bookmark in the \"Bookmarks\" menu with the title of the test page didn't appear with the expected title in a reasonable timeframe." @@ -133,34 +149,10 @@ class BookmarksAndFavoritesTests: XCTestCase { } func test_bookmarks_canBeAddedTo_withSettingsItemBookmarkThisPage() { - let openBookmarksMenuItem = app.menuItems["MoreOptionsMenu.openBookmarks"] - let bookmarkPageMenuItem = app.menuItems["MoreOptionsMenu.bookmarkPage"] - - XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The address bar text field didn't become available in a reasonable timeframe." - ) - addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") - XCTAssertTrue( - app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Visited site didn't load with the expected title in a reasonable timeframe." - ) - - XCTAssertTrue( // Bookmark the loaded page via settings menu - optionsButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The options button didn't appear with the expected title in a reasonable timeframe." - ) - optionsButton.click() - XCTAssertTrue( - openBookmarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The settings \"Bookmarks\" menu item didn't appear with the expected title in a reasonable timeframe." - ) - openBookmarksMenuItem.hover() - XCTAssertTrue( - bookmarkPageMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The settings \"Bookmark page\" menu item didn't appear with the expected title in a reasonable timeframe." - ) - bookmarkPageMenuItem.click() + openSiteToBookmark(bookmarkingViaDialog: false, escapingDialog: false) + optionsButton.clickAfterExistenceTestSucceeds() + openBookmarksMenuItem.hoverAfterExistenceTestSucceeds() + bookmarkPageMenuItem.clickAfterExistenceTestSucceeds() XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." @@ -182,21 +174,18 @@ class BookmarksAndFavoritesTests: XCTestCase { "Bookmarks", "The accessibility value of the \"Add bookmark\" dialog's bookmark folder dropdown must be \"Bookmarks\"." ) + let addressBarBookmarkButtonValue = try? XCTUnwrap( addressBarBookmarkButton.value as? String, "It wasn't possible to get the value of the address bar bookmark button as String" ) - XCTAssertEqual( // The bookmark icon is already in a filled state and it isn't necessary to click the add button addressBarBookmarkButtonValue, "Bookmarked", "The accessibility value of the address bar bookmark button must be \"Bookmarked\", which indicates the icon in the filled state." ) - XCTAssertTrue( - bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The \"Bookmarks\" menu bar item didn't appear with the expected title in a reasonable timeframe." - ) - bookmarksMenu.click() + + bookmarksMenu.clickAfterExistenceTestSucceeds() XCTAssertTrue( // And the bookmark is found in the Bookmarks menu app.menuItems[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), "The bookmark in the \"Bookmarks\" menu with the title of the test page didn't appear with the expected title in a reasonable timeframe." @@ -204,27 +193,10 @@ class BookmarksAndFavoritesTests: XCTestCase { } func test_bookmarks_canBeAddedTo_byClickingBookmarksButtonInAddressBar() { - XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The address bar text field didn't become available in a reasonable timeframe." - ) - addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") - XCTAssertTrue( - app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Visited site didn't load with the expected title in a reasonable timeframe." - ) - + openSiteToBookmark(bookmarkingViaDialog: false, escapingDialog: false) // In order to directly click the bookmark button in the address bar, we need to hover over something in the bar area - XCTAssertTrue( // We already have access to the optionsButton so it is OK to use for this hover - optionsButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The options button didn't appear with the expected title in a reasonable timeframe." - ) - optionsButton.hover() - XCTAssertTrue( // Now the button will exist so we can check for it and use it - addressBarBookmarkButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The address bar bookmark button didn't appear with the expected title in a reasonable timeframe." - ) - addressBarBookmarkButton.click() + optionsButton.hoverAfterExistenceTestSucceeds() + addressBarBookmarkButton.clickAfterExistenceTestSucceeds() XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." @@ -252,11 +224,8 @@ class BookmarksAndFavoritesTests: XCTestCase { "Bookmarked", "The accessibility value of the address bar bookmark button must be \"Bookmarked\", which indicates the icon in the filled state." ) - XCTAssertTrue( - bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The \"Bookmarks\" menu bar item didn't appear with the expected title in a reasonable timeframe." - ) - bookmarksMenu.click() + + bookmarksMenu.clickAfterExistenceTestSucceeds() XCTAssertTrue( // And the bookmark is found in the Bookmarks menu app.menuItems[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), "The bookmark in the \"Bookmarks\" menu with the title of the test page didn't appear with the expected title in a reasonable timeframe." @@ -264,26 +233,9 @@ class BookmarksAndFavoritesTests: XCTestCase { } func test_favorites_canBeAddedTo_byClickingFavoriteThisPageMenuBarItem() { - XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The address bar text field didn't become available in a reasonable timeframe." - ) - addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") - XCTAssertTrue( - app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Visited site didn't load with the expected title in a reasonable timeframe." - ) - XCTAssertTrue( - bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Bookmarks menu didn't load with the expected title in a reasonable timeframe." - ) - bookmarksMenu.click() - - XCTAssertTrue( - favoriteThisPageMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Favorite this page menu item didn't load with the expected title in a reasonable timeframe." - ) - favoriteThisPageMenuItem.click() + openSiteToBookmark(bookmarkingViaDialog: false, escapingDialog: false) + bookmarksMenu.clickAfterExistenceTestSucceeds() + favoriteThisPageMenuItem.clickAfterExistenceTestSucceeds() XCTAssertTrue( // Check Add Bookmark dialog for existence but don't click on it defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), @@ -306,6 +258,7 @@ class BookmarksAndFavoritesTests: XCTestCase { bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." ) + let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( bookmarksDialogAddToFavoritesCheckbox.value as? Bool, "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" @@ -318,49 +271,368 @@ class BookmarksAndFavoritesTests: XCTestCase { } func test_favorites_canBeAddedTo_byClickingAddFavoriteInAddBookmarkPopover() { + openSiteToBookmark(bookmarkingViaDialog: false, escapingDialog: false) + // In order to directly click the bookmark button in the address bar, we need to hover over something in the bar area + optionsButton.hoverAfterExistenceTestSucceeds() + + addressBarBookmarkButton.clickAfterExistenceTestSucceeds() + XCTAssertTrue( // Check Add Bookmark dialog for existence before adding to favorites + defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." + ) + + bookmarksDialogAddToFavoritesCheckbox.clickAfterExistenceTestSucceeds() + let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + XCTAssertEqual( // The favorite checkbox in the dialog is already checked + bookmarksDialogAddToFavoritesCheckboxValue, + true, + "The the value of the bookmarks dialog's add to favorites checkbox must be checked, which indicates that the item has been favorited." + ) + } + + func test_favorites_canBeManuallyAddedTo_byClickingAddFavoriteInNewTabPage() throws { + toggleBookmarksBarShowFavoritesOn() + + favoriteGridAddFavoriteButton.clickAfterExistenceTestSucceeds() + let pageTitleForAddFavoriteDialog: String = try XCTUnwrap(pageTitle, "Couldn't unwrap page title") + let urlForAddFavoriteDialog = try XCTUnwrap(urlForBookmarksBar, "Couldn't unwrap page url") + app.typeText("\(pageTitleForAddFavoriteDialog)\t") + app.typeURL(urlForAddFavoriteDialog) + let newFavorite = app.otherElements.staticTexts[pageTitleForAddFavoriteDialog] + XCTAssertTrue( - addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The address bar text field didn't become available in a reasonable timeframe." + newFavorite.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The new favorite on the new tab page did not become available in a reasonable timeframe." ) - addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") + } + + func test_favorites_canBeAddedToFromManageBookmarksView() { + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) + bookmarksMenu.clickAfterExistenceTestSucceeds() + manageBookmarksMenuItem.clickAfterExistenceTestSucceeds() + bookmarkTableCellViewFavIconImageView.hoverAfterExistenceTestSucceeds() + bookmarkTableCellViewMenuButton.clickAfterExistenceTestSucceeds() + + contextualMenuAddBookmarkToFavoritesMenuItem.clickAfterExistenceTestSucceeds() XCTAssertTrue( - app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Visited site didn't load with the expected title in a reasonable timeframe." + bookmarksManagementAccessoryImageView.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Bookmarks accessory view favorites indicator didn't load with the expected title in a reasonable timeframe." + ) + let bookmarksManagementAccessoryImageViewValue = try? XCTUnwrap( + bookmarksManagementAccessoryImageView.value as? String, + "It wasn't possible to get the value of the bookmarks management accessory image view as String" ) - // In order to directly click the bookmark button in the address bar, we need to hover over something in the bar area - XCTAssertTrue( // We already have access to the optionsButton so it is OK to use for this hover - optionsButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The options button didn't appear with the expected title in a reasonable timeframe." + XCTAssertEqual( + bookmarksManagementAccessoryImageViewValue, + "Favorited", + "The accessibility value of the favorite accessory view on the bookmark management view must be \"Favorited\"." ) - optionsButton.hover() - XCTAssertTrue( // Now the button will exist so we can check for it and use it + } + + func test_bookmarks_canBeViewedInBookmarkMenuItem() { + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) + addressBarBookmarkButton.clickAfterExistenceTestSucceeds() + + bookmarksMenu.clickAfterExistenceTestSucceeds() + let bookmarkedItemInMenu = app.menuItems[pageTitle] + + XCTAssertTrue( + bookmarkedItemInMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Bookmarked page couldn't be detected in the bookmarks menu in a reasonable timeframe." + ) + } + + func test_bookmarks_canBeViewedInAddressBarBookmarkDialog() { + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) + XCTAssertTrue( addressBarBookmarkButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The address bar bookmark button didn't appear with the expected title in a reasonable timeframe." + "Address bar bookmark button didn't load with the expected title in a reasonable timeframe." + ) + let addressBarBookmarkButtonValue = try? XCTUnwrap( + addressBarBookmarkButton.value as? String, + "It wasn't possible to get the value of the bookmarks management accessory image view as String" ) + XCTAssertEqual( + addressBarBookmarkButtonValue, + "Bookmarked", + "The accessibility value of the Address Bar Bookmark Button must be \"Bookmarked\"." + ) + addressBarBookmarkButton.click() - XCTAssertTrue( // Check Add Bookmark dialog for existence before adding to favorites - defaultBookmarkDialogButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The \"Add bookmark\" dialog option button didn't appear with the expected title in a reasonable timeframe." + let bookMarkDialogBookmarkTitle = app.textFields[pageTitle] + + XCTAssertTrue( + bookMarkDialogBookmarkTitle.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarked url title wasn't found in the bookmark dialog in a bookmarked state in a reasonable timeframe." + ) + } + + func test_bookmarksTab_canBeViewedViaMenuItemManageBookmarks() { + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) + bookmarksMenu.clickAfterExistenceTestSucceeds() + + manageBookmarksMenuItem.clickAfterExistenceTestSucceeds() + + XCTAssertTrue( + bookmarksTabPopup.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Bookmarks tab bookmarks popup didn't load with the expected title in a reasonable timeframe." ) + } + func test_favorites_appearWithTheCorrectIndicatorInBookmarksTab() { + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: false) XCTAssertTrue( bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." ) - bookmarksDialogAddToFavoritesCheckbox.click() let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( bookmarksDialogAddToFavoritesCheckbox.value as? Bool, "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" ) - XCTAssertEqual( // The favorite checkbox in the dialog is already checked - bookmarksDialogAddToFavoritesCheckboxValue, - true, - "The the value of the bookmarks dialog's add to favorites checkbox must be checked, which indicates that the item has been favorited." + if bookmarksDialogAddToFavoritesCheckboxValue == false { + bookmarksDialogAddToFavoritesCheckbox.click() + } + app.typeKey(.escape, modifierFlags: []) // Exit dialog + + bookmarksMenu.clickAfterExistenceTestSucceeds() + manageBookmarksMenuItem.clickAfterExistenceTestSucceeds() + XCTAssertTrue( + bookmarksTabPopup.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Bookmarks tab bookmarks popup didn't load with the expected title in a reasonable timeframe." + ) + XCTAssertTrue( + bookmarksManagementAccessoryImageView.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Bookmarks accessory view favorites indicator didn't load with the expected title in a reasonable timeframe." + ) + let bookmarksManagementAccessoryImageViewValue = try? XCTUnwrap( + bookmarksManagementAccessoryImageView.value as? String, + "It wasn't possible to get the value of the bookmarks management accessory image view as String" + ) + + XCTAssertEqual( + bookmarksManagementAccessoryImageViewValue, + "Favorited", + "The accessibility value of the favorite accessory view on the bookmark management view must be \"Favorited\"." ) } - func test_favorites_canBeManuallyAddedTo_byClickingAddFavoriteInNewTabPage() throws { + func test_favorites_appearInNewTabFavoritesGrid() throws { + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: false) + XCTAssertTrue( + bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + if bookmarksDialogAddToFavoritesCheckboxValue == false { + bookmarksDialogAddToFavoritesCheckbox.click() + } + app.typeKey(.escape, modifierFlags: []) // Exit dialog + + toggleBookmarksBarShowFavoritesOn() + let unwrappedPageTitle = try XCTUnwrap(pageTitle, "It wasn't possible to unwrap pageTitle") + let firstFavoriteInGridMatchingTitle = app.staticTexts["HomePage.Models.FavoriteModel.\(unwrappedPageTitle)"].firstMatch + + XCTAssertTrue( + firstFavoriteInGridMatchingTitle.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The favorited item in the grid did not become available in a reasonable timeframe." + ) + } + + func test_favorites_canBeRemovedFromAddressBarBookmarkDialog() { + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: false) + XCTAssertTrue( + bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + if bookmarksDialogAddToFavoritesCheckboxValue == false { + bookmarksDialogAddToFavoritesCheckbox.click() // Favorite the bookmark + } + app.typeKey(.escape, modifierFlags: []) // Exit dialog + + XCTAssertTrue( + addressBarBookmarkButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Address bar bookmark button didn't load with the expected title in a reasonable timeframe." + ) + let addressBarBookmarkButtonValue = try? XCTUnwrap( + addressBarBookmarkButton.value as? String, + "It wasn't possible to get the value of the bookmarks management accessory image view as String" + ) + XCTAssertEqual( + addressBarBookmarkButtonValue, + "Bookmarked", + "The accessibility value of the Address Bar Bookmark Button must be \"Bookmarked\"." + ) + addressBarBookmarkButton.click() + XCTAssertTrue( + bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarksDialogAddToFavoritesCheckboxNewValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + if bookmarksDialogAddToFavoritesCheckboxNewValue == true { + bookmarksDialogAddToFavoritesCheckbox.click() // Unfavorite the bookmark + } + let bookmarksDialogAddToFavoritesCheckboxLastValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + let addToFavoritesLabel = "Add to Favorites" + + XCTAssertEqual( + bookmarksDialogAddToFavoritesCheckboxLastValue, + false, + "The favorite checkbox in the add bookmark dialog must now be unchecked" + ) + XCTAssertEqual( + bookmarksDialogAddToFavoritesCheckbox.label, + addToFavoritesLabel, + "The label of the add to favorites checkbox must now be \"\(addToFavoritesLabel)\"" + ) + } + + func test_favorites_canBeRemovedFromManageBookmarks() { + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: false) + XCTAssertTrue( + bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + if bookmarksDialogAddToFavoritesCheckboxValue == false { + bookmarksDialogAddToFavoritesCheckbox.click() // Favorite the bookmark + } + app.typeKey(.escape, modifierFlags: []) // Exit dialog + + bookmarksMenu.clickAfterExistenceTestSucceeds() + manageBookmarksMenuItem.clickAfterExistenceTestSucceeds() + bookmarkTableCellViewFavIconImageView.hoverAfterExistenceTestSucceeds() + bookmarkTableCellViewMenuButton.clickAfterExistenceTestSucceeds() + contextualMenuRemoveBookmarkFromFavoritesMenuItem.clickAfterExistenceTestSucceeds() + + XCTAssertTrue( + bookmarksManagementAccessoryImageView.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "Bookmarks accessory view favorites indicator didn't disappear from the view in a reasonable timeframe." + ) + } + + func test_favorites_canBeRemovedFromNewTabViaContextClick() throws { + toggleBookmarksBarShowFavoritesOn() + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: false) + XCTAssertTrue( + bookmarksDialogAddToFavoritesCheckbox.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The add to favorites checkbox in the add bookmark dialog didn't appear with the expected title in a reasonable timeframe." + ) + let bookmarksDialogAddToFavoritesCheckboxValue = try? XCTUnwrap( + bookmarksDialogAddToFavoritesCheckbox.value as? Bool, + "It wasn't possible to get the value of the bookmarks dialog's add to favorites checkbox as Bool" + ) + if bookmarksDialogAddToFavoritesCheckboxValue == false { + bookmarksDialogAddToFavoritesCheckbox.click() // Favorite the bookmark + } + app.typeKey(.escape, modifierFlags: []) // Exit dialog + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close all windows + app.typeKey("n", modifierFlags: .command) // New window + + let unwrappedPageTitle = try XCTUnwrap(pageTitle, "It wasn't possible to unwrap pageTitle") + let firstFavoriteInGridMatchingTitle = app.staticTexts["HomePage.Models.FavoriteModel.\(unwrappedPageTitle)"].firstMatch + XCTAssertTrue( + firstFavoriteInGridMatchingTitle.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The favorited item in the grid did not become available in a reasonable timeframe." + ) + firstFavoriteInGridMatchingTitle.rightClick() + removeFavoritesContextMenuItem.clickAfterExistenceTestSucceeds() + + XCTAssertTrue( + firstFavoriteInGridMatchingTitle.waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "The favorited item in the grid did not disappear in a reasonable timeframe." + ) + } + + func test_bookmark_canBeRemovedViaAddressBarIconClick() { + toggleShowBookmarksBarAlwaysOn() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) + + addressBarBookmarkButton.clickAfterExistenceTestSucceeds() + defaultBookmarkOtherButton.clickAfterExistenceTestSucceeds() + app.typeKey(.escape, modifierFlags: []) // Exit dialog + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + + XCTAssertTrue( + app.staticTexts[pageTitle].waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "Since there is no bookmark of the page, and we show bookmarks in the bookmark bar, the title of the page should not appear in a new browser window anywhere." + ) + } + + func test_bookmark_canBeRemovedFromBookmarksTabViaHoverAndContextMenu() { + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) + + bookmarksMenu.clickAfterExistenceTestSucceeds() + manageBookmarksMenuItem.clickAfterExistenceTestSucceeds() + bookmarkTableCellViewFavIconImageView.hoverAfterExistenceTestSucceeds() + bookmarkTableCellViewMenuButton.clickAfterExistenceTestSucceeds() + contextualMenuDeleteBookmarkMenuItem.clickAfterExistenceTestSucceeds() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + + XCTAssertTrue( + app.staticTexts[pageTitle].waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "Since there is no bookmark of the page, and we show bookmarks in the bookmark bar, the title of the page should not appear in a new browser window anywhere." + ) + } + + func test_bookmark_canBeRemovedFromBookmarksBarViaRightClick() { + toggleShowBookmarksBarAlwaysOn() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + + XCTAssertTrue( + bookmarksBarCollectionView.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarks bar collection view failed to become available in a reasonable timeframe." + ) + let bookmarkBarBookmarkIcon = bookmarksBarCollectionView.images.firstMatch + XCTAssertTrue( + bookmarkBarBookmarkIcon.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The bookmarks bar bookmark icon failed to become available in a reasonable timeframe." + ) + let bookmarkBarBookmarkIconCoordinate = bookmarkBarBookmarkIcon.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) + let deleteContextMenuItemCoordinate = bookmarkBarBookmarkIcon.coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 9.0)) + bookmarkBarBookmarkIconCoordinate.rightClick() + deleteContextMenuItemCoordinate.click() + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + + XCTAssertTrue( + app.staticTexts[pageTitle].waitForNonExistence(timeout: UITests.Timeouts.elementExistence), + "Since there is no bookmark of the page, and we show bookmarks in the bookmark bar, the title of the page should not appear in a new browser window anywhere. In this specific test, it is highly probable that the reason for a failure (when this area of the app appears to be working correctly) is the contextual menu being rearranged, since it has to address the menu elements by coordinate." + ) + } +} + +private extension BookmarksAndFavoritesTests { + func toggleShowBookmarksBarAlwaysOn() { app.typeKey(",", modifierFlags: [.command]) // Open settings XCTAssertTrue( @@ -368,58 +640,70 @@ class BookmarksAndFavoritesTests: XCTestCase { "The user settings appearance section button didn't become available in a reasonable timeframe." ) settingsAppearanceButton.click(forDuration: 0.5, thenDragTo: settingsAppearanceButton) - XCTAssertTrue( - showFavoritesPreferenceToggle.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The user settings appearance section show favorites toggle didn't become available in a reasonable timeframe." + showBookmarksBarPreferenceToggle.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The toggle for showing the bookmarks bar didn't become available in a reasonable timeframe." ) - let showFavoritesPreferenceToggleIsChecked = showFavoritesPreferenceToggle.value as? Bool - if showFavoritesPreferenceToggleIsChecked == false { // If untoggled, - showFavoritesPreferenceToggle.click() // Toggle "show favorites" + + let showBookmarksBarIsChecked = try? XCTUnwrap( + showBookmarksBarPreferenceToggle.value as? Bool, + "It wasn't possible to get the \"Show bookmarks bar\" value as a Bool" + ) + if showBookmarksBarIsChecked == false { + showBookmarksBarPreferenceToggle.click() } + XCTAssertTrue( + showBookmarksBarPopup.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Show Bookmarks Bar\" popup button didn't become available in a reasonable timeframe." + ) + showBookmarksBarPopup.click() + XCTAssertTrue( + showBookmarksBarAlways.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The \"Show Bookmarks Bar Always\" button didn't become available in a reasonable timeframe." + ) + showBookmarksBarAlways.click() + } + + /// Make sure that appearance tab has been used to set "show favorites" to true + func toggleBookmarksBarShowFavoritesOn() { + app.typeKey(",", modifierFlags: [.command]) // Open settings - app.typeKey("n", modifierFlags: [.command]) - let addFavoriteInNewTabButton = app.staticTexts["Add Favorite"] XCTAssertTrue( - addFavoriteInNewTabButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The add favorite button in the new tab webview did not become available in a reasonable timeframe." + settingsAppearanceButton.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The user settings appearance section button didn't become available in a reasonable timeframe." ) - addFavoriteInNewTabButton.click() - let pageTitleForAddFavoriteDialog: String = try XCTUnwrap(pageTitle, "Couldn't unwrap page title") - let urlForAddFavoriteDialog = try XCTUnwrap(urlForBookmarksBar, "Couldn't unwrap page url") - app.typeText("\(pageTitleForAddFavoriteDialog)\t") - app.typeText("\(urlForAddFavoriteDialog)\r") - let newFavorite = app.otherElements.staticTexts[pageTitleForAddFavoriteDialog] + settingsAppearanceButton.click(forDuration: 0.5, thenDragTo: settingsAppearanceButton) XCTAssertTrue( - newFavorite.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "The new favorite on the new tab page did not become available in a reasonable timeframe." + showFavoritesPreferenceToggle.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "The user settings appearance section show favorites toggle didn't become available in a reasonable timeframe." ) + let showFavoritesPreferenceToggleIsChecked = showFavoritesPreferenceToggle.value as? Bool + if showFavoritesPreferenceToggleIsChecked == false { // If untoggled, + showFavoritesPreferenceToggle.click() // Toggle "show favorites" + } + app.typeKey("w", modifierFlags: [.command, .option, .shift]) // Close settings and everything else + app.typeKey("n", modifierFlags: .command) // New window } - func test_favorites_canBeAddedToFromManageBookmarksView() { + /// Open the initial site to be bookmarked, bookmarking it and/or escaping out of the dialog only if needed + /// - Parameter bookmarkingViaDialog: open bookmark dialog, adding bookmark + /// - Parameter escapingDialog: `esc` key to leave dialog + func openSiteToBookmark(bookmarkingViaDialog: Bool, escapingDialog: Bool) { XCTAssertTrue( addressBarTextField.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The address bar text field didn't become available in a reasonable timeframe." ) - addressBarTextField.typeText("\(urlForBookmarksBar.absoluteString)\r") + addressBarTextField.typeURL(urlForBookmarksBar) XCTAssertTrue( app.windows.webViews[pageTitle].waitForExistence(timeout: UITests.Timeouts.elementExistence), "Visited site didn't load with the expected title in a reasonable timeframe." ) - app.typeKey("d", modifierFlags: [.command]) // Add bookmark - app.typeKey(.escape, modifierFlags: []) // Exit dialog - XCTAssertTrue( - bookmarksMenu.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Bookmarks menu didn't load with the expected title in a reasonable timeframe." - ) - bookmarksMenu.click() - XCTAssertTrue( - manageBookmarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Manage bookmarks menu item didn't load with the expected title in a reasonable timeframe." - ) - manageBookmarksMenuItem.click() - app.staticTexts[pageTitle].hover() - + if bookmarkingViaDialog { + app.typeKey("d", modifierFlags: [.command]) // Add bookmark + if escapingDialog { + app.typeKey(.escape, modifierFlags: []) // Exit dialog + } + } } } From aba9a990c10fc6192a0f8d9fe749924dabf41523 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:04:17 +0200 Subject: [PATCH 104/108] Whitespace warnings --- UITests/BookmarksAndFavoritesTests.swift | 12 ++++++------ UITests/Common/XCUIElementExtension.swift | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/UITests/BookmarksAndFavoritesTests.swift b/UITests/BookmarksAndFavoritesTests.swift index 2e5e842277..24e443435c 100644 --- a/UITests/BookmarksAndFavoritesTests.swift +++ b/UITests/BookmarksAndFavoritesTests.swift @@ -568,13 +568,13 @@ class BookmarksAndFavoritesTests: XCTestCase { app.typeKey("w", modifierFlags: [.command, .option, .shift]) app.typeKey("n", modifierFlags: .command) openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) - + addressBarBookmarkButton.clickAfterExistenceTestSucceeds() defaultBookmarkOtherButton.clickAfterExistenceTestSucceeds() app.typeKey(.escape, modifierFlags: []) // Exit dialog app.typeKey("w", modifierFlags: [.command, .option, .shift]) app.typeKey("n", modifierFlags: .command) - + XCTAssertTrue( app.staticTexts[pageTitle].waitForNonExistence(timeout: UITests.Timeouts.elementExistence), "Since there is no bookmark of the page, and we show bookmarks in the bookmark bar, the title of the page should not appear in a new browser window anywhere." @@ -585,7 +585,7 @@ class BookmarksAndFavoritesTests: XCTestCase { app.typeKey("w", modifierFlags: [.command, .option, .shift]) app.typeKey("n", modifierFlags: .command) openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) - + bookmarksMenu.clickAfterExistenceTestSucceeds() manageBookmarksMenuItem.clickAfterExistenceTestSucceeds() bookmarkTableCellViewFavIconImageView.hoverAfterExistenceTestSucceeds() @@ -593,7 +593,7 @@ class BookmarksAndFavoritesTests: XCTestCase { contextualMenuDeleteBookmarkMenuItem.clickAfterExistenceTestSucceeds() app.typeKey("w", modifierFlags: [.command, .option, .shift]) app.typeKey("n", modifierFlags: .command) - + XCTAssertTrue( app.staticTexts[pageTitle].waitForNonExistence(timeout: UITests.Timeouts.elementExistence), "Since there is no bookmark of the page, and we show bookmarks in the bookmark bar, the title of the page should not appear in a new browser window anywhere." @@ -607,7 +607,7 @@ class BookmarksAndFavoritesTests: XCTestCase { openSiteToBookmark(bookmarkingViaDialog: true, escapingDialog: true) app.typeKey("w", modifierFlags: [.command, .option, .shift]) app.typeKey("n", modifierFlags: .command) - + XCTAssertTrue( bookmarksBarCollectionView.waitForExistence(timeout: UITests.Timeouts.elementExistence), "The bookmarks bar collection view failed to become available in a reasonable timeframe." @@ -623,7 +623,7 @@ class BookmarksAndFavoritesTests: XCTestCase { deleteContextMenuItemCoordinate.click() app.typeKey("w", modifierFlags: [.command, .option, .shift]) app.typeKey("n", modifierFlags: .command) - + XCTAssertTrue( app.staticTexts[pageTitle].waitForNonExistence(timeout: UITests.Timeouts.elementExistence), "Since there is no bookmark of the page, and we show bookmarks in the bookmark bar, the title of the page should not appear in a new browser window anywhere. In this specific test, it is highly probable that the reason for a failure (when this area of the app appears to be working correctly) is the contextual menu being rearranged, since it has to address the menu elements by coordinate." diff --git a/UITests/Common/XCUIElementExtension.swift b/UITests/Common/XCUIElementExtension.swift index e0667e958a..1e0d0ba991 100644 --- a/UITests/Common/XCUIElementExtension.swift +++ b/UITests/Common/XCUIElementExtension.swift @@ -62,7 +62,7 @@ extension XCUIElement { self.typeText("\r") } } - + func clickAfterExistenceTestSucceeds() { XCTAssertTrue( self.waitForExistence(timeout: UITests.Timeouts.elementExistence), @@ -70,7 +70,7 @@ extension XCUIElement { ) self.click() } - + func hoverAfterExistenceTestSucceeds() { XCTAssertTrue( self.waitForExistence(timeout: UITests.Timeouts.elementExistence), From faebda4d0d69dc987fc7dade7026c191ef838312 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:49:37 +0200 Subject: [PATCH 105/108] Move function to extension --- UITests/BookmarksAndFavoritesTests.swift | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/UITests/BookmarksAndFavoritesTests.swift b/UITests/BookmarksAndFavoritesTests.swift index 24e443435c..fc791c10bc 100644 --- a/UITests/BookmarksAndFavoritesTests.swift +++ b/UITests/BookmarksAndFavoritesTests.swift @@ -96,15 +96,6 @@ class BookmarksAndFavoritesTests: XCTestCase { app.typeKey("n", modifierFlags: .command) } - func resetBookmarks() { - app.typeKey("n", modifierFlags: [.command]) // Can't use debug menu without a window - XCTAssertTrue( - resetBookMarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), - "Reset bookmarks menu item didn't become available in a reasonable timeframe." - ) - resetBookMarksMenuItem.click() - } - func test_bookmarks_canBeAddedTo_withContextClickBookmarkThisPage() { openSiteToBookmark(bookmarkingViaDialog: false, escapingDialog: false) app.windows.webViews[pageTitle].rightClick() @@ -632,6 +623,17 @@ class BookmarksAndFavoritesTests: XCTestCase { } private extension BookmarksAndFavoritesTests { + /// Reset the bookmarks so we can rely on a single bookmark's existence + func resetBookmarks() { + app.typeKey("n", modifierFlags: [.command]) // Can't use debug menu without a window + XCTAssertTrue( + resetBookMarksMenuItem.waitForExistence(timeout: UITests.Timeouts.elementExistence), + "Reset bookmarks menu item didn't become available in a reasonable timeframe." + ) + resetBookMarksMenuItem.click() + } + + /// Make sure that we can reply on the bookmarks bar always appearing func toggleShowBookmarksBarAlwaysOn() { app.typeKey(",", modifierFlags: [.command]) // Open settings From d7aa557e9b700105d1dc1e1401ffa1cb14a39594 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:56:48 +0200 Subject: [PATCH 106/108] Workaround for typing problem --- UITests/Common/XCUIElementExtension.swift | 26 ++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/UITests/Common/XCUIElementExtension.swift b/UITests/Common/XCUIElementExtension.swift index 1e0d0ba991..f620744c62 100644 --- a/UITests/Common/XCUIElementExtension.swift +++ b/UITests/Common/XCUIElementExtension.swift @@ -49,18 +49,24 @@ extension XCUIElement { /// will be pressed. func typeURL(_ url: URL, pressingEnter: Bool = true) { let urlString = url.absoluteString - let urlParts = urlString.split(separator: ":") - var completedURLSections = 0 - for urlPart in urlParts { - self.typeText(String(urlPart)) - completedURLSections += 1 - if completedURLSections != urlParts.count { - self.typeKey(":", modifierFlags: []) - } + for character in urlString { + self.typeKey("\(character)", modifierFlags: []) } if pressingEnter { - self.typeText("\r") - } + self.typeText("\r") + } +// let urlParts = urlString.split(separator: ":") +// var completedURLSections = 0 +// for urlPart in urlParts { +// self.typeText(String(urlPart)) +// completedURLSections += 1 +// if completedURLSections != urlParts.count { +// self.typeKey(":", modifierFlags: []) +// } +// } +// if pressingEnter { +// self.typeText("\r") +// } } func clickAfterExistenceTestSucceeds() { From 6c4997402dd62414b3175428d9dc6bb3a9cb20bd Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:04:57 +0200 Subject: [PATCH 107/108] Restore previous typer --- UITests/Common/XCUIElementExtension.swift | 26 +++++++++-------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/UITests/Common/XCUIElementExtension.swift b/UITests/Common/XCUIElementExtension.swift index f620744c62..1e0d0ba991 100644 --- a/UITests/Common/XCUIElementExtension.swift +++ b/UITests/Common/XCUIElementExtension.swift @@ -49,24 +49,18 @@ extension XCUIElement { /// will be pressed. func typeURL(_ url: URL, pressingEnter: Bool = true) { let urlString = url.absoluteString - for character in urlString { - self.typeKey("\(character)", modifierFlags: []) + let urlParts = urlString.split(separator: ":") + var completedURLSections = 0 + for urlPart in urlParts { + self.typeText(String(urlPart)) + completedURLSections += 1 + if completedURLSections != urlParts.count { + self.typeKey(":", modifierFlags: []) + } } if pressingEnter { - self.typeText("\r") - } -// let urlParts = urlString.split(separator: ":") -// var completedURLSections = 0 -// for urlPart in urlParts { -// self.typeText(String(urlPart)) -// completedURLSections += 1 -// if completedURLSections != urlParts.count { -// self.typeKey(":", modifierFlags: []) -// } -// } -// if pressingEnter { -// self.typeText("\r") -// } + self.typeText("\r") + } } func clickAfterExistenceTestSucceeds() { From 4e6205544d804ce2e7f34a47f0a027aceebf5803 Mon Sep 17 00:00:00 2001 From: Halle <378795+Halle@users.noreply.github.com> Date: Tue, 9 Apr 2024 15:16:32 +0200 Subject: [PATCH 108/108] Comment explaining why this test uses coords --- UITests/BookmarksAndFavoritesTests.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/UITests/BookmarksAndFavoritesTests.swift b/UITests/BookmarksAndFavoritesTests.swift index fc791c10bc..bf06b4aac6 100644 --- a/UITests/BookmarksAndFavoritesTests.swift +++ b/UITests/BookmarksAndFavoritesTests.swift @@ -592,6 +592,19 @@ class BookmarksAndFavoritesTests: XCTestCase { } func test_bookmark_canBeRemovedFromBookmarksBarViaRightClick() { +// This test uses coordinates (instead of accessibility IDs) to address the elements of the right click. As the writer of this test, I see this +// as a fragile test hook. However, I think it is preferable to making changes to the UI element it tests for this test alone. The reason is +// that the bookmark item on the bookmark bar isn't yet an accessibility-enabled UI element and doesn't appear to have a natural anchor point +// from which we can set its accessibility values without redesigning it. However, redesigning a road-tested UI element for a single test isn't a +// good idea, since the road-testing is also (valuable) testing and we don't want a single test to be the driver of a possible behavioral +// change in existing interface. +// +// My advice is to keep this as-is for now, with an awareness that it can fail if the coordinates of the items in the right-click menu change, +// or if the system where the testing is done has accessibility settings which change scaling. When the time comes to update this element, into +// SwiftUI, or into a general accessibility revision (for end-user accessibility rather than UI test accessibility), that will be the natural +// time to correct this test and give it accessibility ID access. Until then, I have added some hinting in the failure reason to explain why +// this test can fail while the app is working correctly. -Halle Winkler + toggleShowBookmarksBarAlwaysOn() app.typeKey("w", modifierFlags: [.command, .option, .shift]) app.typeKey("n", modifierFlags: .command)