From f8834baff1ff70810ebed6f764557405875e0dbd Mon Sep 17 00:00:00 2001 From: Juan Manuel Pereira Date: Thu, 15 Aug 2024 10:59:56 -0300 Subject: [PATCH] Add bookmarks search UI tests --- DuckDuckGo.xcodeproj/project.pbxproj | 8 + .../View/BookmarkListViewController.swift | 6 + ...okmarkManagementDetailViewController.swift | 4 + .../BookmarksEmptyStateContent.swift | 12 + DuckDuckGo/Menus/MainMenu.swift | 40 ++- DuckDuckGo/Menus/MainMenuActions.swift | 5 + .../View/NavigationBarViewController.swift | 1 + UITests/BookmarkSearchTests.swift | 337 ++++++++++++++++++ UITests/Common/BookmarksUtilites.swift | 95 +++++ 9 files changed, 489 insertions(+), 19 deletions(-) create mode 100644 UITests/BookmarkSearchTests.swift create mode 100644 UITests/Common/BookmarksUtilites.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 074739e6fe..c228a7a7f4 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -2541,6 +2541,8 @@ BB7B5F992C4ED73800BA4AF8 /* BookmarksSearchAndSortMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB7B5F972C4ED73800BA4AF8 /* BookmarksSearchAndSortMetrics.swift */; }; BBB881882C4029BA001247C6 /* BookmarkListTreeControllerSearchDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB881872C4029BA001247C6 /* BookmarkListTreeControllerSearchDataSource.swift */; }; BBB881892C4029BA001247C6 /* BookmarkListTreeControllerSearchDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB881872C4029BA001247C6 /* BookmarkListTreeControllerSearchDataSource.swift */; }; + BBBB65402C77BB9400E69AC6 /* BookmarkSearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBBB653F2C77BB9400E69AC6 /* BookmarkSearchTests.swift */; }; + BBBB65422C77C46100E69AC6 /* BookmarksUtilites.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBBB65412C77C46100E69AC6 /* BookmarksUtilites.swift */; }; BBBEE1BF2C4FF63600035ABA /* SortBookmarksViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBBEE1BE2C4FF63600035ABA /* SortBookmarksViewModelTests.swift */; }; BBBEE1C02C4FF63600035ABA /* SortBookmarksViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBBEE1BE2C4FF63600035ABA /* SortBookmarksViewModelTests.swift */; }; BBC063E82C5A9E4B007BDC18 /* BookmarkManagementDetailViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBC063E72C5A9E4B007BDC18 /* BookmarkManagementDetailViewModelTests.swift */; }; @@ -4237,6 +4239,8 @@ BB5789712B2CA70F0009DFE2 /* DataBrokerProtectionSubscriptionEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionSubscriptionEventHandler.swift; sourceTree = ""; }; BB7B5F972C4ED73800BA4AF8 /* BookmarksSearchAndSortMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksSearchAndSortMetrics.swift; sourceTree = ""; }; BBB881872C4029BA001247C6 /* BookmarkListTreeControllerSearchDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkListTreeControllerSearchDataSource.swift; sourceTree = ""; }; + BBBB653F2C77BB9400E69AC6 /* BookmarkSearchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkSearchTests.swift; sourceTree = ""; }; + BBBB65412C77C46100E69AC6 /* BookmarksUtilites.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksUtilites.swift; sourceTree = ""; }; BBBEE1BE2C4FF63600035ABA /* SortBookmarksViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortBookmarksViewModelTests.swift; sourceTree = ""; }; BBC063E72C5A9E4B007BDC18 /* BookmarkManagementDetailViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkManagementDetailViewModelTests.swift; sourceTree = ""; }; BBE013E92C5BFD660025F2C6 /* BookmarksEmptyStateContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksEmptyStateContent.swift; sourceTree = ""; }; @@ -6165,6 +6169,7 @@ EE9D81C22BC57A3700338BE3 /* StateRestorationTests.swift */, 7B4CE8E626F02134009134B1 /* TabBarTests.swift */, 56A054522C2592CE007D8FAB /* OnboardingUITests.swift */, + BBBB653F2C77BB9400E69AC6 /* BookmarkSearchTests.swift */, ); path = UITests; sourceTree = ""; @@ -8560,6 +8565,7 @@ children = ( EE02D4192BB4609900DBE6B3 /* UITests.swift */, EEBCE6812BA444FA00B9DF00 /* XCUIElementExtension.swift */, + BBBB65412C77C46100E69AC6 /* BookmarksUtilites.swift */, ); path = Common; sourceTree = ""; @@ -11282,12 +11288,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + BBBB65422C77C46100E69AC6 /* BookmarksUtilites.swift in Sources */, 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 */, EE02D4212BB460FE00DBE6B3 /* StringExtension.swift in Sources */, + BBBB65402C77BB9400E69AC6 /* BookmarkSearchTests.swift in Sources */, 56A054532C2592CE007D8FAB /* OnboardingUITests.swift in Sources */, EE9D81C32BC57A3700338BE3 /* StateRestorationTests.swift in Sources */, EEC7BE2E2BC6C09500F86835 /* AddressBarKeyboardShortcutsTests.swift in Sources */, diff --git a/DuckDuckGo/Bookmarks/View/BookmarkListViewController.swift b/DuckDuckGo/Bookmarks/View/BookmarkListViewController.swift index 43a1450afb..f79625c2a8 100644 --- a/DuckDuckGo/Bookmarks/View/BookmarkListViewController.swift +++ b/DuckDuckGo/Bookmarks/View/BookmarkListViewController.swift @@ -37,7 +37,9 @@ final class BookmarkListViewController: NSViewController { private lazy var stackView = NSStackView() private lazy var newBookmarkButton = MouseOverButton(image: .addBookmark, target: self, action: #selector(newBookmarkButtonClicked)) private lazy var newFolderButton = MouseOverButton(image: .addFolder, target: self, action: #selector(newFolderButtonClicked)) + .withAccessibilityIdentifier("BookmarkListViewController.newFolderButton") private lazy var searchBookmarksButton = MouseOverButton(image: .searchBookmarks, target: self, action: #selector(searchBookmarkButtonClicked)) + .withAccessibilityIdentifier("BookmarkListViewController.searchBookmarksButton") private lazy var sortBookmarksButton = MouseOverButton(image: .bookmarkSortAsc, target: self, action: #selector(sortBookmarksButtonClicked)) private var isSearchVisible = false @@ -50,10 +52,14 @@ final class BookmarkListViewController: NSViewController { private lazy var emptyState = NSView() private lazy var emptyStateTitle = NSTextField() + .withAccessibilityIdentifier(BookmarksEmptyStateContent.titleAccessibilityIdentifier) private lazy var emptyStateMessage = NSTextField() + .withAccessibilityIdentifier(BookmarksEmptyStateContent.descriptionAccessibilityIdentifier) private lazy var emptyStateImageView = NSImageView(image: .bookmarksEmpty) + .withAccessibilityIdentifier(BookmarksEmptyStateContent.imageAccessibilityIdentifier) private lazy var importButton = NSButton(title: UserText.importBookmarksButtonTitle, target: self, action: #selector(onImportClicked)) private lazy var searchBar = NSSearchField() + .withAccessibilityIdentifier("BookmarkListViewController.searchBar") private var boxDividerTopConstraint = NSLayoutConstraint() private var cancellables = Set() diff --git a/DuckDuckGo/Bookmarks/View/BookmarkManagementDetailViewController.swift b/DuckDuckGo/Bookmarks/View/BookmarkManagementDetailViewController.swift index 71243f37d6..c65fa7f350 100644 --- a/DuckDuckGo/Bookmarks/View/BookmarkManagementDetailViewController.swift +++ b/DuckDuckGo/Bookmarks/View/BookmarkManagementDetailViewController.swift @@ -45,14 +45,18 @@ final class BookmarkManagementDetailViewController: NSViewController, NSMenuItem .withAccessibilityIdentifier("BookmarkManagementDetailViewController.sortItemsButton") lazy var searchBar = NSSearchField() + .withAccessibilityIdentifier("BookmarkManagementDetailViewController.searchBar") private lazy var separator = NSBox() private lazy var scrollView = NSScrollView() private lazy var tableView = NSTableView() private lazy var emptyState = NSView() private lazy var emptyStateImageView = NSImageView(image: .bookmarksEmpty) + .withAccessibilityIdentifier(BookmarksEmptyStateContent.imageAccessibilityIdentifier) private lazy var emptyStateTitle = NSTextField() + .withAccessibilityIdentifier(BookmarksEmptyStateContent.titleAccessibilityIdentifier) private lazy var emptyStateMessage = NSTextField() + .withAccessibilityIdentifier(BookmarksEmptyStateContent.descriptionAccessibilityIdentifier) private lazy var importButton = NSButton(title: UserText.importBookmarksButtonTitle, target: self, action: #selector(onImportClicked)) weak var delegate: BookmarkManagementDetailViewControllerDelegate? diff --git a/DuckDuckGo/Bookmarks/ViewModel/BookmarksEmptyStateContent.swift b/DuckDuckGo/Bookmarks/ViewModel/BookmarksEmptyStateContent.swift index 3a32c9c2ce..acbb8ee4ac 100644 --- a/DuckDuckGo/Bookmarks/ViewModel/BookmarksEmptyStateContent.swift +++ b/DuckDuckGo/Bookmarks/ViewModel/BookmarksEmptyStateContent.swift @@ -20,6 +20,18 @@ enum BookmarksEmptyStateContent { case noBookmarks case noSearchResults + static var titleAccessibilityIdentifier: String { + "BookmarksEmptyStateContent.emptyStateTitle" + } + + static var descriptionAccessibilityIdentifier: String { + "BookmarksEmptyStateContent.emptyStateMessage" + } + + static var imageAccessibilityIdentifier: String { + "BookmarksEmptyStateContent.emptyStateImageView" + } + var title: String { switch self { case .noBookmarks: return UserText.bookmarksEmptyStateTitle diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index fefb5754e8..78ff04f38b 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -285,28 +285,30 @@ final class MainMenu: NSMenu { } func buildBookmarksMenu() -> NSMenuItem { - NSMenuItem(title: UserText.bookmarks).submenu(bookmarksMenu.buildItems { - NSMenuItem(title: UserText.bookmarkThisPage, action: #selector(MainViewController.bookmarkThisPage), keyEquivalent: "d") - NSMenuItem(title: UserText.bookmarkAllTabs, action: #selector(MainViewController.bookmarkAllOpenTabs), keyEquivalent: [.command, .shift, "d"]) - manageBookmarksMenuItem - bookmarksMenuToggleBookmarksBarMenuItem - NSMenuItem.separator() + NSMenuItem(title: UserText.bookmarks) + .withAccessibilityIdentifier("MainMenu.bookmarks") + .submenu(bookmarksMenu.buildItems { + NSMenuItem(title: UserText.bookmarkThisPage, action: #selector(MainViewController.bookmarkThisPage), keyEquivalent: "d") + NSMenuItem(title: UserText.bookmarkAllTabs, action: #selector(MainViewController.bookmarkAllOpenTabs), keyEquivalent: [.command, .shift, "d"]) + manageBookmarksMenuItem + bookmarksMenuToggleBookmarksBarMenuItem + NSMenuItem.separator() - importBookmarksMenuItem - NSMenuItem(title: UserText.exportBookmarks, action: #selector(AppDelegate.openExportBookmarks)) - NSMenuItem.separator() + importBookmarksMenuItem + NSMenuItem(title: UserText.exportBookmarks, action: #selector(AppDelegate.openExportBookmarks)) + NSMenuItem.separator() - NSMenuItem(title: UserText.favorites) - .submenu(favoritesMenu.buildItems { - NSMenuItem(title: UserText.mainMenuHistoryFavoriteThisPage, action: #selector(MainViewController.favoriteThisPage)) - .withImage(.favorite) - .withAccessibilityIdentifier("MainMenu.favoriteThisPage") - NSMenuItem.separator() - }) - .withImage(.favorite) + NSMenuItem(title: UserText.favorites) + .submenu(favoritesMenu.buildItems { + NSMenuItem(title: UserText.mainMenuHistoryFavoriteThisPage, action: #selector(MainViewController.favoriteThisPage)) + .withImage(.favorite) + .withAccessibilityIdentifier("MainMenu.favoriteThisPage") + NSMenuItem.separator() + }) + .withImage(.favorite) - NSMenuItem.separator() - }) + NSMenuItem.separator() + }) } func buildWindowMenu() -> NSMenuItem { diff --git a/DuckDuckGo/Menus/MainMenuActions.swift b/DuckDuckGo/Menus/MainMenuActions.swift index 824d544a71..224891f89e 100644 --- a/DuckDuckGo/Menus/MainMenuActions.swift +++ b/DuckDuckGo/Menus/MainMenuActions.swift @@ -797,6 +797,11 @@ extension MainViewController { @objc func resetBookmarks(_ sender: Any?) { LocalBookmarkManager.shared.resetBookmarks() UserDefaults.standard.set(false, forKey: UserDefaultsWrapper.Key.homePageContinueSetUpImport.rawValue) + + + UserDefaults.standard.set(false, forKey: UserDefaultsWrapper.Key.bookmarksBarPromptShown.rawValue) + UserDefaults.standard.set(true, forKey: UserDefaultsWrapper.Key.onboardingFinished.rawValue) + UserDefaults.standard.set([String](), forKey: UserDefaultsWrapper.Key.pinnedViews.rawValue) } @objc func resetPinnedTabs(_ sender: Any?) { diff --git a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift index ed07f0746a..c014f10b3d 100644 --- a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift +++ b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift @@ -156,6 +156,7 @@ final class NavigationBarViewController: NSViewController { optionsButton.sendAction(on: .leftMouseDown) bookmarkListButton.sendAction(on: .leftMouseDown) + bookmarkListButton.setAccessibilityIdentifier("NavigationBarViewController.bookmarkListButton") downloadsButton.sendAction(on: .leftMouseDown) networkProtectionButton.sendAction(on: .leftMouseDown) passwordManagementButton.sendAction(on: .leftMouseDown) diff --git a/UITests/BookmarkSearchTests.swift b/UITests/BookmarkSearchTests.swift new file mode 100644 index 0000000000..a95a7ea281 --- /dev/null +++ b/UITests/BookmarkSearchTests.swift @@ -0,0 +1,337 @@ +// +// BookmarkSearchTests.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 BookmarkSearchTests: XCTestCase { + private var app: XCUIApplication! + + private enum AccessibilityIdentifiers { + static let bookmarkButton = "AddressBarButtonsViewController.bookmarkButton" + static let addressBarTextField = "AddressBarViewController.addressBarTextField" + static let manageBookmarksMenuItem = "MainMenu.manageBookmarksMenuItem" + static let bookmarksMenu = "MainMenu.bookmarks" + static let bookmarksPanelShortcutButton = "NavigationBarViewController.bookmarkListButton" + static let optionsButton = "NavigationBarViewController.optionsButton" + static let resetBookmarksMenuItem = "MainMenu.resetBookmarks" + static let searchBookmarksButton = "BookmarkListViewController.searchBookmarksButton" + static let bookmarksPanelSearchBar = "BookmarkListViewController.searchBar" + static let bookmarksManagerSearchBar = "BookmarkManagementDetailViewController.searchBar" + static let emptyStateTitle = "BookmarksEmptyStateContent.emptyStateTitle" + static let emptyStateMessage = "BookmarksEmptyStateContent.emptyStateMessage" + static let emptyStateImageView = "BookmarksEmptyStateContent.emptyStateImageView" + static let newFolderButton = "BookmarkListViewController.newFolderButton" + } + + override class func setUp() { + UITests.firstRun() + } + + override func setUpWithError() throws { + continueAfterFailure = false + app = XCUIApplication() + app.launchEnvironment["UITEST_MODE"] = "1" + app.launch() + BookmarkUtilities.resetBookmarks(app: app, resetMenuItem: app.menuItems[AccessibilityIdentifiers.resetBookmarksMenuItem]) + enforceSingleWindow() + } + + // MARK: - Tests + + func testEmptyStateWhenSearchingInPanel() { + addBookmarkAndOpenBookmarksPanel(bookmarkPageTitle: "Bookmark #1") + verifyEmptyState(in: app.popovers.firstMatch, with: AccessibilityIdentifiers.bookmarksPanelSearchBar, mode: .panel) + } + + func testEmptyStateWhenSearchingInManager() { + addBookmarkAndOpenBookmarksManager(bookmarkPageTitle: "Bookmark #1") + verifyEmptyState(in: app, with: AccessibilityIdentifiers.bookmarksManagerSearchBar, mode: .manager) + } + + func testFilteredResultsInPanel() { + addThreeBookmarks() + closeShowBookmarksBarAlert() + openBookmarksPanel() + searchInBookmarksPanel(for: "Bookmark #2") + assertOnlyBookmarkExists(on: app.outlines.firstMatch, bookmarkTitle: "Bookmark #2") + } + + func testFilteredResultsInManager() { + addThreeBookmarks() + openBookmarksManager() + searchInBookmarksManager(for: "Bookmark #2") + assertOnlyBookmarkExists(on: app.tables.firstMatch, bookmarkTitle: "Bookmark #2") + } + + func testShowInFolderFunctionalityOnBookmarksPanel() { + testShowInFolderFunctionality(in: .panel) + } + + func testShoInFolderFunctionalityOnBookmarksManager() { + testShowInFolderFunctionality(in: .manager) + } + + func testDragAndDropToReorderIsNotPossibleWhenInSearchOnBookmarksPanel() { + testDragAndDropToReorder(in: .panel) + } + + func testDragAndDropToReorderIsNotPossibleWhenInSearchOnBookmarksManager() { + testDragAndDropToReorder(in: .manager) + } + + func testSearchActionIsHiddenOnBookmarksPanelWhenUserHasNoBookmarks() { + openBookmarksPanel() + let bookmarksPanelPopover = app.popovers.firstMatch + XCTAssertFalse(bookmarksPanelPopover.buttons[AccessibilityIdentifiers.searchBookmarksButton].exists) + } + + // MARK: - Utilities + + private func enforceSingleWindow() { + app.typeKey("w", modifierFlags: [.command, .option, .shift]) + app.typeKey("n", modifierFlags: .command) + } + + private func addBookmarkAndOpenBookmarksPanel(bookmarkPageTitle: String, in folder: String? = nil) { + addBookmark(pageTitle: bookmarkPageTitle, in: folder) + closeShowBookmarksBarAlert() + openBookmarksPanel() + } + + private func addBookmarkAndOpenBookmarksManager(bookmarkPageTitle: String, in folder: String? = nil) { + addBookmark(pageTitle: bookmarkPageTitle, in: folder) + openBookmarksManager() + } + + private func addThreeBookmarks() { + ["Bookmark #1", "Bookmark #2", "Bookmark #3"].forEach { + addBookmark(pageTitle: $0) + openNewTab() + } + } + + private func addBookmark(pageTitle: String, in folder: String? = nil) { + let urlForBookmarksBar = UITests.simpleServedPage(titled: pageTitle) + BookmarkUtilities.openSiteToBookmark(app: app, + url: urlForBookmarksBar, + pageTitle: pageTitle, + bookmarkingViaDialog: true, + escapingDialog: true, + addressBarTextField: app.windows.textFields[AccessibilityIdentifiers.addressBarTextField], + folderName: folder) + } + + private func searchInBookmarksPanel(for title: String) { + bringFocusToBookmarksPanelSearchBar() + app.popovers.firstMatch.searchFields[AccessibilityIdentifiers.bookmarksPanelSearchBar].typeText(title) + } + + private func searchInBookmarksManager(for title: String) { + let searchField = app.searchFields[AccessibilityIdentifiers.bookmarksManagerSearchBar] + searchField.tap() + searchField.typeText(title) + } + + private func assertOnlyBookmarkExists(on element: XCUIElement, bookmarkTitle: String) { + XCTAssertTrue(element.staticTexts[bookmarkTitle].exists) + // Assert that other bookmarks do not exist + ["Bookmark #1", "Bookmark #2", "Bookmark #3"].filter { $0 != bookmarkTitle }.forEach { + XCTAssertFalse(element.staticTexts[$0].exists) + } + } + + private func verifyEmptyState(in element: XCUIElement, with accessibilityIdentifier: String, mode: BookmarkMode) { + if mode == .panel { + searchInBookmarksPanel(for: "No results") + } else { + searchInBookmarksManager(for: "No results") + } + assertEmptyState(in: element) + } + + private func assertEmptyState(in element: XCUIElement) { + let emptyStateTitle = element.staticTexts[AccessibilityIdentifiers.emptyStateTitle] + let emptyStateDescription = element.staticTexts[AccessibilityIdentifiers.emptyStateMessage] + let emptyStateImage = element.images[AccessibilityIdentifiers.emptyStateImageView] + + XCTAssertTrue(emptyStateImage.exists, "The empty state image does not exist.") + XCTAssertTrue(emptyStateTitle.exists, "The empty state title does not exist.") + XCTAssertTrue(emptyStateDescription.exists, "The empty state description does not exist.") + + XCTAssertEqual(emptyStateTitle.value as? String, "No bookmarks found") + XCTAssertEqual(emptyStateDescription.value as? String, "Try different search terms.") + } + + private func bringFocusToBookmarksPanelSearchBar() { + let popover = app.popovers.firstMatch + popover.buttons[AccessibilityIdentifiers.searchBookmarksButton].tap() + } + + private func openBookmarksPanel() { + BookmarkUtilities.showAndTapBookmarksPanelShortcut(app: app, bookmarksPanelShortcutButton: app.buttons[AccessibilityIdentifiers.bookmarksPanelShortcutButton]) + } + + private func openBookmarksManager() { + BookmarkUtilities.openBookmarksManager(app: app, manageBookmarksMenuItem: app.menuItems[AccessibilityIdentifiers.manageBookmarksMenuItem]) + } + + private func openNewTab() { + app.typeKey("t", modifierFlags: .command) + } + + private func testShowInFolderFunctionality(in mode: BookmarkMode) { + createFolderWithSubFolder() + openNewTab() + addBookmark(pageTitle: "Bookmark #1", in: "Folder #2") + closeShowBookmarksBarAlert() + + if mode == .panel { + openBookmarksPanel() + searchInBookmarksPanel(for: "Bookmark #1") + } else { + openBookmarksManager() + searchInBookmarksManager(for: "Bookmark #1") + } + + let result = app.staticTexts["Bookmark #1"] + result.rightClick() + let showInFolderMenuItem = app.menuItems["Show in Folder"] + XCTAssertTrue(showInFolderMenuItem.exists) + showInFolderMenuItem.tap() + + assertSearchBarVisibilityAfterShowInFolder(mode: mode) + assertFolderStructure(mode: mode) + } + + private func assertSearchBarVisibilityAfterShowInFolder(mode: BookmarkMode) { + if mode == .panel { + XCTAssertFalse(app.popovers.firstMatch.searchFields[AccessibilityIdentifiers.bookmarksPanelSearchBar].exists) + } else { + XCTAssertEqual(app.searchFields[AccessibilityIdentifiers.bookmarksManagerSearchBar].value as? String, "") + } + } + + private func assertFolderStructure(mode: BookmarkMode) { + let treeBookmarks: XCUIElement = mode == .panel ? app.popovers.firstMatch.outlines.firstMatch : app.outlines.firstMatch + + XCTAssertTrue(treeBookmarks.staticTexts["Folder #1"].exists) + if mode == .panel { + XCTAssertTrue(treeBookmarks.staticTexts["Bookmark #1"].exists) + XCTAssertTrue(treeBookmarks.staticTexts["Folder #2"].exists) + } else { + /// On the bookmarks manager the sidebar tree structure only has folders while the list has what's inside the selected folder in the tree. + XCTAssertTrue(treeBookmarks.staticTexts["Folder #2"].exists) + let bookmarksList = app.tables.firstMatch + XCTAssertTrue(bookmarksList.staticTexts["Bookmark #1"].exists) + } + } + + private func testDragAndDropToReorder(in mode: BookmarkMode) { + addThreeBookmarks() + if mode == .panel { + closeShowBookmarksBarAlert() + openBookmarksPanel() + } else { + openBookmarksManager() + } + searchInBookmarks(mode: mode) + + let thirdBookmarkCell = getThirdBookmarkCell(mode: mode) + dragAndDropBookmark(thirdBookmarkCell, mode: mode) + + if mode == .panel { + bringFocusToBookmarksPanelSearchBar() + } else { + clearSearchInBookmarksManager() + } + + verifyBookmarkOrder(expectedOrder: ["Bookmark #1", "Bookmark #2", "Bookmark #3"], mode: mode) + } + + private func searchInBookmarks(mode: BookmarkMode) { + if mode == .panel { + searchInBookmarksPanel(for: "Bookmark") + } else { + searchInBookmarksManager(for: "Bookmark") + } + } + + private func getThirdBookmarkCell(mode: BookmarkMode) -> XCUIElement { + if mode == .panel { + let treeBookmarks = app.popovers.firstMatch.outlines.firstMatch + return treeBookmarks.staticTexts["Bookmark #3"] + } else { + let bookmarksSearchResultsList = app.tables.firstMatch + return bookmarksSearchResultsList.staticTexts["Bookmark #3"] + } + } + + private func dragAndDropBookmark(_ thirdBookmarkCell: XCUIElement, mode: BookmarkMode) { + let startCoordinate = thirdBookmarkCell.coordinate(withNormalizedOffset: .zero) + let targetCoordinate = (mode == .panel ? app.popovers.firstMatch.outlines.firstMatch : app.tables.firstMatch).coordinate(withNormalizedOffset: .zero) + startCoordinate.press(forDuration: 0.1, thenDragTo: targetCoordinate) + } + + private func clearSearchInBookmarksManager() { + let searchField = app.searchFields[AccessibilityIdentifiers.bookmarksManagerSearchBar] + searchField.doubleTap() + app.typeKey(.delete, modifierFlags: []) + } + + private func verifyBookmarkOrder(expectedOrder: [String], mode: BookmarkMode) { + let rowCount = (mode == .panel ? app.popovers.firstMatch.outlines.firstMatch : app.tables.firstMatch).cells.count + XCTAssertEqual(rowCount, expectedOrder.count, "Row count does not match expected count.") + + for index in 0..