From 002b3a2238425b0dc680ca015b495f705363cf97 Mon Sep 17 00:00:00 2001 From: dragosb01 <134391433+dragosb01@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:56:59 +0200 Subject: [PATCH 01/45] Add MTE-4051 - tests for share options (#24091) --- firefox-ios/Client.xcodeproj/project.pbxproj | 4 + .../ExperimentIntegrationTests.xctestplan | 1 + .../Tests/PerformanceTestPlan.xctestplan | 1 + .../Tests/Smoketest1.xctestplan | 1 + .../Tests/Smoketest2.xctestplan | 1 + .../Tests/Smoketest3.xctestplan | 1 + .../Tests/Smoketest4.xctestplan | 1 + .../Tests/SyncIntegrationTestPlan.xctestplan | 1 + .../Tests/XCUITests/BaseTestCase.swift | 1 + .../Tests/XCUITests/ShareMenuTests.swift | 168 ++++++++++++++++++ 10 files changed, 180 insertions(+) create mode 100644 firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 0ca72cffd268..761cb0a5307e 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -1217,6 +1217,7 @@ ABEF80D92A2F283E003F52C4 /* CreditCardBottomSheetViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABEF80D82A2F283D003F52C4 /* CreditCardBottomSheetViewModelTests.swift */; }; B10997432A97251D00CC8860 /* UrlBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10997422A97251D00CC8860 /* UrlBarTests.swift */; }; B1158F2A2B5029F200AC9D70 /* URLValidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1158F292B5029F200AC9D70 /* URLValidationTests.swift */; }; + B126B8AA2D2FFD93002F4EFC /* ShareMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B126B8A92D2FFD93002F4EFC /* ShareMenuTests.swift */; }; B12DDFED2A8DE825008CE9CF /* ToolbarMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12DDFEC2A8DE825008CE9CF /* ToolbarMenuTests.swift */; }; B15058812AA0A878008B7382 /* OpeningScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15058802AA0A878008B7382 /* OpeningScreenTests.swift */; }; B1664E9E2B163B7A005D4C71 /* CreditCardsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1664E9D2B163B7A005D4C71 /* CreditCardsTests.swift */; }; @@ -8320,6 +8321,7 @@ B0CB471BA31B6922E598AA88 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Today.strings; sourceTree = ""; }; B10997422A97251D00CC8860 /* UrlBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlBarTests.swift; sourceTree = ""; }; B1158F292B5029F200AC9D70 /* URLValidationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLValidationTests.swift; sourceTree = ""; }; + B126B8A92D2FFD93002F4EFC /* ShareMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareMenuTests.swift; sourceTree = ""; }; B12DDFEC2A8DE825008CE9CF /* ToolbarMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarMenuTests.swift; sourceTree = ""; }; B15058802AA0A878008B7382 /* OpeningScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpeningScreenTests.swift; sourceTree = ""; }; B1664E9D2B163B7A005D4C71 /* CreditCardsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardsTests.swift; sourceTree = ""; }; @@ -10856,6 +10858,7 @@ 0B9D40781E8D5AC80059E664 /* XCUITests-Bridging-Header.h */, B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */, 78F28FBF2CB81FDF00DA862E /* InactiveTabsTest.swift */, + B126B8A92D2FFD93002F4EFC /* ShareMenuTests.swift */, ); path = XCUITests; sourceTree = ""; @@ -16077,6 +16080,7 @@ 39012F281F8ED262002E3D31 /* ScreenGraphTest.swift in Sources */, 0B305E1B1E3A98A900BE0767 /* BookmarksTests.swift in Sources */, 5FF4AF5C2CC69CC900BABC62 /* TodayWidgetTests.swift in Sources */, + B126B8AA2D2FFD93002F4EFC /* ShareMenuTests.swift in Sources */, D81127D81F84023B0050841D /* PhotonActionSheetTests.swift in Sources */, 3BFE4B501D34673D00DDF53F /* ThirdPartySearchTest.swift in Sources */, EB3A38A02032673E004C6E67 /* DatabaseFixtureTest.swift in Sources */, diff --git a/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan b/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan index 82265952832d..80b7ba508d95 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan @@ -98,6 +98,7 @@ "ReportSiteTests", "ScreenGraphTest", "SearchSettingsUITests", + "ShareMenuTests", "SiteLoadTest", "SyncUITests", "ThirdPartySearchTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan b/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan index 073540cb4ab7..a1656be13805 100644 --- a/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan @@ -80,6 +80,7 @@ "SearchSettingsUITests", "SearchTests", "SettingsTests", + "ShareMenuTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan index 6c6539ac33b4..e6818ebd58eb 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan @@ -209,6 +209,7 @@ "SearchTests\/testSearchIconOnAboutHome()", "SearchTests\/testSearchWithFirefoxOption()", "SettingsTests", + "ShareMenuTests", "SiteLoadTest", "SyncUITests", "SyncUITests\/testCreateAnAccountLink()", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan index f0eb51189ded..3850b95c79a0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan @@ -109,6 +109,7 @@ "SettingsTests\/testImageOnOff()", "SettingsTests\/testOpenMailAppSettings()", "SettingsTests\/testOpenSiriOption()", + "ShareMenuTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan index e98fd56cb03c..d375172210b2 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan @@ -144,6 +144,7 @@ "SettingsTests\/testOpenMailAppSettings()", "SettingsTests\/testOpenSiriOption()", "SettingsTests\/testSettingsOptionSubtitles()", + "ShareMenuTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan index 0c53151ef4f6..7a6298d3e000 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan @@ -149,6 +149,7 @@ "SearchTests\/testSearchSuggestions()", "SearchTests\/testSearchWithFirefoxOption()", "SettingsTests", + "ShareMenuTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan b/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan index 8410e09dd3bc..d54a8bd78b50 100644 --- a/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan @@ -98,6 +98,7 @@ "SearchSettingsUITests", "SearchTests", "SettingsTests", + "ShareMenuTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift index 00b04a1b8c17..d9eeb9458da6 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift @@ -428,6 +428,7 @@ class BaseTestCase: XCTestCase { navigator.nowAt(SettingsScreen) app.buttons["Done"].waitAndTap() } + func waitForElementsToExist(_ elements: [XCUIElement], timeout: TimeInterval = TIMEOUT, message: String? = nil) { var elementsDict = [XCUIElement: String]() for element in elements { diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift new file mode 100644 index 000000000000..05a710ebcd9e --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift @@ -0,0 +1,168 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +class ShareMenuTests: BaseTestCase { + // https://mozilla.testrail.io/index.php?/cases/view/2863631 + func testShareNormalWebsiteTabViaReminders() { + // Coudn't find a way to tap on reminders on iOS 16 + if #available(iOS 17, *) { + reachShareMenuLayoutAndSelectOption(option: "Reminders") + // The URL of the website is added in a new reminder + waitForElementsToExist( + [ + app.navigationBars["Reminders"], + app.links["http://" + url_3] + ] + ) + } + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864049 + func testShareNormalWebsitePrint() { + reachShareMenuLayoutAndSelectOption(option: "Print") + // The Print dialog appears + waitForElementsToExist( + [ + app.staticTexts["Printer"], + app.staticTexts["Copies"], + app.staticTexts["Paper Size"], + app.staticTexts["Letter"], + app.staticTexts["Orientation"], + app.staticTexts["Layout"] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864047 + func testShareNormalWebsiteSendLinkToDevice() { + reachShareMenuLayoutAndSelectOption(option: "Send Link to Device") + // If not signed in, the browser prompts you to sign in + waitForElementsToExist( + [ + app.staticTexts["You are not signed in to your account."], + app.staticTexts["Please open Firefox, go to Settings and sign in to continue."] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864048 + func testShareNormalWebsiteMarkup() { + reachShareMenuLayoutAndSelectOption(option: "Markup") + // The Markup tool opens + waitForElementsToExist( + [ + app.buttons["Undo"], + app.buttons["Redo"], + app.buttons["autofill"], + app.buttons["Done"], + app.buttons["Color picker"] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864046 + func testShareNormalWebsiteCopyUrl() { + reachShareMenuLayoutAndSelectOption(option: "Copy") + openNewTabAndValidateURLisPaste(url: url_3) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864073 + func testShareWebsiteReaderModeReminders() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Reminders") + // The URL of the website is added in a new reminder + waitForElementsToExist( + [ + app.navigationBars["Reminders"], + app.links.elementContainingText("test-mozilla-book.html") + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864082 + func testShareWebsiteReaderModePrint() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Print") + // The Print dialog appears + waitForElementsToExist( + [ + app.staticTexts["Printer"], + app.staticTexts["Copies"], + app.staticTexts["Paper Size"], + app.staticTexts["Letter"], + app.staticTexts["Orientation"], + app.staticTexts["Layout"] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864079 + func testShareWebsiteReaderModeCopy() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Copy") + openNewTabAndValidateURLisPaste(url: "test-mozilla-book.html") + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864080 + func testShareWebsiteReaderModeSendLink() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Send Link to Device") + // If not signed in, the browser prompts you to sign in + waitForElementsToExist( + [ + app.staticTexts["You are not signed in to your account."], + app.staticTexts["Please open Firefox, go to Settings and sign in to continue."] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864081 + func testShareWebsiteReaderModeMarkup() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Markup") + // The Markup tool opens + waitForElementsToExist( + [ + app.buttons["Undo"], + app.buttons["Redo"], + app.buttons["autofill"], + app.buttons["Done"], + app.buttons["Color picker"] + ] + ) + } + + private func reachReaderModeShareMenuLayoutAndSelectOption(option: String) { + navigator.openURL(path(forTestPage: "test-mozilla-book.html")) + waitUntilPageLoad() + navigator.nowAt(BrowserTab) + mozWaitForElementToNotExist(app.staticTexts["Fennec pasted from XCUITests-Runner"]) + app.buttons["Reader View"].waitAndTap() + navigator.goto(ToolsBrowserTabMenu) + // Tap the Share button in the menu + navigator.performAction(Action.ShareBrowserTabMenuOption) + app.collectionViews.cells[option].waitAndTap() + } + + private func reachShareMenuLayoutAndSelectOption(option: String) { + // Open a website in the browser + navigator.openURL(url_3) + waitForTabsButton() + navigator.goto(ToolsBrowserTabMenu) + // Tap the Share button in the menu + navigator.performAction(Action.ShareBrowserTabMenuOption) + app.collectionViews.cells[option].waitAndTap() + } + + private func openNewTabAndValidateURLisPaste(url: String) { + app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() + if #available(iOS 17, *) { + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 1.5) + } else { + navigator.performAction(Action.CloseURLBarOpen) + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 2) + } + mozWaitForElementToExist(app.tables["Context Menu"]) + app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].waitAndTap() + let urlBar = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] + mozWaitForValueContains(urlBar, value: url) + } +} From c9ca770d1a22bafad3aaee6ffc0040aa748a0c84 Mon Sep 17 00:00:00 2001 From: Clare So <1740517+clarmso@users.noreply.github.com> Date: Mon, 13 Jan 2025 04:57:34 -0500 Subject: [PATCH 02/45] Refactor MTE-3733 Use waitAndTap() throughout XCUITest and SyncIntegrationTests (#24050) * Use waitAndTap() throughout XCUITest and SyncIntegrationTests * Remove mozWait() right before waitAndTap() --- .../Tests/XCUITests/ActivityStreamTest.swift | 2 +- .../Tests/XCUITests/AddressesTests.swift | 29 +++-- .../Tests/XCUITests/AuthenticationTest.swift | 4 +- .../Tests/XCUITests/BaseTestCase.swift | 34 +++--- .../Tests/XCUITests/BookmarksTests.swift | 24 ++-- .../Tests/XCUITests/BrowsingPDFTests.swift | 11 +- .../Tests/XCUITests/ClipBoardTests.swift | 8 +- .../Tests/XCUITests/CreditCardsTests.swift | 74 ++++++------ .../Tests/XCUITests/DataManagementTests.swift | 10 +- .../Tests/XCUITests/DesktopModeTests.swift | 2 +- .../XCUITests/DomainAutocompleteTests.swift | 22 ++-- .../Tests/XCUITests/DownloadsTests.swift | 18 ++- .../Tests/XCUITests/DragAndDropTests.swift | 4 +- .../EngagementNotificationTests.swift | 4 +- .../ExperimentIntegrationTests.swift | 20 ++-- .../Tests/XCUITests/FindInPageTests.swift | 19 ++-- .../Tests/XCUITests/HistoryTests.swift | 31 +++-- .../Tests/XCUITests/HomeButtonTests.swift | 3 +- .../XCUITests/HomePageSettingsUITest.swift | 28 ++--- .../Tests/XCUITests/InactiveTabsTest.swift | 33 +++--- .../Tests/XCUITests/IntegrationTests.swift | 28 ++--- .../Tests/XCUITests/JumpBackInTests.swift | 4 +- .../Tests/XCUITests/LibraryTests.swift | 2 +- .../Tests/XCUITests/LoginsTests.swift | 48 ++++---- .../Tests/XCUITests/MicrosurveyTests.swift | 6 +- .../Tests/XCUITests/MultiWindowTests.swift | 12 +- .../Tests/XCUITests/NavigationTest.swift | 49 ++++---- .../Tests/XCUITests/NewTabSettings.swift | 4 +- .../Tests/XCUITests/NightModeTests.swift | 2 +- .../Tests/XCUITests/OnboardingTests.swift | 62 +++++----- .../XCUITests/PhotonActionSheetTests.swift | 8 +- .../Tests/XCUITests/PrivateBrowsingTest.swift | 10 +- .../Tests/XCUITests/ReadingListTests.swift | 24 ++-- .../Tests/XCUITests/ScreenGraphTest.swift | 8 +- .../XCUITests/SearchSettingsUITest.swift | 24 ++-- .../Tests/XCUITests/SearchTest.swift | 46 ++++---- .../Tests/XCUITests/SettingsTests.swift | 10 +- .../Tests/XCUITests/SiteLoadTest.swift | 2 +- .../Tests/XCUITests/SyncFAUITests.swift | 6 +- .../Tests/XCUITests/TabCounterTests.swift | 6 +- .../XCUITests/ThirdPartySearchTest.swift | 18 +-- .../Tests/XCUITests/TodayWidgetTests.swift | 107 +++++++++--------- .../Tests/XCUITests/ToolbarMenuTests.swift | 6 +- .../Tests/XCUITests/ToolbarTest.swift | 8 +- .../Tests/XCUITests/TopTabsTest.swift | 38 +++---- .../XCUITests/TrackingProtectionTests.swift | 29 +++-- .../Tests/XCUITests/URLValidationTests.swift | 4 +- .../Tests/XCUITests/UrlBarTests.swift | 12 +- .../Tests/XCUITests/ZoomingTests.swift | 25 ++-- 49 files changed, 479 insertions(+), 509 deletions(-) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ActivityStreamTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ActivityStreamTest.swift index a366550b6eae..6a8f6f2ff7d0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ActivityStreamTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ActivityStreamTest.swift @@ -94,7 +94,7 @@ class ActivityStreamTest: BaseTestCase { func testTopSitesRemoveAllExceptPinnedClearPrivateData() { waitForExistence(TopSiteCellgroup) if iPad() { - app.textFields.element(boundBy: 0).tap() + app.textFields.element(boundBy: 0).waitAndTap() app.typeText("mozilla.org\n") } else { navigator.openURL("mozilla.org") diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/AddressesTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/AddressesTests.swift index 74a240068ce3..0493f8bf8c8b 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/AddressesTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/AddressesTests.swift @@ -213,11 +213,11 @@ class AddressesTests: BaseTestCase { navigator.goto(AddressesSettings) let addresses = AccessibilityIdentifiers.Settings.Address.Addresses.self mozWaitForElementToExist(app.navigationBars[addresses.title]) - app.buttons[addresses.addAddress].tap() + app.buttons[addresses.addAddress].waitAndTap() mozWaitForElementToExist(app.navigationBars[addresses.addAddress]) if !app.staticTexts["Name"].exists { - app.buttons["Close"].tap() - app.buttons[addresses.addAddress].tap() + app.buttons["Close"].waitAndTap() + app.buttons[addresses.addAddress].waitAndTap() } mozWaitForElementToExist(app.staticTexts["Name"]) } @@ -246,7 +246,7 @@ class AddressesTests: BaseTestCase { } private func typeName(name: String, updateText: Bool = false) { - app.staticTexts["Name"].tap() + app.staticTexts["Name"].waitAndTap() if updateText { clearText() } @@ -254,7 +254,7 @@ class AddressesTests: BaseTestCase { } private func typeOrganization(organization: String, updateText: Bool = false) { - app.staticTexts["Organization"].tap() + app.staticTexts["Organization"].waitAndTap() if updateText { clearText() } @@ -262,7 +262,7 @@ class AddressesTests: BaseTestCase { } private func typeStreetAddress(street: String, updateText: Bool = false) { - app.staticTexts["Street Address"].tap() + app.staticTexts["Street Address"].waitAndTap() if updateText { clearText() } @@ -270,7 +270,7 @@ class AddressesTests: BaseTestCase { } private func typeCity(city: String, updateText: Bool = false) { - app.staticTexts["City"].tap() + app.staticTexts["City"].waitAndTap() if updateText { clearText() } @@ -278,18 +278,17 @@ class AddressesTests: BaseTestCase { } private func selectCountry(country: String) { - app.staticTexts["Country or Region"].tap() - mozWaitForElementToExist(app.buttons[country]) - app.buttons[country].tap() + app.staticTexts["Country or Region"].waitAndTap() + app.buttons[country].waitAndTap() } private func typeZIP(zip: String, updateText: Bool = false, isPostalCode: Bool = false) { if isPostalCode { scrollToElement(app.staticTexts["Postal Code"]) - app.staticTexts["Postal Code"].tap() + app.staticTexts["Postal Code"].waitAndTap() } else { scrollToElement(app.staticTexts["ZIP Code"]) - app.staticTexts["ZIP Code"].tap() + app.staticTexts["ZIP Code"].waitAndTap() } if updateText { clearText() @@ -299,7 +298,7 @@ class AddressesTests: BaseTestCase { private func typePhone(phone: String, updateText: Bool = false) { if app.buttons["Done"].isHittable { - app.buttons["Done"].tap() + app.buttons["Done"].waitAndTap() } app.staticTexts["Phone"].tapOnApp() if updateText { @@ -310,7 +309,7 @@ class AddressesTests: BaseTestCase { private func typeEmail(email: String, updateText: Bool = false) { scrollToElement(app.staticTexts["Email"]) - app.staticTexts["Email"].tap() + app.staticTexts["Email"].waitAndTap() if updateText { clearText() } @@ -321,7 +320,7 @@ class AddressesTests: BaseTestCase { if withRetry { app.buttons["Save"].tapWithRetry() } else { - app.buttons["Save"].tap() + app.buttons["Save"].waitAndTap() } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/AuthenticationTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/AuthenticationTest.swift index 614565c5b7e9..b6ecfc584741 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/AuthenticationTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/AuthenticationTest.swift @@ -23,7 +23,7 @@ class AuthenticationTest: BaseTestCase { if result != .completed { // User already logged, tap on reload button - app.buttons["TabLocationView.reloadButton"].tap() + app.buttons["TabLocationView.reloadButton"].waitAndTap() } mozWaitForElementToExist(app.staticTexts[ "A username and password are being requested by jigsaw.w3.org. The site says: test" @@ -41,7 +41,7 @@ class AuthenticationTest: BaseTestCase { app.alerts.textFields["Username"].typeText("guest") app.alerts.secureTextFields["Password"].tapAndTypeText("guest") mozWaitElementHittable(element: app.alerts.buttons["Log in"], timeout: TIMEOUT) - app.alerts.buttons["Log in"].tap() + app.alerts.buttons["Log in"].waitAndTap() /* There is no other way to verify basic auth is successful as the webview is inaccessible after sign in to verify the success text. */ waitForNoExistence(app.alerts.buttons["Cancel"], timeoutValue: 5) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift index d9eeb9458da6..811090fff3da 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift @@ -69,12 +69,9 @@ class BaseTestCase: XCTestCase { let icon = springboard.icons.containingText("Fennec").element(boundBy: 0) if icon.exists { icon.press(forDuration: 1.5) - mozWaitForElementToExist(springboard.buttons["Remove App"]) - springboard.buttons["Remove App"].tap() - mozWaitForElementToExist(springboard.alerts.buttons["Delete App"]) - springboard.alerts.buttons["Delete App"].tap() - mozWaitForElementToExist(springboard.alerts.buttons["Delete"]) - springboard.alerts.buttons["Delete"].tap() + springboard.buttons["Remove App"].waitAndTap() + springboard.alerts.buttons["Delete App"].waitAndTap() + springboard.alerts.buttons["Delete"].waitAndTap() } } @@ -138,7 +135,7 @@ class BaseTestCase: XCTestCase { if firstRunUI.exists { firstRunUI.swipeLeft() - XCUIApplication().buttons["Start Browsing"].tap() + XCUIApplication().buttons["Start Browsing"].waitAndTap() } } @@ -294,11 +291,9 @@ class BaseTestCase: XCTestCase { userState.url = path(forTestPage: "test-mozilla-book.html") navigator.openURL(path(forTestPage: "test-mozilla-book.html")) waitUntilPageLoad() - mozWaitForElementToExist(app.buttons["Reader View"]) - app.buttons["Reader View"].tap() + app.buttons["Reader View"].waitAndTap() waitUntilPageLoad() - mozWaitForElementToExist(app.buttons["Add to Reading List"]) - app.buttons["Add to Reading List"].tap() + app.buttons["Add to Reading List"].waitAndTap() } func removeContentFromReaderView() { @@ -310,12 +305,11 @@ class BaseTestCase: XCTestCase { // Remove the item from reading list savedToReadingList.swipeLeft() mozWaitForElementToExist(app.buttons["Remove"]) - app.buttons["Remove"].tap() + app.buttons["Remove"].waitAndTap() } func selectOptionFromContextMenu(option: String) { - mozWaitForElementToExist(app.tables["Context Menu"].cells.otherElements[option]) - app.tables["Context Menu"].cells.otherElements[option].tap() + app.tables["Context Menu"].cells.otherElements[option].waitAndTap() mozWaitForElementToNotExist(app.tables["Context Menu"]) } @@ -323,7 +317,7 @@ class BaseTestCase: XCTestCase { let app = XCUIApplication() UIPasteboard.general.string = url app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 2.0) - app.tables["Context Menu"].cells[AccessibilityIdentifiers.Photon.pasteAndGoAction].firstMatch.tap() + app.tables["Context Menu"].cells[AccessibilityIdentifiers.Photon.pasteAndGoAction].firstMatch.waitAndTap() if waitForLoadToFinish { let finishLoadingTimeout: TimeInterval = 30 @@ -358,7 +352,7 @@ class BaseTestCase: XCTestCase { func unlockLoginsView() { // Press continue button on the password onboarding if it's shown if app.buttons[AccessibilityIdentifiers.Settings.Passwords.onboardingContinue].exists { - app.buttons[AccessibilityIdentifiers.Settings.Passwords.onboardingContinue].tap() + app.buttons[AccessibilityIdentifiers.Settings.Passwords.onboardingContinue].waitAndTap() } let passcodeInput = springboard.otherElements.secureTextFields.firstMatch @@ -398,7 +392,7 @@ class BaseTestCase: XCTestCase { func dismissSurveyPrompt() { if app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].exists { - app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].tap() + app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].waitAndTap() } } @@ -420,11 +414,11 @@ class BaseTestCase: XCTestCase { } mozWaitForElementToExist(app.cells.staticTexts["Dark"]) if theme == "Dark" { - app.cells.staticTexts["Dark"].tap() + app.cells.staticTexts["Dark"].waitAndTap() } else { - app.cells.staticTexts["Light"].tap() + app.cells.staticTexts["Light"].waitAndTap() } - app.buttons["Settings"].tap() + app.buttons["Settings"].waitAndTap() navigator.nowAt(SettingsScreen) app.buttons["Done"].waitAndTap() } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift index ab67f0350416..e9265297254f 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift @@ -64,9 +64,9 @@ class BookmarksTests: BaseTestCase { app.collectionViews .cells["Example Domain"].children(matching: .other) .element.children(matching: .other) - .element.tap() + .element.waitAndTap() } else { - app.cells.staticTexts["Example Domain"].tap() + app.cells.staticTexts["Example Domain"].waitAndTap() } navigator.nowAt(BrowserTab) waitForTabsButton() @@ -175,7 +175,7 @@ class BookmarksTests: BaseTestCase { func testAddBookmark() { addNewBookmark() // Verify that clicking on bookmark opens the website - app.tables["Bookmarks List"].cells.element(boundBy: 1).tap() + app.tables["Bookmarks List"].cells.element(boundBy: 1).waitAndTap() mozWaitForElementToExist(app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField]) } @@ -187,8 +187,8 @@ class BookmarksTests: BaseTestCase { mozWaitForElementToExist(app.navigationBars["Bookmarks"]) // XCTAssertFalse(app.buttons["Save"].isEnabled), is this a bug allowing empty folder name? app.tables.cells.textFields.element(boundBy: 0).tapAndTypeText("Test Folder") - app.buttons["Save"].tap() - app.buttons["Done"].tap() + app.buttons["Save"].waitAndTap() + app.buttons["Done"].waitAndTap() checkItemsInBookmarksList(items: 2) navigator.nowAt(MobileBookmarks) // Now remove the folder @@ -208,7 +208,7 @@ class BookmarksTests: BaseTestCase { navigator.goto(LibraryPanel_Bookmarks) navigator.nowAt(MobileBookmarks) navigator.performAction(Action.AddNewSeparator) - app.buttons["Done"].tap() + app.buttons["Done"].waitAndTap() // There is one item plus the default Desktop Bookmarks folder checkItemsInBookmarksList(items: 2) @@ -237,7 +237,7 @@ class BookmarksTests: BaseTestCase { // Remove by long press and select option from context menu app.tables.staticTexts.element(boundBy: 1).press(forDuration: 1) mozWaitForElementToExist(app.tables["Context Menu"]) - app.tables.otherElements["Remove Bookmark"].tap() + app.tables.otherElements["Remove Bookmark"].waitAndTap() // Verify that there are only 1 cell (desktop bookmark folder) checkItemsInBookmarksList(items: 1) } @@ -307,7 +307,7 @@ class BookmarksTests: BaseTestCase { // Delete the Bookmark added, check it is removed app.tables["Bookmarks List"].cells.staticTexts["Example Domain"].swipeLeft() - app.buttons["Delete"].tap() + app.buttons["Delete"].waitAndTap() mozWaitForElementToNotExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"]) } @@ -323,7 +323,7 @@ class BookmarksTests: BaseTestCase { XCTAssertEqual(app.tables["Bookmarks List"].cells.count, 1) // There is only three folders inside the desktop bookmarks - app.tables["Bookmarks List"].cells.firstMatch.tap() + app.tables["Bookmarks List"].cells.firstMatch.waitAndTap() mozWaitForElementToExist(app.tables["Bookmarks List"]) XCTAssertEqual(app.tables["Bookmarks List"].cells.count, 3) } @@ -349,12 +349,12 @@ class BookmarksTests: BaseTestCase { navigator.openURL(path(forTestPage: url_2["url"]!)) waitForTabsButton() bookmarkPageAndTapEdit() - app.buttons["Close"].tap() + app.buttons["Close"].waitAndTap() waitForTabsButton() navigator.nowAt(BrowserTab) unbookmark() bookmarkPageAndTapEdit() - app.buttons["Save"].tap() + app.buttons["Save"].waitAndTap() navigator.nowAt(BrowserTab) navigator.goto(LibraryPanel_Bookmarks) checkItemInBookmarkList(oneItemBookmarked: true) @@ -399,7 +399,7 @@ class BookmarksTests: BaseTestCase { ] ) // Tap to "Open in New Tab" - contextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.plus].tap() + contextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.plus].waitAndTap() // The webpage opens in a new tab switchToTabAndValidate(nrOfTabs: "3") diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift index 9a7d16d414bf..8f72c7236402 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift @@ -42,14 +42,14 @@ class BrowsingPDFTests: BaseTestCase { waitUntilPageLoad() let checkboxValidation = app.webViews["Web content"].staticTexts["Verify you are human"] if checkboxValidation.exists { - checkboxValidation.tap() + checkboxValidation.waitAndTap() } mozWaitForValueContains(url, value: PDF_website["urlValue"]!) // Let's comment the next line until that fails intermittently due to the page re-direction // mozWaitForElementToExist(app.staticTexts["Education and schools"]) // Go back to pdf view - app.buttons[AccessibilityIdentifiers.Toolbar.backButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.backButton].waitAndTap() mozWaitForValueContains(url, value: PDF_website["pdfValue"]!) } @@ -93,7 +93,7 @@ class BrowsingPDFTests: BaseTestCase { } mozWaitForElementToExist(app.staticTexts[PDF_website["longUrlValue"]!]) - app.buttons["Add to Reading List"].tap() + app.buttons["Add to Reading List"].waitAndTap() navigator.nowAt(BrowserTab) // Go to reading list and check that the item is there @@ -124,7 +124,7 @@ class BrowsingPDFTests: BaseTestCase { .element .children(matching: .other) .element(boundBy: 0) - pdfTopSite.tap() + pdfTopSite.waitAndTap() waitUntilPageLoad() mozWaitForValueContains(url, value: PDF_website["pdfValue"]!) @@ -132,8 +132,7 @@ class BrowsingPDFTests: BaseTestCase { navigator.performAction(Action.OpenNewTabFromTabTray) mozWaitForElementToExist(app.collectionViews.cells.staticTexts[PDF_website["bookmarkLabel"]!]) pdfTopSite.press(forDuration: 1) - mozWaitForElementToExist(app.tables.cells.otherElements[StandardImageIdentifiers.Large.pinSlash]) - app.tables.cells.otherElements[StandardImageIdentifiers.Large.pinSlash].tap() + app.tables.cells.otherElements[StandardImageIdentifiers.Large.pinSlash].waitAndTap() waitForElementsToExist( [ app.links[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell], diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ClipBoardTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ClipBoardTests.swift index d68a4bb972d3..03880367ec9d 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ClipBoardTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ClipBoardTests.swift @@ -18,7 +18,7 @@ class ClipBoardTests: BaseTestCase { navigator.goto(URLBarOpen) urlBarAddress.waitAndTap() if iPad() { - app.menuItems["Select All"].tap() + app.menuItems["Select All"].waitAndTap() } app.menuItems["Copy"].waitAndTap() app.typeText("\r") @@ -32,7 +32,7 @@ class ClipBoardTests: BaseTestCase { let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let allowBtn = springboard.buttons["Allow Paste"] if allowBtn.waitForExistence(timeout: TIMEOUT) { - allowBtn.tap() + allowBtn.waitAndTap() } guard var value = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].value @@ -85,7 +85,7 @@ class ClipBoardTests: BaseTestCase { let urlBar = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] mozWaitForElementToExist(urlBar) urlBar.press(forDuration: 1.5) - app.otherElements[AccessibilityIdentifiers.Photon.pasteAndGoAction].tap() + app.otherElements[AccessibilityIdentifiers.Photon.pasteAndGoAction].waitAndTap() // The URL is pasted and the page is correctly loaded mozWaitForElementToExist(urlBar) waitForValueContains(urlBar, value: "localhost") @@ -114,7 +114,7 @@ class ClipBoardTests: BaseTestCase { // mozWaitForElementToExist( // app.tables["Context Menu"].otherElements[AccessibilityIdentifiers.Photon.pasteAndGoAction] // ) -// app.tables["Context Menu"].otherElements[AccessibilityIdentifiers.Photon.pasteAndGoAction].tap() +// app.tables["Context Menu"].otherElements[AccessibilityIdentifiers.Photon.pasteAndGoAction].waitAndTap() // mozWaitForElementToExist(app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField]) // mozWaitForValueContains(app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField], value: "www.example.com") } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/CreditCardsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/CreditCardsTests.swift index c905589a33f0..f2b9bc1997dd 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/CreditCardsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/CreditCardsTests.swift @@ -73,7 +73,7 @@ class CreditCardsTests: BaseTestCase { func testDeleteButtonFromEditCard() { addCardAndReachViewCardPage() // Tap on the "Remove card" button - app.buttons[creditCardsStaticTexts.ViewCreditCard.edit].tap() + app.buttons[creditCardsStaticTexts.ViewCreditCard.edit].waitAndTap() app.buttons[creditCardsStaticTexts.EditCreditCard.removeCard].waitAndTap() // Validate the pop up displayed let removeThisCardAlert = app.alerts[creditCardsStaticTexts.EditCreditCard.removeThisCard] @@ -91,12 +91,12 @@ class CreditCardsTests: BaseTestCase { ] ) // Tap on "CANCEL" - cancelButton.tap() + cancelButton.waitAndTap() // The prompt is dismissed, the "Edit card" page is displayed mozWaitForElementToNotExist(removeThisCardAlert) mozWaitForElementToExist(app.navigationBars[creditCardsStaticTexts.EditCreditCard.editCreditCard]) // Tap again on the "Remove card" button - app.buttons[creditCardsStaticTexts.EditCreditCard.removeCard].tap() + app.buttons[creditCardsStaticTexts.EditCreditCard.removeCard].waitAndTap() // The prompt is displayed again // Tap "Remove" on the prompt removeButton.waitAndTap() @@ -112,7 +112,7 @@ class CreditCardsTests: BaseTestCase { addCardAndReachViewCardPage() // Go back to saved cards section - app.navigationBars.buttons[creditCardsStaticTexts.ViewCreditCard.close].tap() + app.navigationBars.buttons[creditCardsStaticTexts.ViewCreditCard.close].waitAndTap() waitForElementsToExist( [ app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards], @@ -129,12 +129,12 @@ class CreditCardsTests: BaseTestCase { } addCreditCardAndReachAutofillWebsite() // Tap on the "Manage credit cards" option - app.buttons[manageCards].tap() + app.buttons[manageCards].waitAndTap() unlockLoginsView() // The user is redirected to the "Credit cards" section in Settings mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) // Tap the back button on the Credit cards page - app.buttons["Settings"].tap() + app.buttons["Settings"].waitAndTap() navigator.nowAt(SettingsScreen) app.buttons["Done"].waitAndTap() // The user is returned to the webpage @@ -153,10 +153,10 @@ class CreditCardsTests: BaseTestCase { let saveAndFillPaymentMethodsSwitch = app.switches[creditCardsStaticTexts.AutoFillCreditCard.saveAutofillCards].switches.firstMatch if saveAndFillPaymentMethodsSwitch.value! as? String == "1" { - saveAndFillPaymentMethodsSwitch.tap() + saveAndFillPaymentMethodsSwitch.waitAndTap() } XCTAssertEqual(saveAndFillPaymentMethodsSwitch.value! as? String, "0") - app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].tap() + app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].waitAndTap() let dateFiveYearsFromNow = Calendar.current.date(byAdding: .year, value: 5, to: Date()) let formatter = DateFormatter() formatter.dateFormat = "MMyy" @@ -177,7 +177,7 @@ class CreditCardsTests: BaseTestCase { let timeout: TimeInterval = 5.0 // Set the timeout duration if keyboard.waitForExistence(timeout: timeout) { - app.buttons["KeyboardAccessory.doneButton"].tap() + app.buttons["KeyboardAccessory.doneButton"].waitAndTap() } else { XCTFail("Keyboard did not appear within \(timeout) seconds") } @@ -186,15 +186,15 @@ class CreditCardsTests: BaseTestCase { unlockLoginsView() mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) // Enable the "Save and Fill Payment Methods" toggle - app.switches.element(boundBy: 1).tap() + app.switches.element(boundBy: 1).waitAndTap() XCTAssertEqual(saveAndFillPaymentMethodsSwitch.value! as? String, "1") - app.buttons["Settings"].tap() + app.buttons["Settings"].waitAndTap() navigator.nowAt(SettingsScreen) app.buttons["Done"].waitAndTap() cardNumber.waitAndTap() // The autofill option (Use saved card prompt) is displayed if !app.buttons[useSavedCard].waitForExistence(timeout: 3) { - app.webViews["Web content"].staticTexts["Card Number:"].tap() + app.webViews["Web content"].staticTexts["Card Number:"].waitAndTap() } mozWaitForElementToExist(app.buttons[useSavedCard]) } @@ -230,23 +230,23 @@ class CreditCardsTests: BaseTestCase { tapCardName() nameOnCard.clearText() typeCardName(name: updatedName) - app.buttons["Save"].tap() + app.buttons["Save"].waitAndTap() // The name of the card is saved without issues mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons[updatedName]) // Go to an saved credit card and change the credit card number - app.tables.cells.element(boundBy: 1).tap() - app.buttons[creditCardsStaticTexts.ViewCreditCard.edit].tap() + app.tables.cells.element(boundBy: 1).waitAndTap() + app.buttons[creditCardsStaticTexts.ViewCreditCard.edit].waitAndTap() tapCardNr() cardNr.clearText() typeCardNr(cardNo: cards[1]) - app.buttons["Save"].tap() + app.buttons["Save"].waitAndTap() // The credit card number is saved without issues mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons.elementContainingText("1111")) // Reach autofill website // reachAutofillWebsite() does not work on iOS 15 if #available(iOS 16, *) { reachAutofillWebsite() - app.scrollViews.otherElements.tables.cells.firstMatch.tap() + app.scrollViews.otherElements.tables.cells.firstMatch.waitAndTap() // The credit card's number and name are imported correctly on the designated fields validateAutofillCardInfo(cardNr: "4111 1111 1111 1111", expirationNr: "05 / 40", name: updatedName) } @@ -261,13 +261,13 @@ class CreditCardsTests: BaseTestCase { navigator.goto(CreditCardsSettings) unlockLoginsView() mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) - app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].tap() + app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].waitAndTap() addCreditCard(name: "Test", cardNumber: cards[0], expirationDate: "0540") mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) - app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].tap() + app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].waitAndTap() addCreditCard(name: "Test2", cardNumber: cards[1], expirationDate: "0640") mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) - app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].tap() + app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].waitAndTap() addCreditCard(name: "Test3", cardNumber: cards[2], expirationDate: "0740") // The cards are saved and displayed in Saved cards XCTAssertEqual(app.tables.cells.count - 1, expectedCards) @@ -289,7 +289,7 @@ class CreditCardsTests: BaseTestCase { // Reach used saved cards autofill website reachAutofillWebsite() // Any saved card can be selected/used from the autofill menu - app.scrollViews.otherElements.tables.cells.firstMatch.tap() + app.scrollViews.otherElements.tables.cells.firstMatch.waitAndTap() validateAutofillCardInfo(cardNr: "2720 9943 2658 1252", expirationNr: "05 / 40", name: "Test") dismissSavedCardsPrompt() swipeUp(nrOfSwipes: 2) @@ -303,7 +303,7 @@ class CreditCardsTests: BaseTestCase { app.buttons[useSavedCard].waitAndTap() unlockLoginsView() mozWaitForElementToExist(app.staticTexts["Use saved card"]) - app.scrollViews.otherElements.tables.cells["creditCardCell_1"].tap() + app.scrollViews.otherElements.tables.cells["creditCardCell_1"].waitAndTap() validateAutofillCardInfo(cardNr: "4111 1111 1111 1111", expirationNr: "06 / 40", name: "Test2") if #available(iOS 17, *) { app.webViews["Web content"].textFields["Email"].tapOnApp() @@ -316,7 +316,7 @@ class CreditCardsTests: BaseTestCase { mozWaitForElementToExist(app.staticTexts["Use saved card"]) app.scrollViews.element.swipeUp() mozWaitForElementToExist(app.scrollViews.otherElements.tables.cells["creditCardCell_2"]) - app.scrollViews.otherElements.tables.cells["creditCardCell_2"].tap() + app.scrollViews.otherElements.tables.cells["creditCardCell_2"].waitAndTap() validateAutofillCardInfo(cardNr: "5346 7556 0029 9631", expirationNr: "07 / 40", name: "Test3") } } @@ -326,7 +326,7 @@ class CreditCardsTests: BaseTestCase { func testErrorStatesCreditCards() { // Go to a saved credit card and delete the name addCardAndReachViewCardPage() - app.buttons[creditCardsStaticTexts.ViewCreditCard.edit].tap() + app.buttons[creditCardsStaticTexts.ViewCreditCard.edit].waitAndTap() let saveButton = app.buttons[creditCardsStaticTexts.AddCreditCard.save] tapCardName() nameOnCard.clearText() @@ -376,7 +376,7 @@ class CreditCardsTests: BaseTestCase { mozWaitForElementToNotExist(app.otherElements.staticTexts["Enter a valid expiration date"]) mozWaitForElementToExist(saveButton) XCTAssertTrue(saveButton.isEnabled) - saveButton.tap() + saveButton.waitAndTap() // The credit card is saved let cardsInfo = ["Test", "5/40"] mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons.elementContainingText("1252")) @@ -411,7 +411,7 @@ class CreditCardsTests: BaseTestCase { ] ) // Tapping 'x' will dismiss the prompt - app.buttons[AccessibilityIdentifiers.SaveCardPrompt.Prompt.closeButton].tap() + app.buttons[AccessibilityIdentifiers.SaveCardPrompt.Prompt.closeButton].waitAndTap() mozWaitForElementToNotExist(app.staticTexts["Securely save this card?"]) // Go to the Settings --> Payment methods swipeDown(nrOfSwipes: 2) @@ -436,7 +436,7 @@ class CreditCardsTests: BaseTestCase { // Fill in the form with the card details of a new (unsaved) credit card. fillCardDetailsOnWebsite(cardNr: cards[1], expirationDate: "0540", nameOnCard: "Test") payButton.tapOnApp() - app.buttons["Save"].tap() + app.buttons["Save"].waitAndTap() // The prompt is dismissed. And a toast message is displayed: "New card saved" mozWaitForElementToExist(app.staticTexts["New Card Saved"]) mozWaitForElementToNotExist(app.staticTexts["Securely save this card?"]) @@ -530,7 +530,7 @@ class CreditCardsTests: BaseTestCase { // The prompt contains two buttons: "Save" and "x". mozWaitForElementToExist(app.staticTexts["Update card?"]) // Tapping 'x' will dismiss the prompt - app.buttons["Save"].tap() + app.buttons["Save"].waitAndTap() mozWaitForElementToNotExist(app.staticTexts["Update card?"]) // Go to the Settings --> Payment methods swipeDown(nrOfSwipes: 2) @@ -607,7 +607,7 @@ class CreditCardsTests: BaseTestCase { } sleep(1) if app.keyboards.firstMatch.exists { - app.buttons["KeyboardAccessory.doneButton"].tap() + app.buttons["KeyboardAccessory.doneButton"].waitAndTap() } cvc.tapOnApp() cvc.typeText("123") @@ -621,7 +621,7 @@ class CreditCardsTests: BaseTestCase { nrOfRetries -= 1 } zip.typeText("12345") - app.buttons["KeyboardAccessory.doneButton"].tap() + app.buttons["KeyboardAccessory.doneButton"].waitAndTap() if app.webViews["Web content"].switches.element(boundBy: 0).value as? String == "1" { app.webViews["Web content"].switches.element(boundBy: 0).tapOnApp() @@ -647,7 +647,7 @@ class CreditCardsTests: BaseTestCase { private func dismissSavedCardsPrompt() { if app.buttons.elementContainingText("Decline").isVisible() && app.buttons.elementContainingText("Decline").isHittable { - app.staticTexts["TEST CARDS"].tap() + app.staticTexts["TEST CARDS"].waitAndTap() } } @@ -669,13 +669,13 @@ class CreditCardsTests: BaseTestCase { func tapCardNr() { initCardFields() - cardNr.tap() + cardNr.waitAndTap() mozWaitForElementToExist(cardNr) } func tapExpiration() { initCardFields() - expiration.tap() + expiration.waitAndTap() mozWaitForElementToExist(expiration) } @@ -732,9 +732,9 @@ class CreditCardsTests: BaseTestCase { mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) let saveAndFillPaymentMethodsSwitch = app.switches[creditCardsStaticTexts.AutoFillCreditCard.saveAutofillCards] if saveAndFillPaymentMethodsSwitch.value! as? String == "0" { - saveAndFillPaymentMethodsSwitch.tap() + saveAndFillPaymentMethodsSwitch.waitAndTap() } - app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].tap() + app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].waitAndTap() addCreditCard(name: "Test", cardNumber: cards[0], expirationDate: "0540") navigator.goto(NewTabScreen) navigator.openURL("https://checkout.stripe.dev/preview") @@ -764,11 +764,11 @@ class CreditCardsTests: BaseTestCase { navigator.goto(CreditCardsSettings) unlockLoginsView() mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) - app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].tap() + app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard].waitAndTap() addCreditCard(name: "Test", cardNumber: cards[0], expirationDate: "0540") // Tap on a saved card mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) - app.tables.cells.element(boundBy: 1).tap() + app.tables.cells.element(boundBy: 1).waitAndTap() // The "View card" page is displayed with all the details of the card waitForElementsToExist( [ diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DataManagementTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DataManagementTests.swift index b41160baad9a..782707b9c872 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DataManagementTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DataManagementTests.swift @@ -16,7 +16,7 @@ class DataManagementTests: BaseTestCase { XCTAssertEqual(app.cells.buttons.images.count, 0, "The Website data has not cleared correctly") // Navigate back to the browser mozWaitElementHittable(element: app.buttons["Data Management"], timeout: TIMEOUT) - app.buttons["Data Management"].tap() + app.buttons["Data Management"].waitAndTap() app.buttons["Settings"].waitAndTap() app.buttons["Done"].waitAndTap() } @@ -36,10 +36,10 @@ class DataManagementTests: BaseTestCase { var beforeDelete = 0 if #available(iOS 17, *) { beforeDelete = app.cells.images.count - app.cells.images["circle"].firstMatch.tap() + app.cells.images["circle"].firstMatch.waitAndTap() } else { beforeDelete = app.cells.staticTexts.count - app.cells.staticTexts.firstMatch.tap() + app.cells.staticTexts.firstMatch.waitAndTap() } app.otherElements.staticTexts["Clear Items: 1"].waitAndTap() @@ -70,7 +70,7 @@ class DataManagementTests: BaseTestCase { mozWaitForElementToExist(app.tables.buttons.firstMatch) } if app.cells["ShowMoreWebsiteData"].exists { - app.cells["ShowMoreWebsiteData"].tap() + app.cells["ShowMoreWebsiteData"].waitAndTap() } mozWaitForElementToExist(app.staticTexts["example.com"]) if #available(iOS 17, *) { @@ -96,7 +96,7 @@ class DataManagementTests: BaseTestCase { mozWaitForElementToExist(app.tables["Search results"]) let expectedSearchResults = app.tables["Search results"].cells.count XCTAssertEqual(expectedSearchResults-1, 1) - app.buttons["Cancel"].tap() + app.buttons["Cancel"].waitAndTap() mozWaitForElementToExist(app.tables.otherElements["Website Data"]) if #available(iOS 17, *) { XCTAssertGreaterThan(app.cells.images.count, 1) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DesktopModeTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DesktopModeTests.swift index ec9210dc4376..e09b0c62b035 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DesktopModeTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DesktopModeTests.swift @@ -169,7 +169,7 @@ class DesktopModeTestsIphone: IphoneOnlyTestCase { navigator.openURL(path(forTestPage: "test-user-agent.html")) waitUntilPageLoad() // Workaround - app.buttons[AccessibilityIdentifiers.Toolbar.reloadButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.reloadButton].waitAndTap() navigator.goto(BrowserTabMenu) navigator.goto(RequestDesktopSite) waitUntilPageLoad() diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DomainAutocompleteTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DomainAutocompleteTests.swift index 6e101375e7f0..5fd9108221d4 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DomainAutocompleteTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DomainAutocompleteTests.swift @@ -64,7 +64,7 @@ class DomainAutocompleteTests: BaseTestCase { XCTAssertEqual(value as? String, website["value"]!, "Wrong autocompletion") // Enter the complete website and check that there is not more text added, just what user typed - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText(website["value"]!) mozWaitForValueContains(urlBarAddress, value: website["value"]!) let value2 = urlBarAddress.value @@ -141,36 +141,36 @@ class DomainAutocompleteTests: BaseTestCase { XCTAssertEqual(value as? String, "baz", "Wrong autocompletion") // Ensure we don't match against TLDs. - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText(".com") let value2 = urlBarAddress.value // Check there is not more text added, just what user typed XCTAssertEqual(value2 as? String, ".com", "Wrong autocompletion") // Ensure we don't match other characters ie: ., :, / - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText(".") let value3 = urlBarAddress.value XCTAssertEqual(value3 as? String, ".", "Wrong autocompletion") - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText(":") let value4 = urlBarAddress.value XCTAssertEqual(value4 as? String, ":", "Wrong autocompletion") - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText("/") let value5 = urlBarAddress.value XCTAssertEqual(value5 as? String, "/", "Wrong autocompletion") // Ensure we don't match strings that don't start a word. - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText("tter") let value6 = urlBarAddress.value XCTAssertEqual(value6 as? String, "tter", "Wrong autocompletion") // Ensure we don't match words outside of the domain - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText("login") let value7 = urlBarAddress.value XCTAssertEqual(value7 as? String, "login", "Wrong autocompletion") @@ -185,13 +185,13 @@ class DomainAutocompleteTests: BaseTestCase { let value = urlBarAddress.value XCTAssertEqual(value as? String, "amazon.com", "Wrong autocompletion") - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText("an") mozWaitForValueContains(urlBarAddress, value: ".com") let value2 = urlBarAddress.value XCTAssertEqual(value2 as? String, "answers.com", "Wrong autocompletion") - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText("anc") mozWaitForValueContains(urlBarAddress, value: ".com") let value3 = urlBarAddress.value @@ -208,14 +208,14 @@ class DomainAutocompleteTests: BaseTestCase { XCTAssertEqual(value as? String, "MoZilla.org", "Wrong autocompletion") // Test that leading spaces still show suggestions. - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText(" moz") mozWaitForValueContains(urlBarAddress, value: ".org") let value2 = urlBarAddress.value XCTAssertEqual(value2 as? String, " mozilla.org", "Wrong autocompletion") // Test that trailing spaces do *not* show suggestions. - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() urlBarAddress.typeText(" moz ") mozWaitForValueContains(urlBarAddress, value: "moz") let value3 = urlBarAddress.value diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DownloadsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DownloadsTests.swift index d87bf624d858..10184e43d56c 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DownloadsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DownloadsTests.swift @@ -56,7 +56,7 @@ class DownloadsTests: BaseTestCase { app.webViews.links.firstMatch.swipeLeft(velocity: 1000) app.webViews.links.firstMatch.swipeLeft(velocity: 1000) } - app.webViews.links[testFileName].firstMatch.tap() + app.webViews.links[testFileName].firstMatch.waitAndTap() waitForElementsToExist( [ @@ -65,7 +65,7 @@ class DownloadsTests: BaseTestCase { app.tables["Context Menu"].otherElements[StandardImageIdentifiers.Large.download] ] ) - app.buttons["Cancel"].tap() + app.buttons["Cancel"].waitAndTap() navigator.goto(BrowserTabMenu) navigator.goto(LibraryPanel_Downloads) checkTheNumberOfDownloadedItems(items: 0) @@ -149,7 +149,7 @@ class DownloadsTests: BaseTestCase { mozWaitForElementToExist(app.collectionViews.buttons["Copy"]) } if !iPad() { - app.navigationBars["UIActivityContentView"].buttons["Close"].tap() + app.navigationBars["UIActivityContentView"].buttons["Close"].waitAndTap() } else { // Workaround to close the context menu. // XCUITest does not allow me to click the greyed out portion of the app without the force option. @@ -186,7 +186,7 @@ class DownloadsTests: BaseTestCase { mozWaitForElementToExist(app.collectionViews.buttons["Copy"]) } if !iPad() { - app.navigationBars["UIActivityContentView"].buttons["Close"].tap() + app.navigationBars["UIActivityContentView"].buttons["Close"].waitAndTap() } else { // Workaround to close the context menu. // XCUITest does not allow me to click the greyed out portion of the app without the force option. @@ -199,14 +199,12 @@ class DownloadsTests: BaseTestCase { waitUntilPageLoad() app.webViews.firstMatch.swipeLeft() for _ in 0.. minBoundary) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/InactiveTabsTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/InactiveTabsTest.swift index dcefe1cf6269..68fdf1ff4d24 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/InactiveTabsTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/InactiveTabsTest.swift @@ -48,7 +48,7 @@ final class InactiveTabsTest: BaseTestCase { ) // Tap on the ">" button. - app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerView].tap() + app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerView].waitAndTap() waitForElementsToExist( [ app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerButton], @@ -58,20 +58,19 @@ final class InactiveTabsTest: BaseTestCase { app.otherElements["Tabs Tray"].staticTexts["Amazon.com. Spend less. Smile more."] ] ) - app.buttons[AccessibilityIdentifiers.TabTray.doneButton].tap() + app.buttons[AccessibilityIdentifiers.TabTray.doneButton].waitAndTap() // Go to Settings -> Tabs and disable "Inactive tabs" navigator.nowAt(NewTabScreen) navigator.goto(SettingsScreen) - mozWaitForElementToExist(app.cells[AccessibilityIdentifiers.Settings.Tabs.title]) - app.cells[AccessibilityIdentifiers.Settings.Tabs.title].tap() + app.cells[AccessibilityIdentifiers.Settings.Tabs.title].waitAndTap() mozWaitForElementToExist(app.tables.otherElements[AccessibilityIdentifiers.Settings.Tabs.Customize.title]) XCTAssertEqual( app.switches[AccessibilityIdentifiers.Settings.Tabs.Customize.inactiveTabsSwitch].value as? String, "1") - app.switches[AccessibilityIdentifiers.Settings.Tabs.Customize.inactiveTabsSwitch].tap() + app.switches[AccessibilityIdentifiers.Settings.Tabs.Customize.inactiveTabsSwitch].waitAndTap() XCTAssertEqual( app.switches[AccessibilityIdentifiers.Settings.Tabs.Customize.inactiveTabsSwitch].value as? String, "0") - app.navigationBars.buttons["Settings"].tap() // Note: No AccessibilityIdentifiers + app.navigationBars.buttons["Settings"].waitAndTap() // Note: No AccessibilityIdentifiers navigator.nowAt(SettingsScreen) // Return to tabs tray @@ -88,33 +87,30 @@ final class InactiveTabsTest: BaseTestCase { ] ) mozWaitForElementToNotExist(app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerButton]) - app.buttons[AccessibilityIdentifiers.TabTray.doneButton].tap() + app.buttons[AccessibilityIdentifiers.TabTray.doneButton].waitAndTap() navigator.nowAt(NewTabScreen) // Go to Settings -> Tabs and enable "Inactive tabs" navigator.goto(SettingsScreen) - mozWaitForElementToExist(app.cells[AccessibilityIdentifiers.Settings.Tabs.title]) - app.cells[AccessibilityIdentifiers.Settings.Tabs.title].tap() - mozWaitForElementToExist(app.tables.otherElements[AccessibilityIdentifiers.Settings.Tabs.Customize.title]) - app.switches[AccessibilityIdentifiers.Settings.Tabs.Customize.inactiveTabsSwitch].tap() + app.cells[AccessibilityIdentifiers.Settings.Tabs.title].waitAndTap() + app.switches[AccessibilityIdentifiers.Settings.Tabs.Customize.inactiveTabsSwitch].waitAndTap() XCTAssertEqual( app.switches[AccessibilityIdentifiers.Settings.Tabs.Customize.inactiveTabsSwitch].value as? String, "1") - app.navigationBars.buttons["Settings"].tap() + app.navigationBars.buttons["Settings"].waitAndTap() navigator.nowAt(SettingsScreen) // Return to tabs tray navigator.goto(TabTray) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerView]) // Tap on a tab from the inactive list - app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerView].tap() + app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerView].waitAndTap() waitForElementsToExist( [ app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerButton], app.otherElements["Tabs Tray"].staticTexts["Homepage"] ] ) - app.otherElements["Tabs Tray"].staticTexts["Homepage"].tap() + app.otherElements["Tabs Tray"].staticTexts["Homepage"].waitAndTap() mozWaitForElementToNotExist(app.otherElements["Tabs Tray"]) navigator.nowAt(NewTabScreen) @@ -127,7 +123,7 @@ final class InactiveTabsTest: BaseTestCase { ) // Expand inactive list - app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerView].tap() + app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerView].waitAndTap() waitForElementsToExist( [ app.buttons[AccessibilityIdentifiers.TabTray.InactiveTabs.headerButton], @@ -142,8 +138,7 @@ final class InactiveTabsTest: BaseTestCase { // Swipe on a tab from the list to delete app.otherElements["Tabs Tray"].staticTexts["Google"].swipeLeft() - mozWaitForElementToExist(app.otherElements["Tabs Tray"].buttons["Close"]) - app.otherElements["Tabs Tray"].buttons["Close"].tap() // Note: No AccessibilityIdentifier + app.otherElements["Tabs Tray"].buttons["Close"].waitAndTap() // Note: No AccessibilityIdentifier mozWaitForElementToNotExist(app.otherElements["Tabs Tray"].staticTexts["Google"]) waitForElementsToExist( [ @@ -155,7 +150,7 @@ final class InactiveTabsTest: BaseTestCase { // Tap "Close All Inactive Tabs" app.swipeUp() mozWaitForElementToExist(app.buttons["InactiveTabs.deleteButton"]) - app.buttons["InactiveTabs.deleteButton"].tap() + app.buttons["InactiveTabs.deleteButton"].waitAndTap() mozWaitForElementToExist(app.staticTexts["Tabs Closed: 17"]) // All inactive tabs are deleted diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift index 6ddd403c8052..b025b0d7fab7 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift @@ -45,7 +45,7 @@ class IntegrationTests: BaseTestCase { func allowNotifications () { addUIInterruptionMonitor(withDescription: "notifications") { (alert) -> Bool in - alert.buttons["Allow"].tap() + alert.buttons["Allow"].waitAndTap() return true } app.swipeDown() @@ -80,10 +80,10 @@ class IntegrationTests: BaseTestCase { mozWaitForElementToNotExist(app.staticTexts["Sync and Save Data"]) sleep(5) if app.tables.staticTexts["Sync is offline"].exists { - app.tables.staticTexts["Sync is offline"].tap() + app.tables.staticTexts["Sync is offline"].waitAndTap() } if app.tables.staticTexts["Sync Now"].exists { - app.tables.staticTexts["Sync Now"].tap() + app.tables.staticTexts["Sync Now"].waitAndTap() } mozWaitForElementToNotExist(app.tables.staticTexts["Syncing…"]) mozWaitForElementToExist(app.tables.staticTexts["Sync Now"], timeout: TIMEOUT_LONG) @@ -154,9 +154,9 @@ class IntegrationTests: BaseTestCase { ) // Sync again just to make sure to sync after new name is shown - app.buttons["Settings"].tap() + app.buttons["Settings"].waitAndTap() mozWaitForElementToExist(app.staticTexts["ACCOUNT"]) - app.tables.cells.element(boundBy: 2).tap() + app.tables.cells.element(boundBy: 2).waitAndTap() mozWaitForElementToExist(app.tables.staticTexts["Sync Now"], timeout: TIMEOUT_LONG) } @@ -166,10 +166,10 @@ class IntegrationTests: BaseTestCase { // Log in in order to save it app.webViews.textFields["Email or phone"].tapAndTypeText(userName) - app.webViews.buttons["Next"].tap() + app.webViews.buttons["Next"].waitAndTap() app.webViews.secureTextFields["Password"].tapAndTypeText(userPassword) - app.webViews.buttons["Sign in"].tap() + app.webViews.buttons["Sign in"].waitAndTap() // Save the login app.buttons["SaveLoginPrompt.saveLoginButton"].waitAndTap() @@ -203,7 +203,7 @@ class IntegrationTests: BaseTestCase { navigator.nowAt(SettingsScreen) navigator.goto(LoginsSettings) mozWaitForElementToExist(app.buttons.firstMatch) - app.scrollViews.buttons["Continue"].tap() + app.scrollViews.buttons["Continue"].waitAndTap() let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let passcodeInput = springboard.secureTextFields["Passcode field"] @@ -222,7 +222,7 @@ class IntegrationTests: BaseTestCase { waitForInitialSyncComplete() // Check synced Tabs - app.buttons["Done"].tap() + app.buttons["Done"].waitAndTap() navigator.nowAt(HomePanelsScreen) navigator.goto(TabTray) navigator.performAction(Action.ToggleSyncMode) @@ -258,7 +258,7 @@ class IntegrationTests: BaseTestCase { navigator.nowAt(SettingsScreen) navigator.goto(LoginsSettings) mozWaitForElementToExist(app.buttons.firstMatch) - app.scrollViews.buttons["Continue"].tap() + app.scrollViews.buttons["Continue"].waitAndTap() let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let passcodeInput = springboard.secureTextFields["Passcode field"] @@ -270,18 +270,18 @@ class IntegrationTests: BaseTestCase { // Disconnect account navigator.goto(SettingsScreen) - app.tables.cells.element(boundBy: 1).tap() + app.tables.cells.element(boundBy: 1).waitAndTap() mozWaitForElementToExist(app.cells["DeviceNameSetting"].textFields["DeviceNameSettingTextField"]) - app.cells["SignOut"].tap() + app.cells["SignOut"].waitAndTap() app.buttons["Disconnect"].waitAndTap() sleep(3) // Connect same account again navigator.nowAt(SettingsScreen) - app.tables.cells["SignInToSync"].tap() - app.buttons["EmailSignIn.button"].tap() + app.tables.cells["SignInToSync"].waitAndTap() + app.buttons["EmailSignIn.button"].waitAndTap() navigator.nowAt(FxASigninScreen) mozWaitForElementToExist(app.staticTexts["Enter your password"], timeout: TIMEOUT_LONG) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/JumpBackInTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/JumpBackInTests.swift index 899e607a6428..69cded9545e3 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/JumpBackInTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/JumpBackInTests.swift @@ -104,7 +104,7 @@ class JumpBackInTests: BaseTestCase { mozWaitForElementToNotExist(app.cells["JumpBackInCell"].staticTexts["YouTube"]) // Tap on Twitter from "Jump Back In" - app.cells["JumpBackInCell"].staticTexts["Wikipedia"].firstMatch.tap() + app.cells["JumpBackInCell"].staticTexts["Wikipedia"].firstMatch.waitAndTap() // The view is switched to the twitter tab if let url = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].value as? String { @@ -134,7 +134,7 @@ class JumpBackInTests: BaseTestCase { } else { mozWaitForElementToExist(app.navigationBars.staticTexts["Open Tabs"]) } - app.cells["Example Domain"].buttons[StandardImageIdentifiers.Large.cross].tap() + app.cells["Example Domain"].buttons[StandardImageIdentifiers.Large.cross].waitAndTap() // Revisit the "Jump Back In" section mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.TabTray.newTabButton], timeout: TIMEOUT) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/LibraryTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/LibraryTests.swift index d5a3a2a1c9f0..984dc66f188f 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/LibraryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/LibraryTests.swift @@ -14,7 +14,7 @@ class LibraryTestsIpad: IpadOnlyTestCase { // The Bookmark panel is displayed mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.bookmarksButton]) let libraryShorcutButton = app.buttons[AccessibilityIdentifiers.Toolbar.bookmarksButton] - libraryShorcutButton.tap() + libraryShorcutButton.waitAndTap() navigator.nowAt(HomePanel_Library) mozWaitForElementToExist(app.tables[AccessibilityIdentifiers.LibraryPanels.BookmarksPanel.tableView]) // Go to a different panel diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift index aab36595f6fb..a6ff515cc695 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift @@ -29,8 +29,7 @@ class LoginTest: BaseTestCase { navigator.openURL(givenUrl) waitUntilPageLoad() app.buttons["submit"].waitAndTap() - mozWaitForElementToExist(app.buttons["SaveLoginPrompt.saveLoginButton"]) - app.buttons["SaveLoginPrompt.saveLoginButton"].tap() + app.buttons["SaveLoginPrompt.saveLoginButton"].waitAndTap() } private func openLoginsSettings() { @@ -95,10 +94,10 @@ class LoginTest: BaseTestCase { openLoginsSettingsFromBrowserTab() XCTAssertEqual(app.tables["Login List"].cells.count, defaultNumRowsLoginsList) // Save a login and check that it appears on the list from BrowserTabMenu - app.buttons["Settings"].tap() + app.buttons["Settings"].waitAndTap() navigator.nowAt(SettingsScreen) waitForExistence(app.buttons["Done"]) - app.buttons["Done"].tap() + app.buttons["Done"].waitAndTap() navigator.nowAt(HomePanelsScreen) saveLogin(givenUrl: testLoginPage) @@ -112,10 +111,10 @@ class LoginTest: BaseTestCase { // I can't reproduce the issue manually. The issue occurs only during test automation. if #available(iOS 16, *) { // Check to see how it works with multiple entries in the list- in this case, two for now - app.buttons["Settings"].tap() + app.buttons["Settings"].waitAndTap() navigator.nowAt(SettingsScreen) waitForExistence(app.buttons["Done"]) - app.buttons["Done"].tap() + app.buttons["Done"].waitAndTap() navigator.nowAt(HomePanelsScreen) saveLogin(givenUrl: testSecondLoginPage) @@ -139,8 +138,8 @@ class LoginTest: BaseTestCase { func testDoNotSaveLogin() { navigator.openURL(testLoginPage) waitUntilPageLoad() - app.buttons["submit"].tap() - app.buttons["SaveLoginPrompt.dontSaveButton"].tap() + app.buttons["submit"].waitAndTap() + app.buttons["SaveLoginPrompt.dontSaveButton"].waitAndTap() // There should not be any login saved openLoginsSettings() mozWaitForElementToNotExist(app.staticTexts[domain]) @@ -156,7 +155,7 @@ class LoginTest: BaseTestCase { mozWaitForElementToExist(app.staticTexts[domain]) mozWaitForElementToExist(app.staticTexts[domainLogin]) XCTAssertTrue(app.buttons["Edit"].isHittable) - app.buttons["Edit"].tap() + app.buttons["Edit"].waitAndTap() mozWaitForElementToExist(app.buttons["Select All"]) mozWaitForElementToExist(app.staticTexts[domainLogin]) @@ -172,9 +171,9 @@ class LoginTest: BaseTestCase { openLoginsSettings() mozWaitForElementToExist(app.staticTexts[domainLogin]) app.staticTexts[domain].waitAndTap() - app.cells.staticTexts["Delete"].tap() + app.cells.staticTexts["Delete"].waitAndTap() mozWaitForElementToExist(app.alerts["Remove Password?"]) - app.alerts.buttons["Remove"].tap() + app.alerts.buttons["Remove"].waitAndTap() mozWaitForElementToExist(app.tables["Login List"]) mozWaitForElementToNotExist(app.staticTexts[domain]) mozWaitForElementToNotExist(app.staticTexts[domainLogin]) @@ -189,7 +188,7 @@ class LoginTest: BaseTestCase { openLoginsSettingsFromBrowserTab() XCTAssertTrue(app.staticTexts[domain].exists) XCTAssertTrue(app.staticTexts[domainLogin].exists) - app.staticTexts[domain].tap() + app.staticTexts[domain].waitAndTap() // The login details are available waitForExistence(app.tables["Login Detail List"]) mozWaitForElementToExist(app.tables.cells[loginsListURLLabel]) @@ -197,11 +196,10 @@ class LoginTest: BaseTestCase { mozWaitForElementToExist(app.tables.cells[loginsListPasswordLabel]) mozWaitForElementToExist(app.tables.cells.staticTexts["Delete"]) // Change the username - app.buttons["Edit"].tap() + app.buttons["Edit"].waitAndTap() mozWaitForElementToExist(app.tables["Login Detail List"]) - app.tables["Login Detail List"].cells.elementContainingText("Username").tap() - mozWaitForElementToExist(app.menuItems["Select All"]) - app.menuItems["Select All"].tap() + app.tables["Login Detail List"].cells.elementContainingText("Username").waitAndTap() + app.menuItems["Select All"].waitAndTap() app.menuItems["Cut"].waitAndTap() enterTextInField(typedText: "foo") app.buttons["Done"].waitAndTap() @@ -230,7 +228,7 @@ class LoginTest: BaseTestCase { // mozWaitForElementToExist(app.tables["No logins found"]) // Clear Text - app.buttons["Clear text"].tap() + app.buttons["Clear text"].waitAndTap() XCTAssertEqual(app.tables["Login List"].cells.count, defaultNumRowsLoginsList + 1) } @@ -248,7 +246,7 @@ class LoginTest: BaseTestCase { app.webViews.secureTextFields.element(boundBy: 0).tapAndTypeText("test15mz") // Submit form and choose to save the logins - app.buttons["submit"].tap() + app.buttons["submit"].waitAndTap() app.buttons["SaveLoginPrompt.saveLoginButton"].waitAndTap() // Clear Data and go to test page, fields should be filled in @@ -303,7 +301,7 @@ class LoginTest: BaseTestCase { } private func createLoginManually() { - app.buttons["Add"].tap() + app.buttons["Add"].waitAndTap() waitForElementsToExist( [ app.tables["Add Credential"], @@ -313,16 +311,16 @@ class LoginTest: BaseTestCase { ] ) - app.tables["Add Credential"].cells["Website, "].tap() + app.tables["Add Credential"].cells["Website, "].waitAndTap() enterTextInField(typedText: "testweb") - app.tables["Add Credential"].cells["Username, "].tap() + app.tables["Add Credential"].cells["Username, "].waitAndTap() enterTextInField(typedText: "foo") - app.tables["Add Credential"].cells["Password"].tap() + app.tables["Add Credential"].cells["Password"].waitAndTap() enterTextInField(typedText: "bar") - app.buttons["Save"].tap() + app.buttons["Save"].waitAndTap() mozWaitForElementToExist(app.tables["Login List"].otherElements["SAVED PASSWORDS"]) } @@ -331,7 +329,7 @@ class LoginTest: BaseTestCase { if #unavailable(iOS 16) { mozWaitForElementToExist(app.keyboards.firstMatch) if app.keyboards.buttons["Continue"].exists { - app.keyboards.buttons["Continue"].tap() + app.keyboards.buttons["Continue"].waitAndTap() mozWaitForElementToNotExist(app.keyboards.buttons["Continue"]) } // The keyboard may need extra time to respond. @@ -339,7 +337,7 @@ class LoginTest: BaseTestCase { } for letter in typedText { print("\(letter)") - app.keyboards.keys["\(letter)"].tap() + app.keyboards.keys["\(letter)"].waitAndTap() } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/MicrosurveyTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/MicrosurveyTests.swift index 69d8aeb34319..f7d9353b9d22 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/MicrosurveyTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/MicrosurveyTests.swift @@ -28,7 +28,7 @@ final class MicrosurveyTests: BaseTestCase { XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton].exists) XCTAssertTrue(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.firefoxLogo].exists) XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].exists) - app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].tap() + app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].waitAndTap() XCTAssertFalse(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].exists) } @@ -52,7 +52,7 @@ final class MicrosurveyTests: BaseTestCase { func testShowMicrosurvey() { generateTriggerForMicrosurvey() let continueButton = app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton] - continueButton.tap() + continueButton.waitAndTap() waitForElementsToExist( [ @@ -62,7 +62,7 @@ final class MicrosurveyTests: BaseTestCase { ) let tablesQuery = app.scrollViews.otherElements.tables let firstOption = tablesQuery.cells.firstMatch - firstOption.tap() + firstOption.waitAndTap() mozWaitForElementToExist(firstOption) XCTAssertEqual(firstOption.label, "Very satisfied") mozWaitForValueContains(firstOption, value: "1 out of 6") diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/MultiWindowTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/MultiWindowTests.swift index 45a7b5a6ec82..89e43580b68c 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/MultiWindowTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/MultiWindowTests.swift @@ -43,14 +43,14 @@ class MultiWindowTests: IpadOnlyTestCase { let homeButtom = AccessibilityIdentifiers.Toolbar.addNewTabButton // A new tab is opened in the same window app.collectionViews.cells.matching(identifier: topSites).firstMatch.waitAndTap() - app.buttons[homeButtom].firstMatch.tap() - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].firstMatch.tap() + app.buttons[homeButtom].firstMatch.waitAndTap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].firstMatch.waitAndTap() let tabButtonSecondWindow = app.buttons.matching(identifier: tabsButtonIdentifier).element(boundBy: 0) XCTAssertEqual(tabButtonSecondWindow.value as? String, "2", "Number of tabs opened should be equal to 2") // A new tab is opened in the same window app.collectionViews.cells.matching(identifier: topSites).element(boundBy: 6).waitAndTap() - app.buttons[homeButtom].firstMatch.tap() - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].firstMatch.tap() + app.buttons[homeButtom].firstMatch.waitAndTap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].firstMatch.waitAndTap() let tabButtonFirstWindow = app.buttons.matching(identifier: tabsButtonIdentifier).element(boundBy: 1) XCTAssertEqual(tabButtonFirstWindow.value as? String, "2", "Number of tabs opened should be equal to 2") } @@ -64,7 +64,7 @@ class MultiWindowTests: IpadOnlyTestCase { private func splitViewFromHomeScreen() { dotMenu.waitAndTap() splitView.waitAndTap() - springboard.icons.elementContainingText("split view with Fennec").tap() + springboard.icons.elementContainingText("split view with Fennec").waitAndTap() } // Param windowsNumber - number of tab windows to open from switcher @@ -72,7 +72,7 @@ class MultiWindowTests: IpadOnlyTestCase { for _ in 1...windowsNumber { dotMenu.waitAndTap() let cardOrgMozillaIosFennecButton = springboard.buttons["card:org.mozilla.ios.Fennec:"] - cardOrgMozillaIosFennecButton.tap() + cardOrgMozillaIosFennecButton.waitAndTap() } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NavigationTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NavigationTest.swift index 2b2569baf315..097679d34dbf 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NavigationTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NavigationTest.swift @@ -34,7 +34,7 @@ class NavigationTest: BaseTestCase { XCTAssertFalse(app.buttons[AccessibilityIdentifiers.Toolbar.forwardButton].isEnabled) if iPad() { - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() // Once an url has been open, the back button is enabled but not the forward button navigator.performAction(Action.CloseURLBarOpen) navigator.nowAt(NewTabScreen) @@ -53,13 +53,13 @@ class NavigationTest: BaseTestCase { XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Toolbar.backButton].isEnabled) XCTAssertFalse(app.buttons[AccessibilityIdentifiers.Toolbar.forwardButton].isEnabled) // Go back to previous visited web site - app.buttons[AccessibilityIdentifiers.Toolbar.backButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.backButton].waitAndTap() waitUntilPageLoad() mozWaitForValueContains(url, value: "localhost") if iPad() { - app.buttons[AccessibilityIdentifiers.Toolbar.forwardButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.forwardButton].waitAndTap() } else { // Go forward to next visited web site app.buttons[AccessibilityIdentifiers.Toolbar.forwardButton].waitAndTap() @@ -106,7 +106,7 @@ class NavigationTest: BaseTestCase { app.navigationBars[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaNavigationBar], timeout: TIMEOUT_LONG ) - app.buttons["EmailSignIn.button"].tap() + app.buttons["EmailSignIn.button"].waitAndTap() mozWaitForElementToExist(app.webViews.textFields.element(boundBy: 0), timeout: TIMEOUT_LONG) let email = app.webViews.textFields.element(boundBy: 0) @@ -124,7 +124,7 @@ class NavigationTest: BaseTestCase { navigator.goto(TabTray) navigator.performAction(Action.ToggleSyncMode) - app.tables.buttons[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaSettingsButton].tap() + app.tables.buttons[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaSettingsButton].waitAndTap() waitForElementsToExist( [ app.navigationBars["Sync and Save Data"], @@ -174,8 +174,8 @@ class NavigationTest: BaseTestCase { app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 2) mozWaitForElementToExist(app.tables["Context Menu"]) - app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].tap() - app.buttons["Go"].tap() + app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].waitAndTap() + app.buttons["Go"].waitAndTap() waitUntilPageLoad() let url = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] mozWaitForValueContains(url, value: website_2["moreLinkLongPressInfo"]!) @@ -190,7 +190,7 @@ class NavigationTest: BaseTestCase { mozWaitForElementToExist(app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField]) app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 2) - app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].tap() + app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].waitAndTap() app.buttons["Go"].waitAndTap() waitUntilPageLoad() let url = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] @@ -242,7 +242,7 @@ class NavigationTest: BaseTestCase { // Since the textField value appears all selected first time is clicked // this workaround is necessary mozWaitForElementToNotExist(app.staticTexts["XCUITests-Runner pasted from Fennec"]) - urlBarAddress.tap() + urlBarAddress.waitAndTap() mozWaitForElementToExist(app.menuItems["Copy"]) if iPad() { XCTAssertTrue(app.menuItems["Cut"].exists) @@ -262,25 +262,24 @@ class NavigationTest: BaseTestCase { private func longPressLinkOptions(optionSelected: String) { navigator.nowAt(NewTabScreen) if app.buttons["Done"].exists { - app.buttons["Done"].tap() + app.buttons["Done"].waitAndTap() } navigator.goto(ClearPrivateDataSettings) - app.cells.switches["Downloaded Files"].tap() + app.cells.switches["Downloaded Files"].waitAndTap() navigator.performAction(Action.AcceptClearPrivateData) navigator.goto(HomePanelsScreen) navigator.openURL(path(forTestPage: "test-example.html")) waitUntilPageLoad() app.webViews.links[website_2["link"]!].press(forDuration: 2) - app.buttons[optionSelected].tap() + app.buttons[optionSelected].waitAndTap() } // https://mozilla.testrail.io/index.php?/cases/view/2441498 func testDownloadLink() { longPressLinkOptions(optionSelected: "Download Link") mozWaitForElementToExist(app.tables["Context Menu"]) - XCTAssertTrue(app.tables["Context Menu"].otherElements[StandardImageIdentifiers.Large.download].exists) - app.tables["Context Menu"].otherElements[StandardImageIdentifiers.Large.download].tap() + app.tables["Context Menu"].otherElements[StandardImageIdentifiers.Large.download].waitAndTap() navigator.goto(BrowserTabMenu) navigator.goto(LibraryPanel_Downloads) mozWaitForElementToExist(app.tables["DownloadsTable"]) @@ -290,7 +289,7 @@ class NavigationTest: BaseTestCase { mozWaitForElementToExist(app.tables.cells.staticTexts["example-domains.html"]) // Tap on the just downloaded link to check that the web page is loaded - app.tables.cells.staticTexts["example-domains.html"].tap() + app.tables.cells.staticTexts["example-domains.html"].waitAndTap() waitUntilPageLoad() let url = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] mozWaitForValueContains(url, value: "example-domains.html") @@ -362,8 +361,7 @@ class NavigationTest: BaseTestCase { // Now disable the Block PopUps option navigator.goto(BrowserTabMenu) navigator.goto(SettingsScreen) - mozWaitForElementToExist(switchBlockPopUps, timeout: TIMEOUT) - switchBlockPopUps.tap() + switchBlockPopUps.waitAndTap() let switchValueAfter = switchBlockPopUps.value! XCTAssertEqual(switchValueAfter as? String, "0") @@ -386,7 +384,7 @@ class NavigationTest: BaseTestCase { navigator.openURL("https://expired.badssl.com/") mozWaitForElementToExist(app.webViews.otherElements["This Connection is Untrusted"]) XCTAssertTrue(app.webViews.otherElements["This Connection is Untrusted"].exists) - app.buttons["Go Back"].tap() + app.buttons["Go Back"].waitAndTap() navigator.nowAt(NewTabScreen) navigator.openURL("https://expired.badssl.com/") mozWaitForElementToExist(app.webViews.otherElements["This Connection is Untrusted"]) @@ -404,7 +402,7 @@ class NavigationTest: BaseTestCase { navigator.goto(SettingsScreen) mozWaitForElementToExist(app.tables[AccessibilityIdentifiers.Settings.tableViewController]) let switchBlockPopUps = app.tables.cells.switches["blockPopups"] - switchBlockPopUps.tap() + switchBlockPopUps.waitAndTap() let switchValueAfter = switchBlockPopUps.value! XCTAssertEqual(switchValueAfter as? String, "0") navigator.goto(HomePanelsScreen) @@ -479,9 +477,9 @@ class NavigationTest: BaseTestCase { XCTAssertEqual(numTabs, 1, "Total number of regulat opened tabs should be 1") mozWaitForElementToExist(app.otherElements["Tabs Tray"].cells.elementContainingText("Example Domain.")) if iPad() { - app.buttons["Private"].tap() + app.buttons["Private"].waitAndTap() } else { - app.buttons["privateModeLarge"].tap() + app.buttons["privateModeLarge"].waitAndTap() } numTabs = app.otherElements["Tabs Tray"].cells.count XCTAssertEqual(numTabs, 1, "Total number of private opened tabs should be 1") @@ -492,8 +490,7 @@ class NavigationTest: BaseTestCase { func testBookmarkLink() { // Long-tap on an article link. Choose "Bookmark Link". openContextMenuForArticleLink() - mozWaitForElementToExist(app.buttons["Bookmark Link"]) - app.buttons["Bookmark Link"].tap() + app.buttons["Bookmark Link"].waitAndTap() // The link has been added to the Bookmarks panel in Library navigator.goto(LibraryPanel_Bookmarks) waitForElementsToExist( @@ -517,14 +514,14 @@ class NavigationTest: BaseTestCase { mozWaitForElementToExist(backButton) mozWaitElementHittable(element: backButton, timeout: TIMEOUT) XCTAssertTrue(backButton.isEnabled, "Back button is disabled") - backButton.tap() + backButton.waitAndTap() waitUntilPageLoad() if #available(iOS 16, *) { let url = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] mozWaitForValueContains(url, value: "localhost") XCTAssertTrue(backButton.isHittable, "Back button is not hittable") XCTAssertTrue(backButton.isEnabled, "Back button is disabled") - backButton.tap() + backButton.waitAndTap() waitUntilPageLoad() mozWaitForElementToExist(app.links[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell]) } @@ -538,7 +535,7 @@ class NavigationTest: BaseTestCase { let switchBlockLinks = app.tables.cells.switches["blockOpeningExternalApps"] scrollToElement(switchBlockLinks) if let switchValue = switchBlockLinks.value as? String, switchValue == "1" { - switchBlockLinks.tap() + switchBlockLinks.waitAndTap() } // Open website and tap on one of the external article links validateExternalLink() diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NewTabSettings.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NewTabSettings.swift index bad86351b82b..f63a6d999fe7 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NewTabSettings.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NewTabSettings.swift @@ -153,7 +153,7 @@ class NewTabSettingsTest: BaseTestCase { mozWaitForValueContains(url, value: "mozilla") XCTAssertFalse(url.isSelected, "The URL has the focus") XCTAssertFalse(app.keyboards.element.isVisible(), "The keyboard is shown") - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() validateKeyboardIsRaisedAndDismissed() @@ -164,7 +164,7 @@ class NewTabSettingsTest: BaseTestCase { mozWaitForValueContains(url, value: "mozilla") XCTAssertFalse(url.isSelected, "The URL has the focus") XCTAssertFalse(app.keyboards.element.isVisible(), "The keyboard is shown") - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() validateKeyboardIsRaisedAndDismissed() } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NightModeTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NightModeTests.swift index 2863f22b036e..9f471da65e67 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NightModeTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NightModeTests.swift @@ -11,7 +11,7 @@ class NightModeTests: BaseTestCase { mozWaitForElementToExist(app.tables.cells["MainMenu.NightModeOn"]) XCTAssertTrue(app.tables.cells["MainMenu.NightModeOn"].label == "Turn off Night Mode") // Turn off night mode - app.tables.cells["MainMenu.NightModeOn"].tap() + app.tables.cells["MainMenu.NightModeOn"].waitAndTap() } private func checkNightModeOff() { diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/OnboardingTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/OnboardingTests.swift index 576d44b3bc66..6a9230cfe1ca 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/OnboardingTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/OnboardingTests.swift @@ -48,7 +48,7 @@ class OnboardingTests: BaseTestCase { try app.performAccessibilityAudit() // Swipe to the second screen - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)SecondaryButton"].waitAndTap() currentScreen += 1 waitForElementsToExist( [ @@ -72,7 +72,7 @@ class OnboardingTests: BaseTestCase { try app.performAccessibilityAudit() // Swipe to the fourth screen - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)SecondaryButton"].waitAndTap() currentScreen += 1 mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"], timeout: TIMEOUT) XCTAssertTrue(app.images["\(rootA11yId)ImageView"].exists) @@ -83,7 +83,7 @@ class OnboardingTests: BaseTestCase { try app.performAccessibilityAudit() // Swipe to the fifth screen - app.buttons["\(rootA11yId)PrimaryButton"].tap() + app.buttons["\(rootA11yId)PrimaryButton"].waitAndTap() currentScreen += 1 mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"], timeout: TIMEOUT) XCTAssertTrue(app.images["\(rootA11yId)ImageView"].exists) @@ -94,14 +94,14 @@ class OnboardingTests: BaseTestCase { try app.performAccessibilityAudit() // Finish onboarding - app.buttons["\(rootA11yId)PrimaryButton"].tap() + app.buttons["\(rootA11yId)PrimaryButton"].waitAndTap() let topSites = app.collectionViews.links[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell] mozWaitForElementToExist(topSites) } // https://mozilla.testrail.io/index.php?/cases/view/2793818 func testFirstRunTourDarkMode() { - app.buttons["CloseButton"].tap() + app.buttons["CloseButton"].waitAndTap() switchThemeToDarkOrLight(theme: "Dark") app.terminate() app.launch() @@ -120,7 +120,7 @@ class OnboardingTests: BaseTestCase { ) // Swipe to the second screen - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)SecondaryButton"].waitAndTap() currentScreen += 1 waitForElementsToExist( [ @@ -142,7 +142,7 @@ class OnboardingTests: BaseTestCase { XCTAssertTrue(app.buttons["\(rootA11yId)SecondaryButton"].exists) // Swipe to the fourth screen - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)SecondaryButton"].waitAndTap() currentScreen += 1 mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"], timeout: TIMEOUT) XCTAssertTrue(app.images["\(rootA11yId)ImageView"].exists) @@ -152,7 +152,7 @@ class OnboardingTests: BaseTestCase { XCTAssertTrue(app.buttons["\(rootA11yId)SecondaryButton"].exists) // Swipe to the fifth screen - app.buttons["\(rootA11yId)PrimaryButton"].tap() + app.buttons["\(rootA11yId)PrimaryButton"].waitAndTap() currentScreen += 1 mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"], timeout: TIMEOUT) XCTAssertTrue(app.images["\(rootA11yId)ImageView"].exists) @@ -163,7 +163,7 @@ class OnboardingTests: BaseTestCase { // Finish onboarding let topSites = app.collectionViews.links[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell] - app.buttons["\(rootA11yId)PrimaryButton"].tap() + app.buttons["\(rootA11yId)PrimaryButton"].waitAndTap() mozWaitForElementToExist(topSites) } @@ -172,7 +172,7 @@ class OnboardingTests: BaseTestCase { func testOnboardingSignIn() { mozWaitForElementToExist(app.staticTexts["\(rootA11yId)TitleLabel"]) // Swipe to the second screen - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)SecondaryButton"].waitAndTap() currentScreen += 1 mozWaitForElementToExist(app.staticTexts["\(rootA11yId)TitleLabel"]) XCTAssertEqual("Stay encrypted when you hop between devices", app.staticTexts["\(rootA11yId)TitleLabel"].label) @@ -181,31 +181,31 @@ class OnboardingTests: BaseTestCase { XCTAssertEqual("Sign In", app.buttons["\(rootA11yId)PrimaryButton"].label) XCTAssertEqual("Skip", app.buttons["\(rootA11yId)SecondaryButton"].label) // Tap on Sign In - app.buttons["\(rootA11yId)PrimaryButton"].tap() + app.buttons["\(rootA11yId)PrimaryButton"].waitAndTap() mozWaitForElementToExist(app.navigationBars["Sync and Save Data"]) XCTAssertTrue(app.buttons["QRCodeSignIn.button"].exists) XCTAssertEqual("Ready to Scan", app.buttons["QRCodeSignIn.button"].label) XCTAssertTrue(app.buttons["EmailSignIn.button"].exists) XCTAssertEqual("Use Email Instead", app.buttons["EmailSignIn.button"].label) - app.buttons["Done"].tap() - app.buttons["CloseButton"].tap() + app.buttons["Done"].waitAndTap() + app.buttons["CloseButton"].waitAndTap() } // Smoketest // https://mozilla.testrail.io/index.php?/cases/view/2306816 func testCloseTour() { - app.buttons["\(AccessibilityIdentifiers.Onboarding.closeButton)"].tap() + app.buttons["\(AccessibilityIdentifiers.Onboarding.closeButton)"].waitAndTap() let topSites = app.collectionViews.links[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell] mozWaitForElementToExist(topSites) } // https://mozilla.testrail.io/index.php?/cases/view/2306815 func testWhatsNewPage() { - app.buttons["\(AccessibilityIdentifiers.Onboarding.closeButton)"].tap() + app.buttons["\(AccessibilityIdentifiers.Onboarding.closeButton)"].waitAndTap() navigator.goto(BrowserTabMenu) navigator.performAction(Action.OpenWhatsNewPage) waitUntilPageLoad() - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() // Extract version number from url let url = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].value @@ -221,7 +221,7 @@ class OnboardingTests: BaseTestCase { app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField], value: "https://www.mozilla.org/en-US/firefox/ios/" + releaseVersion + "/releasenotes/" ) - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].tap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].waitAndTap() waitForElementsToExist( [ app.staticTexts["Release Notes"], @@ -240,23 +240,23 @@ class OnboardingTests: BaseTestCase { // Wait for the initial title label to appear mozWaitForElementToExist(app.staticTexts["\(rootA11yId)TitleLabel"]) - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)SecondaryButton"].waitAndTap() currentScreen += 1 mozWaitForElementToExist(app.staticTexts["\(rootA11yId)TitleLabel"]) - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)SecondaryButton"].waitAndTap() currentScreen += 1 mozWaitForElementToExist(app.staticTexts["\(rootA11yId)TitleLabel"]) - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)SecondaryButton"].waitAndTap() currentScreen += 1 mozWaitForElementToExist(app.staticTexts["\(rootA11yId)TitleLabel"]) - app.buttons["\(rootA11yId)PrimaryButton"].tap() + app.buttons["\(rootA11yId)PrimaryButton"].waitAndTap() currentScreen += 1 let buttons = app.buttons.matching(identifier: "\(rootA11yId)MultipleChoiceButton") for i in 0.. screenState.gesture(forAction: TestActions.LoadURLByPasting, TestActions.LoadURL) { userState in UIPasteboard.general.string = userState.url ?? defaultURL app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 1.0) - app.tables["Context Menu"].cells[AccessibilityIdentifiers.Photon.pasteAndGoAction].tap() + app.tables["Context Menu"].cells[AccessibilityIdentifiers.Photon.pasteAndGoAction].waitAndTap() } } @@ -192,15 +192,15 @@ private func createTestGraph(for test: XCTestCase, with app: XCUIApplication) -> screenState.backAction = { if isTablet { // There is no Cancel option in iPad. - app.otherElements["PopoverDismissRegion"].tap() + app.otherElements["PopoverDismissRegion"].waitAndTap() } else { - app.buttons["PhotonMenu.close"].tap() + app.buttons["PhotonMenu.close"].waitAndTap() } } } let navigationControllerBackAction = { - app.navigationBars.element(boundBy: 0).buttons.element(boundBy: 0).tap() + app.navigationBars.element(boundBy: 0).buttons.element(boundBy: 0).waitAndTap() } map.addScreenState(SettingsScreen) { screenState in diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchSettingsUITest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchSettingsUITest.swift index ba3a133270cb..5eca68596dd0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchSettingsUITest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchSettingsUITest.swift @@ -18,9 +18,9 @@ class SearchSettingsUITests: BaseTestCase { mozWaitForElementToExist(app.tables.cells.staticTexts[defaultSearchEngine1]) // Change to another browser and check it is set as default - defaultSearchEngine.tap() + defaultSearchEngine.waitAndTap() let listOfEngines = app.tables - listOfEngines.staticTexts[defaultSearchEngine2].tap() + listOfEngines.staticTexts[defaultSearchEngine2].waitAndTap() mozWaitForElementToExist(app.tables.cells.staticTexts[defaultSearchEngine2]) } @@ -35,7 +35,7 @@ class SearchSettingsUITests: BaseTestCase { // Check that it can be edited XCTAssertTrue(app.buttons["Edit"].isEnabled) - app.buttons["Edit"].tap() + app.buttons["Edit"].waitAndTap() XCTAssertTrue(app.buttons["Done"].isEnabled) if #unavailable(iOS 17) { mozWaitForElementToExist(app.tables.buttons["Delete \(customSearchEngine["name"]!)"]) @@ -68,9 +68,9 @@ class SearchSettingsUITests: BaseTestCase { // Select the custom engine as the default one let defaultSearchEngine = app.tables.cells.element(boundBy: 0) - defaultSearchEngine.tap() + defaultSearchEngine.waitAndTap() let listOfEngines = app.tables - listOfEngines.staticTexts[customSearchEngine["name"]!].tap() + listOfEngines.staticTexts[customSearchEngine["name"]!].waitAndTap() // Edit is disabled XCTAssertFalse(app.buttons["Edit"].isEnabled) } @@ -85,13 +85,13 @@ class SearchSettingsUITests: BaseTestCase { addCustomSearchEngine() // Edit is enabled XCTAssertTrue(app.buttons["Edit"].isEnabled) - app.buttons["Edit"].tap() + app.buttons["Edit"].waitAndTap() XCTAssertTrue(app.buttons["Done"].isEnabled) // Navigate to the search engine picker and back let defaultSearchEngine = app.tables.cells.element(boundBy: 0) - defaultSearchEngine.tap() - app.buttons["Cancel"].tap() + defaultSearchEngine.waitAndTap() + app.buttons["Cancel"].waitAndTap() // Check to see we're not in editing state, edit is enable and done does not appear XCTAssertTrue(app.buttons["Edit"].isEnabled) @@ -110,15 +110,15 @@ class SearchSettingsUITests: BaseTestCase { // Add a custom search engine addCustomSearchEngine() XCTAssertTrue(app.buttons["Edit"].isEnabled) - app.buttons["Edit"].tap() + app.buttons["Edit"].waitAndTap() // Remove the custom search engine and check that edit is disabled let tablesQuery = app.tables if #unavailable(iOS 17) { - tablesQuery.buttons["Delete \(customSearchEngine["name"]!)"].tap() + tablesQuery.buttons["Delete \(customSearchEngine["name"]!)"].waitAndTap() } else { - tablesQuery.buttons["Remove \(customSearchEngine["name"]!)"].tap() + tablesQuery.buttons["Remove \(customSearchEngine["name"]!)"].waitAndTap() } - tablesQuery.buttons[AccessibilityIdentifiers.Settings.Search.deleteButton].tap() + tablesQuery.buttons[AccessibilityIdentifiers.Settings.Search.deleteButton].waitAndTap() XCTAssertFalse(app.buttons["Edit"].isEnabled) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift index 5f822e8fac38..83220337d147 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift @@ -22,9 +22,9 @@ class SearchTests: BaseTestCase { private func suggestionsOnOff() { navigator.goto(SearchSettings) - app.tables.switches["Show Search Suggestions"].tap() - app.navigationBars["Search"].buttons["Settings"].tap() - app.navigationBars["Settings"].buttons[AccessibilityIdentifiers.Settings.navigationBarItem].tap() + app.tables.switches["Show Search Suggestions"].waitAndTap() + app.navigationBars["Search"].buttons["Settings"].waitAndTap() + app.navigationBars["Settings"].buttons[AccessibilityIdentifiers.Settings.navigationBarItem].waitAndTap() } private func validateSearchSuggestionText(typeText: String) { @@ -50,10 +50,10 @@ class SearchTests: BaseTestCase { mozWaitForElementToExist(app.tables["SiteTable"].cells.firstMatch) // Disable Search suggestion - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].tap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].waitAndTap() waitForTabsButton() - app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton].waitAndTap() navigator.nowAt(BrowserTabMenu) suggestionsOnOff() @@ -65,16 +65,16 @@ class SearchTests: BaseTestCase { mozWaitForElementToNotExist(app.tables["SiteTable"].cells.firstMatch) // Verify that previous choice is remembered - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].tap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].waitAndTap() navigator.nowAt(HomePanelsScreen) waitForTabsButton() typeOnSearchBar(text: "foobar") mozWaitForElementToNotExist(app.tables["SiteTable"].cells[SuggestedSite]) - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].tap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].waitAndTap() waitForTabsButton() - app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton].waitAndTap() navigator.nowAt(BrowserTabMenu) // Reset suggestion button, set it to on @@ -156,7 +156,7 @@ class SearchTests: BaseTestCase { waitUntilPageLoad() // Go back, write part of moz, check the autocompletion - app.buttons[AccessibilityIdentifiers.Toolbar.backButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.backButton].waitAndTap() navigator.nowAt(HomePanelsScreen) waitForTabsButton() typeOnSearchBar(text: "moz") @@ -170,9 +170,9 @@ class SearchTests: BaseTestCase { mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.goto(SearchSettings) // Open the list of default search engines and select the desired - app.tables.cells.element(boundBy: 0).tap() + app.tables.cells.element(boundBy: 0).waitAndTap() let tablesQuery2 = app.tables - tablesQuery2.staticTexts[searchEngine].tap() + tablesQuery2.staticTexts[searchEngine].waitAndTap() navigator.openURL("foo bar") mozWaitForElementToExist(app.webViews.firstMatch) @@ -210,7 +210,7 @@ class SearchTests: BaseTestCase { // Click on the > button to get to that option only on iPhone if #available(iOS 16, *) { while !app.collectionViews.menuItems["Search with Firefox"].exists { - app.buttons["Forward"].firstMatch.tap() + app.buttons["Forward"].firstMatch.waitAndTap() waitForElementsToExist( [ app.collectionViews.menuItems.firstMatch, @@ -220,7 +220,7 @@ class SearchTests: BaseTestCase { } } else { while !app.menuItems["Search with Firefox"].exists { - app.menuItems["Show more items"].firstMatch.tap() + app.menuItems["Show more items"].firstMatch.waitAndTap() waitForElementsToExist( [ app.menuItems.firstMatch, @@ -275,10 +275,10 @@ class SearchTests: BaseTestCase { // Reload icon is displayed. mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton]) XCTAssertEqual(app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].label, "New Tab") - app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() navigator.performAction(Action.CloseURLBarOpen) XCTAssertEqual(app.buttons[AccessibilityIdentifiers.Toolbar.searchButton].label, "Search") - app.buttons[AccessibilityIdentifiers.Toolbar.searchButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.searchButton].waitAndTap() mozWaitForElementToExist(addressBar) XCTAssertTrue(addressBar.value(forKey: "hasKeyboardFocus") as? Bool ?? false) @@ -332,7 +332,7 @@ class SearchTests: BaseTestCase { // In a new tab, tap on the URL bar navigator.goto(NewTabScreen) - urlBar.tap() + urlBar.waitAndTap() // The URL bar is focused and the keyboard is displayed validateUrlHasFocusAndKeyboardIsDisplayed() @@ -346,14 +346,14 @@ class SearchTests: BaseTestCase { waitUntilPageLoad() // Tap on the URL bar - urlBar.tap() + urlBar.waitAndTap() // The URL bar is focused, Top Sites panel is displayed and the keyboard pops-up validateUrlHasFocusAndKeyboardIsDisplayed() mozWaitForElementToExist(app.links[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell]) // Tap the back icon < - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].tap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].waitAndTap() // The focused is dismissed from the URL bar let addressBar = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] @@ -370,7 +370,7 @@ class SearchTests: BaseTestCase { typeTextAndValidateSearchSuggestions(text: "g", isSwitchOn: true) // Tap on the "Append Arrow button" - app.tables.buttons[StandardImageIdentifiers.Large.appendUpLeft].firstMatch.tap() + app.tables.buttons[StandardImageIdentifiers.Large.appendUpLeft].firstMatch.waitAndTap() // The search suggestion fills the URL bar but does not conduct the search waitForValueContains(urlBarAddress, value: "g") @@ -415,7 +415,7 @@ class SearchTests: BaseTestCase { typeTextAndValidateSearchSuggestions(text: "g", isSwitchOn: true) // Tap on the text letter "g" - app.tables.cells.firstMatch.tap() + app.tables.cells.firstMatch.waitAndTap() waitUntilPageLoad() // The search is conducted through the default search engine @@ -430,7 +430,7 @@ class SearchTests: BaseTestCase { typeTextAndValidateSearchSuggestions(text: "g", isSwitchOn: false) // Enable "Show search suggestions" from Settings and type text in a new tab - app.tables.cells.firstMatch.tap() + app.tables.cells.firstMatch.waitAndTap() waitUntilPageLoad() createNewTabAfterModifyingSearchSuggestions(turnOnSwitch: true) @@ -444,9 +444,9 @@ class SearchTests: BaseTestCase { mozWaitForElementToExist(showSearchSuggestions) let switchValue = showSearchSuggestions.value if switchValue as? String == "0", true && turnOnSwitch == true { - showSearchSuggestions.tap() + showSearchSuggestions.waitAndTap() } else if switchValue as? String == "1", true && turnOnSwitch == false { - showSearchSuggestions.tap() + showSearchSuggestions.waitAndTap() } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SettingsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SettingsTests.swift index 1c38b6b3a6ba..30063c881d4c 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SettingsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SettingsTests.swift @@ -26,7 +26,7 @@ class SettingsTests: BaseTestCase { } let helpMenu = settingsTableView.cells["Help"] XCTAssertTrue(helpMenu.isEnabled) - helpMenu.tap() + helpMenu.waitAndTap() waitUntilPageLoad() let url = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] @@ -60,15 +60,15 @@ class SettingsTests: BaseTestCase { XCTAssertEqual(value as? String, "0") // Switch on, Offer to open copied links, when opening firefox - app.tables.cells.switches["Offer to Open Copied Links, When opening Firefox"].tap() + app.tables.cells.switches["Offer to Open Copied Links, When opening Firefox"].waitAndTap() // Check Offer to open copied links, when opening firefox is on let value2 = app.tables.cells.switches["Offer to Open Copied Links, When opening Firefox"].value XCTAssertEqual(value2 as? String, "1") - app.navigationBars["Settings"].buttons["Done"].tap() + app.navigationBars["Settings"].buttons["Done"].waitAndTap() - app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton].waitAndTap() app.staticTexts["Settings"].waitAndTap() // Check Offer to open copied links, when opening firefox is on @@ -99,7 +99,7 @@ class SettingsTests: BaseTestCase { // Check that tapping on an element does nothing mozWaitForElementToExist(app.tables["OpenWithPage.Setting.Options"]) - app.tables.cells.staticTexts["Airmail"].tap() + app.tables.cells.staticTexts["Airmail"].waitAndTap() XCTAssertFalse(app.tables.cells.staticTexts["Airmail"].isSelected) // Check that user can go back from that setting diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SiteLoadTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SiteLoadTest.swift index 48c5c52cd161..a0e56e79250e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SiteLoadTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SiteLoadTest.swift @@ -26,7 +26,7 @@ class SiteLoadTest: BaseTestCase { // clear the cache navigator.goto(ClearPrivateDataSettings) - app.tables.staticTexts["Clear Private Data"].tap() + app.tables.staticTexts["Clear Private Data"].waitAndTap() app.alerts.buttons["OK"].waitAndTap() navigator.goto(BrowserTab) mozWaitForElementToExist(app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField]) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SyncFAUITests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SyncFAUITests.swift index e4642f692c59..3e19d0c3a4f8 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SyncFAUITests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SyncFAUITests.swift @@ -79,7 +79,7 @@ class SyncUITests: BaseTestCase { userState.fxaPassword = "atleasteight" navigator.performAction(Action.FxATypePasswordNewAccount) // Switching to the next text field is required to determine if the message still appears or not - app.webViews.secureTextFields.element(boundBy: 0).tap() + app.webViews.secureTextFields.element(boundBy: 0).waitAndTap() mozWaitForElementToNotExist(app.webViews.staticTexts["At least 8 characters"]) } @@ -120,7 +120,7 @@ class SyncUITests: BaseTestCase { let passMessage = "Your password is currently hidden." mozWaitForElementToExist(app.webViews.switches[passMessage]) // Remove the password typed, Show (password) option should not be shown - app.keyboards.keys["delete"].tap() + app.keyboards.keys["delete"].waitAndTap() mozWaitForElementToNotExist(app.webViews.staticTexts[passMessage]) } @@ -137,7 +137,7 @@ class SyncUITests: BaseTestCase { app.buttons["Ready to Scan"], app.buttons["Use Email Instead"]] ) - app.navigationBars[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaNavigationBar].buttons["Close"].tap() + app.navigationBars[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaNavigationBar].buttons["Close"].waitAndTap() mozWaitForElementToExist(app.collectionViews.links[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell]) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TabCounterTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TabCounterTests.swift index b06237469c54..bd08b176e665 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TabCounterTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TabCounterTests.swift @@ -59,7 +59,7 @@ class TabCounterTests: BaseTestCase { if isTablet { app.otherElements["Tabs Tray"] .collectionViews.cells.element(boundBy: 0) - .buttons[StandardImageIdentifiers.Large.cross].tap() + .buttons[StandardImageIdentifiers.Large.cross].waitAndTap() } else { let navBarTabTrayButton = app.segmentedControls["navBarTabTray"].buttons.firstMatch mozWaitForElementToExist(navBarTabTrayButton) @@ -68,10 +68,10 @@ class TabCounterTests: BaseTestCase { XCTAssertTrue(tabsOpenTabTray.hasSuffix("2")) app.otherElements["Tabs Tray"].cells - .element(boundBy: 0).buttons[StandardImageIdentifiers.Large.cross].tap() + .element(boundBy: 0).buttons[StandardImageIdentifiers.Large.cross].waitAndTap() } - app.otherElements["Tabs Tray"].cells.element(boundBy: 0).tap() + app.otherElements["Tabs Tray"].cells.element(boundBy: 0).waitAndTap() navigator.nowAt(NewTabScreen) waitForTabsButton() diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ThirdPartySearchTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ThirdPartySearchTest.swift index db4f2094a0de..6fa8b964b81d 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ThirdPartySearchTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ThirdPartySearchTest.swift @@ -8,7 +8,7 @@ let mozDeveloperWebsite = "https://developer.mozilla.org/en-US" let searchFieldPlaceholder = "Search MDN" class ThirdPartySearchTest: BaseTestCase { fileprivate func dismissKeyboardAssistant(forApp app: XCUIApplication) { - app.buttons["Done"].tap() + app.buttons["Done"].waitAndTap() } // https://mozilla.testrail.io/index.php?/cases/view/2443998 @@ -19,11 +19,11 @@ class ThirdPartySearchTest: BaseTestCase { app.navigationBars["Settings"].buttons[AccessibilityIdentifiers.Settings.navigationBarItem].waitAndTap() // Perform a search using a custom search engine - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton]) app.textFields.firstMatch.waitAndTap() app.textFields.firstMatch.typeText("window") - app.scrollViews.otherElements.buttons["Mozilla Engine search"].tap() + app.scrollViews.otherElements.buttons["Mozilla Engine search"].waitAndTap() waitUntilPageLoad() guard let url = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].value @@ -45,7 +45,7 @@ class ThirdPartySearchTest: BaseTestCase { dismissSearchScreen() // Perform a search to check - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() app.textFields.firstMatch.typeText("window\n") waitUntilPageLoad() @@ -66,7 +66,7 @@ class ThirdPartySearchTest: BaseTestCase { app.navigationBars["Search"].buttons["Settings"].waitAndTap() app.navigationBars["Settings"].buttons[AccessibilityIdentifiers.Settings.navigationBarItem].waitAndTap() - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton]) app.textFields.firstMatch.waitAndTap() app.textFields.firstMatch.typeText("window") @@ -110,7 +110,7 @@ class ThirdPartySearchTest: BaseTestCase { waitForTabsButton() navigator.nowAt(NewTabScreen) navigator.goto(AddCustomSearchSettings) - app.textViews["customEngineTitle"].tap() + app.textViews["customEngineTitle"].waitAndTap() app.typeText("Feeling Lucky") let searchUrl = "http://www.google.com/search?q=&btnI" @@ -122,18 +122,18 @@ class ThirdPartySearchTest: BaseTestCase { mozWaitForElementToExist(customengineurlTextView) UIPasteboard.general.string = searchUrl - customengineurlTextView.tap() + customengineurlTextView.waitAndTap() customengineurlTextView.press(forDuration: 2.0) let pasteOption = app.menuItems["Paste"] if pasteOption.exists { - pasteOption.tap() + pasteOption.waitAndTap() } else { var nrOfTaps = 3 while !pasteOption.exists && nrOfTaps > 0 { customengineurlTextView.press(forDuration: 2.0) nrOfTaps -= 1 } - pasteOption.tap() + pasteOption.waitAndTap() } app.buttons["customEngineSaveButton"].waitAndTap() diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TodayWidgetTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TodayWidgetTests.swift index dbff2fb17a95..5ec5ddc8fa3b 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TodayWidgetTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TodayWidgetTests.swift @@ -140,10 +140,10 @@ class TodayWidgetTests: BaseTestCase { } if #unavailable(iOS 16) { mozWaitElementHittable(element: springboard.buttons["Remove Widget"], timeout: TIMEOUT) - springboard.buttons["Remove Widget"].tap() + springboard.buttons["Remove Widget"].waitAndTap() } else { mozWaitElementHittable(element: springboard.buttons[removeWidgetButton], timeout: TIMEOUT) - springboard.buttons[removeWidgetButton].tap() + springboard.buttons[removeWidgetButton].waitAndTap() } waitForElementsToExist( @@ -154,7 +154,7 @@ class TodayWidgetTests: BaseTestCase { ) mozWaitElementHittable(element: springboard.alerts.buttons["Remove"], timeout: TIMEOUT) - springboard.alerts.buttons["Remove"].tap() + springboard.alerts.buttons["Remove"].waitAndTap() } private func checkFirefoxAvailablesWidgets() { @@ -210,13 +210,13 @@ class TodayWidgetTests: BaseTestCase { } mozWaitElementHittable(element: springboard.buttons[removeWidgetButton], timeout: TIMEOUT) - springboard.buttons[removeWidgetButton].tap() + springboard.buttons[removeWidgetButton].waitAndTap() mozWaitForElementToExist(springboard.alerts.buttons["Remove"]) mozWaitForElementToExist(springboard.alerts.buttons["Cancel"]) mozWaitElementHittable(element: springboard.alerts.buttons["Remove"], timeout: TIMEOUT) - springboard.alerts.buttons["Remove"].tap() + springboard.alerts.buttons["Remove"].waitAndTap() } private func checkFirefoxWidgetOptions() { @@ -245,7 +245,7 @@ class TodayWidgetTests: BaseTestCase { mozWaitForElementToExist(springboard .buttons[editWidgetButton]) springboard - .buttons[editWidgetButton].tap() + .buttons[editWidgetButton].waitAndTap() } private func longPressOnWidget(widgetType: String, duration: Double) { @@ -257,7 +257,7 @@ class TodayWidgetTests: BaseTestCase { private func tapOnWidget(widgetType: String) { let widget = springboard.buttons.matching(NSPredicate(format: "label CONTAINS[c] %@", widgetType)).element mozWaitElementHittable(element: widget, timeout: TIMEOUT) - widget.tap() + widget.waitAndTap() } private func allowCopyFromOtherApps() { @@ -267,41 +267,36 @@ class TodayWidgetTests: BaseTestCase { iOS_Settings.swipeUp() } // Tap the first Firefox entry - iOS_Settings.staticTexts["org.mozilla.ios.Fennec"].tap() + iOS_Settings.staticTexts["org.mozilla.ios.Fennec"].waitAndTap() // Wait for "Paste from Other Apps" button, tap it, then allow copying - mozWaitForElementToExist(iOS_Settings.buttons["Paste from Other Apps"]) - iOS_Settings.buttons["Paste from Other Apps"].tap() - mozWaitForElementToExist(iOS_Settings.staticTexts["Allow"]) - iOS_Settings.staticTexts["Allow"].tap() + iOS_Settings.buttons["Paste from Other Apps"].waitAndTap() + iOS_Settings.staticTexts["Allow"].waitAndTap() } private func addWidget(widgetName: String) { if iPad() { - mozWaitForElementToExist(springboard.buttons["Add Widget"]) - springboard.buttons["Add Widget"].tap() + springboard.buttons["Add Widget"].waitAndTap() } else { if #available(iOS 18, *) { springboard.icons["Screen Time"].press(forDuration: 3) if springboard.buttons["Edit Home Screen"].exists { - springboard.buttons["Edit Home Screen"].tap() + springboard.buttons["Edit Home Screen"].waitAndTap() } } - springboard.buttons["Edit"].tap() - mozWaitForElementToExist(springboard.buttons["Add Widget"]) - springboard.buttons["Add Widget"].tap() + springboard.buttons["Edit"].waitAndTap() + springboard.buttons["Add Widget"].waitAndTap() } mozWaitElementHittable(element: springboard.searchFields["Search Widgets"], timeout: TIMEOUT) - springboard.searchFields["Search Widgets"].tap() + springboard.searchFields["Search Widgets"].waitAndTap() springboard.searchFields["Search Widgets"].typeText(widgetName) let predicate = NSPredicate(format: "label CONTAINS[c] %@", widgetName+" (") let cells = springboard.cells.matching(predicate) - cells.element.tap() + cells.element.waitAndTap() } private func findAndTapWidget(widgetType: String) { let widget = springboard.buttons.matching(NSPredicate(format: "label CONTAINS[c] %@", widgetType)).element - mozWaitForElementToExist(widget) - widget.tap() + widget.waitAndTap() } private func removeWidgetIfExists(widgetType: String) { @@ -313,11 +308,11 @@ class TodayWidgetTests: BaseTestCase { private func addAndSearchForWidget(widgetName: String) { addWidget(widgetName: widgetName) mozWaitElementHittable(element: springboard.searchFields["Search Widgets"], timeout: TIMEOUT) - springboard.searchFields["Search Widgets"].tap() + springboard.searchFields["Search Widgets"].waitAndTap() springboard.searchFields["Search Widgets"].typeText(widgetName) let predicate = NSPredicate(format: "label CONTAINS[c] %@", widgetName + " (") let cells = springboard.cells.matching(predicate) - cells.element.tap() + cells.element.waitAndTap() } // TESTS @@ -339,15 +334,15 @@ class TodayWidgetTests: BaseTestCase { // Check available widgets checkFirefoxAvailablesWidgets() // Add Quick Action Widget - springboard.buttons[" Add Widget"].tap() + springboard.buttons[" Add Widget"].waitAndTap() springboard.swipeDown() - springboard.buttons["Done"].tap() + springboard.buttons["Done"].waitAndTap() // Check Quick Action widget options checkFirefoxWidgetOptions() // Edit Widget and check the options - springboard.buttons[editWidgetButton].tap() + springboard.buttons[editWidgetButton].waitAndTap() mozWaitElementHittable(element: newSearch, timeout: TIMEOUT) - newSearch.tap() + newSearch.waitAndTap() // Verify widget actions if #unavailable(iOS 17) { goToCopiedLink = springboard.staticTexts["Go to Copied Link"] @@ -359,7 +354,7 @@ class TodayWidgetTests: BaseTestCase { XCTAssertTrue(goToCopiedLink.exists) XCTAssertTrue(newPrivateSearch.exists) XCTAssertTrue(clearPrivateTabs.exists) - newSearch.tap() + newSearch.waitAndTap() // Tap outside alert to close it mozWaitForElementToExist(newSearch) coordinate.tap() @@ -398,19 +393,19 @@ class TodayWidgetTests: BaseTestCase { // Check available widgets checkFirefoxAvailablesWidgets() // Add Quick Action Widget - springboard.buttons[" Add Widget"].tap() + springboard.buttons[" Add Widget"].waitAndTap() springboard.swipeDown() - springboard.buttons["Done"].tap() + springboard.buttons["Done"].waitAndTap() // Verify options available in the Quick Action Widget checkFirefoxWidgetOptions() // Edit widget and interact with options if #unavailable(iOS 16) { - springboard.buttons["Edit Widget"].tap() + springboard.buttons["Edit Widget"].waitAndTap() } else { - springboard.buttons[editWidgetButton].tap() + springboard.buttons[editWidgetButton].waitAndTap() } mozWaitElementHittable(element: newSearch, timeout: TIMEOUT) - newSearch.tap() + newSearch.waitAndTap() if #unavailable(iOS 17) { goToCopiedLink = springboard.staticTexts["Go to Copied Link"] newPrivateSearch = springboard.staticTexts["New Private Search"] @@ -424,7 +419,7 @@ class TodayWidgetTests: BaseTestCase { XCTAssertTrue(clearPrivateTabs.exists, "Clear Private Tabs button not found.") // Start a new private search mozWaitElementHittable(element: newPrivateSearch, timeout: TIMEOUT) - newPrivateSearch.tap() + newPrivateSearch.waitAndTap() // Tap outside the alert to dismiss it mozWaitForElementToExist(newPrivateSearch) coordinate.tap() @@ -435,7 +430,7 @@ class TodayWidgetTests: BaseTestCase { // Handle different UI behavior on iPad and iPhone if !iPad() { mozWaitElementHittable(element: app.buttons["CloseButton"], timeout: TIMEOUT) - app.buttons["CloseButton"].tap() + app.buttons["CloseButton"].waitAndTap() } // Verify the presence of Private Mode message mozWaitForElementToExist(app.staticTexts["Leave no traces on this device"]) @@ -471,19 +466,19 @@ class TodayWidgetTests: BaseTestCase { addWidget(widgetName: "Fennec") checkFirefoxAvailablesWidgets() // Add Quick Action Widget - springboard.buttons[" Add Widget"].tap() + springboard.buttons[" Add Widget"].waitAndTap() springboard.swipeDown() - springboard.buttons["Done"].tap() + springboard.buttons["Done"].waitAndTap() // Check available options in Quick Action Widget checkFirefoxWidgetOptions() // Tap on Edit Widget and check the available options if #unavailable(iOS 16) { - springboard.buttons["Edit Widget"].tap() + springboard.buttons["Edit Widget"].waitAndTap() } else { - springboard.buttons[editWidgetButton].tap() + springboard.buttons[editWidgetButton].waitAndTap() } mozWaitElementHittable(element: newSearch, timeout: TIMEOUT) - newSearch.tap() + newSearch.waitAndTap() if #unavailable(iOS 17) { goToCopiedLink = springboard.staticTexts["Go to Copied Link"] newPrivateSearch = springboard.staticTexts["New Private Search"] @@ -497,7 +492,7 @@ class TodayWidgetTests: BaseTestCase { XCTAssertTrue(clearPrivateTabs.exists, "Clear Private Tabs button not found.") // Tap Go To Copied Link mozWaitElementHittable(element: goToCopiedLink, timeout: TIMEOUT) - goToCopiedLink.tap() + goToCopiedLink.waitAndTap() // Tap outside the alert to close it coordinate.tap() // Copy the string to the clipboard @@ -508,12 +503,12 @@ class TodayWidgetTests: BaseTestCase { // Handle paste alert if #available(iOS 16, *) { mozWaitElementHittable(element: springboard.alerts.buttons["Allow Paste"], timeout: TIMEOUT) - springboard.alerts.buttons["Allow Paste"].tap() + springboard.alerts.buttons["Allow Paste"].waitAndTap() } // Handle iPad/iPhone UI differences if !iPad() { mozWaitElementHittable(element: app.buttons["CloseButton"], timeout: TIMEOUT) - app.buttons["CloseButton"].tap() + app.buttons["CloseButton"].waitAndTap() } // Verify the copied string is in the URL field mozWaitForElementToExist(urlBarAddress, timeout: TIMEOUT) @@ -546,11 +541,11 @@ class TodayWidgetTests: BaseTestCase { // Add Firefox Shortcut Widget springboard.swipeLeft() mozWaitForElementToExist(springboard.staticTexts["Firefox Shortcuts"]) - springboard.buttons[" Add Widget"].tap() + springboard.buttons[" Add Widget"].waitAndTap() springboard.swipeDown() - springboard.buttons["Done"].tap() + springboard.buttons["Done"].waitAndTap() checkFirefoxShortcutsOptions() - springboard.buttons.matching(NSPredicate(format: "label CONTAINS[c] %@", "Firefox")).element.tap() + springboard.buttons.matching(NSPredicate(format: "label CONTAINS[c] %@", "Firefox")).element.waitAndTap() var elementToAssert = AccessibilityIdentifiers.FirefoxHomepage.OtherButtons.privateModeToggleButton if iPad() { elementToAssert = AccessibilityIdentifiers.Browser.TopTabs.privateModeButton @@ -584,19 +579,19 @@ class TodayWidgetTests: BaseTestCase { // Add Firefox Shortcut Widget springboard.swipeLeft() mozWaitForElementToExist(springboard.staticTexts["Firefox Shortcuts"]) - springboard.buttons[" Add Widget"].tap() + springboard.buttons[" Add Widget"].waitAndTap() springboard.swipeDown() - springboard.buttons["Done"].tap() + springboard.buttons["Done"].waitAndTap() checkFirefoxShortcutsOptions() mozWaitElementHittable(element: springboard.buttons.matching( NSPredicate(format: "label CONTAINS[c] %@", "Private Tab") ).element.firstMatch, timeout: TIMEOUT) springboard.buttons.matching(NSPredicate( format: "label CONTAINS[c] %@", "Private Tab") - ).element.firstMatch.tap() + ).element.firstMatch.waitAndTap() if !iPad() { mozWaitElementHittable(element: app.buttons["CloseButton"], timeout: TIMEOUT) - app.buttons["CloseButton"].tap() + app.buttons["CloseButton"].waitAndTap() } // Verify the presence of Private Mode message mozWaitForElementToExist(app.staticTexts["Leave no traces on this device"]) @@ -635,22 +630,22 @@ class TodayWidgetTests: BaseTestCase { // Add Firefox Shortcut Widget springboard.swipeLeft() mozWaitForElementToExist(springboard.staticTexts["Firefox Shortcuts"]) - springboard.buttons[" Add Widget"].tap() + springboard.buttons[" Add Widget"].waitAndTap() springboard.swipeDown() - springboard.buttons["Done"].tap() + springboard.buttons["Done"].waitAndTap() checkFirefoxShortcutsOptions() mozWaitElementHittable(element: springboard.buttons.matching( NSPredicate(format: "label CONTAINS[c] %@", "Copied Link") ).element, timeout: TIMEOUT) springboard.buttons.matching(NSPredicate( format: "label CONTAINS[c] %@", "Copied Link") - ).element.tap() + ).element.waitAndTap() mozWaitElementHittable(element: springboard.alerts.buttons["Allow Paste"], timeout: TIMEOUT) - springboard.alerts.buttons["Allow Paste"].tap() + springboard.alerts.buttons["Allow Paste"].waitAndTap() // Verify the copied string is in the URL field if !iPad() { mozWaitElementHittable(element: app.buttons["CloseButton"], timeout: TIMEOUT) - app.buttons["CloseButton"].tap() + app.buttons["CloseButton"].waitAndTap() } mozWaitForElementToExist(urlBarAddress, timeout: TIMEOUT) mozWaitForValueContains(urlBarAddress, value: copiedString, timeout: TIMEOUT) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarMenuTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarMenuTests.swift index 250bead01ef6..c5a5582de4ed 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarMenuTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarMenuTests.swift @@ -50,7 +50,7 @@ class ToolbarMenuTests: BaseTestCase { navigator.goto(BrowserTabMenu) mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.MainMenu.HeaderView.mainButton]) validateMenuOptions() - app.buttons["MainMenu.CloseMenuButton"].tap() + app.buttons["MainMenu.CloseMenuButton"].waitAndTap() XCUIDevice.shared.orientation = .landscapeLeft waitForElementsToExist( [ @@ -70,10 +70,10 @@ class ToolbarMenuTests: BaseTestCase { hamburgerMenu.isAbove(element: firstPocketCell), "Menu button is not below the pocket cells area" ) - hamburgerMenu.tap() + hamburgerMenu.waitAndTap() mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.MainMenu.HeaderView.mainButton]) validateMenuOptions() - app.buttons["MainMenu.CloseMenuButton"].tap() + app.buttons["MainMenu.CloseMenuButton"].waitAndTap() mozWaitForElementToNotExist(app.buttons[AccessibilityIdentifiers.MainMenu.HeaderView.mainButton]) } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarTest.swift index b31ea93dc930..d00a107effae 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarTest.swift @@ -60,7 +60,7 @@ class ToolbarTests: BaseTestCase { XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Toolbar.backButton].isEnabled) XCTAssertFalse(app.buttons[AccessibilityIdentifiers.Toolbar.forwardButton].isEnabled) - app.buttons[AccessibilityIdentifiers.Toolbar.backButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.backButton].waitAndTap() XCTAssertEqual(valueMozilla, urlValueLong) waitUntilPageLoad() @@ -71,7 +71,7 @@ class ToolbarTests: BaseTestCase { waitForTabsButton() navigator.goto(TabTray) mozWaitForElementToExist(app.cells.staticTexts[website1["label"]!]) - app.cells.element(boundBy: 0).tap() + app.cells.element(boundBy: 0).waitAndTap() XCTAssertEqual(valueMozilla, urlValueLong) // Test to see if all the buttons are enabled. @@ -94,7 +94,7 @@ class ToolbarTests: BaseTestCase { XCTAssertEqual(valueMozilla, urlValueLong) // Simulate pressing on backspace key should remove the text - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() urlBarAddress.typeText("\u{8}") let value = urlBarAddress.value @@ -149,7 +149,7 @@ class ToolbarTests: BaseTestCase { if #available(iOS 16, *) { navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) navigator.performAction(Action.OpenNewTabFromTabTray) - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].tap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].waitAndTap() validateAddNewTabButtonOnToolbar(isPrivate: true) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift index 3f6d3356b8f0..6755a9b4ef4a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift @@ -55,7 +55,7 @@ class TopTabsTest: BaseTestCase { // Open link in a different tab and switch to it mozWaitForElementToExist(app.webViews.links.staticTexts["More information..."]) app.webViews.links.staticTexts["More information..."].press(forDuration: 5) - app.buttons["Open in New Tab"].tap() + app.buttons["Open in New Tab"].waitAndTap() waitUntilPageLoad() // Open tab tray to check that both tabs are there @@ -104,9 +104,9 @@ class TopTabsTest: BaseTestCase { mozWaitForElementToExist(app.cells.staticTexts[urlLabel]) // Close the tab using 'x' button if iPad() { - app.cells.buttons[StandardImageIdentifiers.Large.cross].tap() + app.cells.buttons[StandardImageIdentifiers.Large.cross].waitAndTap() } else { - app.otherElements.cells.buttons[StandardImageIdentifiers.Large.cross].tap() + app.otherElements.cells.buttons[StandardImageIdentifiers.Large.cross].waitAndTap() } // After removing only one tab it automatically goes to HomepanelView @@ -278,7 +278,7 @@ class TopTabsTest: BaseTestCase { ) // Open New Tab - app.cells.otherElements[StandardImageIdentifiers.Large.plus].tap() + app.cells.otherElements[StandardImageIdentifiers.Large.plus].waitAndTap() navigator.performAction(Action.CloseURLBarOpen) waitForTabsButton() @@ -295,7 +295,7 @@ class TopTabsTest: BaseTestCase { mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].press(forDuration: 1) mozWaitForElementToExist(app.tables.cells.otherElements[StandardImageIdentifiers.Large.plus]) - app.tables.cells.otherElements[StandardImageIdentifiers.Large.cross].tap() + app.tables.cells.otherElements[StandardImageIdentifiers.Large.cross].waitAndTap() navigator.nowAt(NewTabScreen) checkNumberOfTabsExpectedToBeOpen(expectedNumberOfTabsOpen: 1) @@ -312,7 +312,7 @@ class TopTabsTest: BaseTestCase { let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] mozWaitForElementToExist(tabsButton) navigator.nowAt(NewTabScreen) - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].tap() + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton].waitAndTap() checkNumberOfTabsExpectedToBeOpen(expectedNumberOfTabsOpen: 1) } } @@ -348,10 +348,10 @@ class TopTabsTest: BaseTestCase { let tabsTrayCell = app.otherElements["Tabs Tray"].cells // Go to a tab that is below the fold of the scrollable “Open Tabs” view if !iPad() { - tabsTrayCell.staticTexts.element(boundBy: 3).tap() + tabsTrayCell.staticTexts.element(boundBy: 3).waitAndTap() } else { XCTAssertEqual(tabsTrayCell.count, Int(numTab!)) - tabsTrayCell.staticTexts.element(boundBy: 6).tap() + tabsTrayCell.staticTexts.element(boundBy: 6).waitAndTap() } // The current tab’s thumbnail is focused in the “Open Tabs” view navigator.nowAt(NewTabScreen) @@ -382,7 +382,7 @@ class TopTabsTest: BaseTestCase { navigator.nowAt(NewTabScreen) validateToastWhenClosingMultipleTabs() // Choose to undo the action - app.buttons["Undo"].tap() + app.buttons["Undo"].waitAndTap() waitUntilPageLoad() // Only the latest tab closed is restored navigator.nowAt(BrowserTab) @@ -401,7 +401,7 @@ class TopTabsTest: BaseTestCase { navigator.performAction(Action.TogglePrivateMode) validateToastWhenClosingMultipleTabs() // Choose to undo the action - app.buttons["Undo"].tap() + app.buttons["Undo"].waitAndTap() // Only the latest tab closed is restored if !iPad() { let numTab = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].value as? String @@ -426,7 +426,7 @@ class TopTabsTest: BaseTestCase { navigator.goto(TabTray) // Close multiple tabs by pressing X button for _ in 0...3 { - app.collectionViews.cells["Homepage. Currently selected tab."].buttons["crossLarge"].tap() + app.collectionViews.cells["Homepage. Currently selected tab."].buttons["crossLarge"].waitAndTap() // A toast notification is displayed with the message "Tab Closed" and the Undo option waitForElementsToExist( [ @@ -435,7 +435,7 @@ class TopTabsTest: BaseTestCase { ] ) } - app.collectionViews.buttons["crossLarge"].tap() + app.collectionViews.buttons["crossLarge"].waitAndTap() waitForElementsToExist( [ app.buttons["Undo"], @@ -466,7 +466,7 @@ class TopTabsTest: BaseTestCase { ] ) // Choose to close the tab - app.buttons["Close Tab"].tap() + app.buttons["Close Tab"].waitAndTap() // A toast notification is displayed with the message "Tab Closed" and the Undo option waitForElementsToExist( [ @@ -474,7 +474,7 @@ class TopTabsTest: BaseTestCase { app.staticTexts["Tab Closed"] ] ) - app.buttons["Undo"].tap() + app.buttons["Undo"].waitAndTap() mozWaitForElementToNotExist(app.buttons["Undo"]) mozWaitForElementToNotExist(app.staticTexts["Tab Closed"]) // The tab closed is restored @@ -498,7 +498,7 @@ fileprivate extension BaseTestCase { } func closeTabTrayView(goBackToBrowserTab: String) { - app.cells.staticTexts[goBackToBrowserTab].firstMatch.tap() + app.cells.staticTexts[goBackToBrowserTab].firstMatch.waitAndTap() navigator.nowAt(BrowserTab) } } @@ -618,8 +618,8 @@ class TopTabsTestIpad: IpadOnlyTestCase { func testUpdateTabCounter() { if skipPlatform { return } // Open three tabs by tapping on '+' button - app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].tap() - app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() + app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) let numTab = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].value as? String XCTAssertEqual("3", numTab) @@ -628,7 +628,7 @@ class TopTabsTestIpad: IpadOnlyTestCase { .children(matching: .cell) .matching(identifier: "Homepage") .element(boundBy: 1).buttons["Remove page — Homepage"] - .tap() + .waitAndTap() waitForTabsButton() mozWaitForElementToNotExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].staticTexts["3"]) mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].staticTexts["2"]) @@ -638,7 +638,7 @@ class TopTabsTestIpad: IpadOnlyTestCase { .children(matching: .cell) .element(boundBy: 1) .buttons["Remove page — Homepage"] - .tap() + .waitAndTap() waitForTabsButton() mozWaitForElementToNotExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].staticTexts["2"]) mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].staticTexts["1"]) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift index 6a5abdf98ae4..178e3e75ebc3 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift @@ -51,7 +51,7 @@ class TrackingProtectionTests: BaseTestCase { ).label ) } - app.otherElements.element(matching: .any, identifier: reloadWithWithoutProtectionButton).tap() + app.otherElements.element(matching: .any, identifier: reloadWithWithoutProtectionButton).waitAndTap() waitUntilPageLoad() mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Browser.AddressToolbar.lockIcon], timeout: 5) if #unavailable(iOS 16) { @@ -62,8 +62,8 @@ class TrackingProtectionTests: BaseTestCase { private func enableStrictMode() { navigator.performAction(Action.EnableStrictMode) - app.buttons[buttonSettings].tap() - app.buttons[buttonDone].tap() + app.buttons[buttonSettings].waitAndTap() + app.buttons[buttonDone].waitAndTap() } func checkTrackingProtectionOn() -> Bool { @@ -142,27 +142,27 @@ class TrackingProtectionTests: BaseTestCase { var switchValue = app.switches.firstMatch.value! // Need to make sure first the setting was not turned off previously if switchValue as? String == "0" { - app.switches.firstMatch.tap() + app.switches.firstMatch.waitAndTap() } switchValue = app.switches.firstMatch.value! XCTAssertEqual(switchValue as? String, "1") - app.switches.firstMatch.tap() + app.switches.firstMatch.waitAndTap() let switchValueOFF = app.switches.firstMatch.value! XCTAssertEqual(switchValueOFF as? String, "0") // Open TP Settings menu - // app.buttons["Privacy settings"].tap() + // app.buttons["Privacy settings"].waitAndTap() // Workaround for https://github.com/mozilla-mobile/firefox-ios/issues/23706 navigator.goto(SettingsScreen) app.staticTexts["Tracking Protection"].waitAndTap() mozWaitForElementToExist(app.navigationBars["Tracking Protection"], timeout: 5) let switchSettingsValue = app.switches["prefkey.trackingprotection.normalbrowsing"].value! XCTAssertEqual(switchSettingsValue as? String, "1") - app.switches["prefkey.trackingprotection.normalbrowsing"].tap() + app.switches["prefkey.trackingprotection.normalbrowsing"].waitAndTap() // Disable ETP from setting and check that it applies to the site - app.buttons["Settings"].tap() - app.buttons["Done"].tap() + app.buttons["Settings"].waitAndTap() + app.buttons["Done"].waitAndTap() navigator.nowAt(BrowserTab) navigator.goto(TrackingProtectionContextMenuDetails) mozWaitForElementToExist(app.staticTexts["Connection not secure"], timeout: 5) @@ -174,7 +174,7 @@ class TrackingProtectionTests: BaseTestCase { navigator.nowAt(NewTabScreen) navigator.goto(TrackingProtectionSettings) // See Basic mode info - app.cells["Settings.TrackingProtectionOption.BlockListBasic"].buttons["More Info"].tap() + app.cells["Settings.TrackingProtectionOption.BlockListBasic"].buttons["More Info"].waitAndTap() waitForElementsToExist( [ app.navigationBars["Client.TPAccessoryInfo"], @@ -187,14 +187,14 @@ class TrackingProtectionTests: BaseTestCase { mozWaitForElementToNotExist(app.cells.staticTexts["Tracking content"]) // Go back to TP settings - app.buttons["Tracking Protection"].tap() + app.buttons["Tracking Protection"].waitAndTap() // See Strict mode info - app.cells["Settings.TrackingProtectionOption.BlockListStrict"].buttons["More Info"].tap() + app.cells["Settings.TrackingProtectionOption.BlockListStrict"].buttons["More Info"].waitAndTap() XCTAssertTrue(app.cells.staticTexts["Tracking content"].exists) // Go back to TP settings - app.buttons["Tracking Protection"].tap() + app.buttons["Tracking Protection"].waitAndTap() } // https://mozilla.testrail.io/index.php?/cases/view/2307061 @@ -225,8 +225,7 @@ class TrackingProtectionTests: BaseTestCase { navigator.nowAt(BrowserTab) navigator.openNewURL(urlString: "https://www.badssl.com") waitUntilPageLoad() - mozWaitForElementToExist(app.links.staticTexts["expired"]) - app.links.staticTexts["expired"].tap() + app.links.staticTexts["expired"].waitAndTap() waitUntilPageLoad() // The page is correctly displayed with the lock icon disabled mozWaitForElementToExist( diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift index 52f8e0cd922a..f1c7d94a762c 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift @@ -14,9 +14,9 @@ class URLValidationTests: BaseTestCase { super.setUp() continueAfterFailure = true navigator.goto(SearchSettings) - app.tables.switches["Show Search Suggestions"].tap() + app.tables.switches["Show Search Suggestions"].waitAndTap() scrollToElement(app.tables.switches["FirefoxSuggestShowNonSponsoredSuggestions"]) - app.tables.switches["FirefoxSuggestShowNonSponsoredSuggestions"].tap() + app.tables.switches["FirefoxSuggestShowNonSponsoredSuggestions"].waitAndTap() navigator.goto(NewTabScreen) } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/UrlBarTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/UrlBarTests.swift index 5591626b4d6a..e12ebab4b543 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/UrlBarTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/UrlBarTests.swift @@ -10,7 +10,7 @@ class UrlBarTests: BaseTestCase { // Visit any website and select the URL bar navigator.openURL("http://localhost:\(serverPort)/test-fixture/find-in-page-test.html") waitUntilPageLoad() - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() // The keyboard is brought up. XCTAssertTrue(urlBarAddress.value(forKey: "hasKeyboardFocus") as? Bool ?? false) // Scroll on the page @@ -36,12 +36,12 @@ class UrlBarTests: BaseTestCase { navigator.goto(SearchSettings) let defaultSearchEngine = app.tables.cells.element(boundBy: 0) mozWaitForElementToExist(app.tables.cells.staticTexts[defaultSearchEngine1]) - defaultSearchEngine.tap() - app.tables.staticTexts[defaultSearchEngine2].tap() + defaultSearchEngine.waitAndTap() + app.tables.staticTexts[defaultSearchEngine2].waitAndTap() mozWaitForElementToExist(app.tables.cells.staticTexts[defaultSearchEngine2]) navigator.goto(SettingsScreen) - app.navigationBars.buttons["Done"].tap() - app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].tap() + app.navigationBars.buttons["Done"].waitAndTap() + app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() tapUrlBarValidateKeyboardAndIcon() typeSearchTermAndHitGo(searchTerm: "Firefox") // The search is conducted correctly trough the default search engine @@ -51,7 +51,7 @@ class UrlBarTests: BaseTestCase { private func tapUrlBarValidateKeyboardAndIcon() { // Tap on the URL bar waitForTabsButton() - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].tap() + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].waitAndTap() // The keyboard pops up and the default search icon is correctly displayed in the URL bar XCTAssertTrue(urlBarAddress.value(forKey: "hasKeyboardFocus") as? Bool ?? false) let keyboardCount = app.keyboards.count diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ZoomingTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ZoomingTests.swift index a6a5f1c941c7..23383440f2b7 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ZoomingTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ZoomingTests.swift @@ -36,7 +36,7 @@ class ZoomingTests: BaseTestCase { navigator.nowAt(BrowserTab) navigator.goto(TabTray) if !app.buttons[AccessibilityIdentifiers.TabTray.newTabButton].exists { - app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].waitAndTap() } navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) validateZoomActions() @@ -54,7 +54,7 @@ class ZoomingTests: BaseTestCase { zoomLevel = app.buttons[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] XCTAssertEqual(zoomLevel.label, "Current Zoom Level: 175%") zoomOut() - zoomOutButton.tap() + zoomOutButton.waitAndTap() zoomLevel = app.staticTexts[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] XCTAssertEqual(zoomLevel.label, "Current Zoom Level: 100%") } @@ -66,7 +66,7 @@ class ZoomingTests: BaseTestCase { navigator.nowAt(BrowserTab) navigator.goto(TabTray) if !app.buttons[AccessibilityIdentifiers.TabTray.newTabButton].exists { - app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].tap() + app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].waitAndTap() } navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) validateZoomLevelOnSwitchingTabs() @@ -112,8 +112,7 @@ class ZoomingTests: BaseTestCase { private func selectTabTrayWebsites(tab: Int) { navigator.nowAt(BrowserTab) navigator.goto(TabTray) - mozWaitForElementToExist(app.collectionViews.staticTexts.element) - app.collectionViews.staticTexts.element(boundBy: tab).tap() + app.collectionViews.staticTexts.element(boundBy: tab).waitAndTap() waitUntilPageLoad() // Tap on the hamburger menu -> Tap on Zoom navigator.nowAt(BrowserTab) @@ -152,14 +151,14 @@ class ZoomingTests: BaseTestCase { swipeUp() swipeDown() zoomOut() - zoomOutButton.tap() + zoomOutButton.waitAndTap() zoomLevel = app.staticTexts[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] XCTAssertEqual(zoomLevel.label, "Current Zoom Level: 100%") // Switch the device orientation to landscape XCUIDevice.shared.orientation = UIDeviceOrientation.landscapeLeft zoomOutLandscape() zoomInLandscape() - zoomInButton.tap() + zoomInButton.waitAndTap() mozWaitForElementToExist(app.staticTexts[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel]) zoomLevel = app.staticTexts[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] XCTAssertEqual(zoomLevel.label, "Current Zoom Level: 100%") @@ -174,7 +173,7 @@ class ZoomingTests: BaseTestCase { for i in 0...3 { zoomLevel = app.buttons[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] let previoustTextSize = bookOfMozillaTxt.frame.size.height - zoomInButton.tap() + zoomInButton.waitAndTap() mozWaitForElementToExist(bookOfMozillaTxt) let currentTextSize = bookOfMozillaTxt.frame.size.height XCTAssertTrue(currentTextSize != previoustTextSize) @@ -186,7 +185,7 @@ class ZoomingTests: BaseTestCase { for i in 0...2 { zoomLevel = app.buttons[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] let previoustTextSize = bookOfMozillaTxt.frame.size.height - zoomOutButton.tap() + zoomOutButton.waitAndTap() mozWaitForElementToExist(bookOfMozillaTxt) let currentTextSize = bookOfMozillaTxt.frame.size.height XCTAssertTrue(currentTextSize != previoustTextSize) @@ -198,7 +197,7 @@ class ZoomingTests: BaseTestCase { for i in 0...2 { zoomLevel = app.buttons[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] let previoustTextSize = bookOfMozillaTxt.frame.size.height - zoomOutButton.tap() + zoomOutButton.waitAndTap() mozWaitForElementToExist(bookOfMozillaTxt) let currentTextSize = bookOfMozillaTxt.frame.size.height XCTAssertTrue(currentTextSize != previoustTextSize) @@ -210,7 +209,7 @@ class ZoomingTests: BaseTestCase { for i in 0...1 { zoomLevel = app.buttons[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] let previoustTextSize = bookOfMozillaTxt.frame.size.height - zoomInButton.tap() + zoomInButton.waitAndTap() mozWaitForElementToExist(bookOfMozillaTxt) let currentTextSize = bookOfMozillaTxt.frame.size.height XCTAssertTrue(currentTextSize != previoustTextSize) @@ -220,13 +219,13 @@ class ZoomingTests: BaseTestCase { private func tapZoomInButton(tapCount: Int) { for _ in 1...tapCount { - zoomInButton.tap() + zoomInButton.waitAndTap() } } private func tapZoomOutButton(tapCount: Int) { for _ in 1...tapCount { - zoomOutButton.tap() + zoomOutButton.waitAndTap() } } From 702d491328955b85fb35f781f972f13682fa7708 Mon Sep 17 00:00:00 2001 From: Ione Souza Junior Date: Mon, 13 Jan 2025 10:43:23 -0300 Subject: [PATCH 03/45] Refactor FXIOS-7301 [Swiftlint] Remove 1 closure_body_length violation from MainMenuState.swift and decrease threshold (#23991) * Decrease warning and error threshold * Extract the view did load action to a new function * Use the new function to handle view did load action * Extract the update account header action to a new function * Use the new function to handle update account header action * Extract the update current tab info action to a new function * Use the new function to handle update current tab info action * Extract the tap show details view action to a new function * Use the new function to handle tap show details view action * Extract the tap navigate to destination action to a new function * Use the new function to handle tap navigate to destination action * Extract the tap toggle user agent and close menu action to a new function * Use the new function to handle tap toggle user agent and tap close menu action * Rename function * Move guard statement into function * Move guard statement into function * Move guard statement into function * Keep the guard let declaration in just one line * Move guard statement into function * Keep the method declaration in just one line --- .swiftlint.yml | 4 +- .../MainMenu/Redux/MainMenuState.swift | 141 +++++++++++------- 2 files changed, 85 insertions(+), 60 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 077c63958112..7bdcc4d32d91 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -102,8 +102,8 @@ line_length: ignores_interpolated_strings: true closure_body_length: - warning: 71 - error: 71 + warning: 68 + error: 68 file_header: required_string: | diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuState.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuState.swift index be55a7a1eaa5..54b1216df7c7 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuState.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuState.swift @@ -126,70 +126,18 @@ struct MainMenuState: ScreenState, Equatable { switch action.actionType { case MainMenuActionType.viewDidLoad: - return MainMenuState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - currentTabInfo: state.currentTabInfo, - accountData: state.accountData, - accountIcon: state.accountIcon - ) + return handleViewDidLoadAction(state: state) case MainMenuMiddlewareActionType.updateAccountHeader: - guard let action = action as? MainMenuAction - else { return defaultState(from: state) } - - return MainMenuState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - currentTabInfo: state.currentTabInfo, - accountData: action.accountData, - accountIcon: action.accountIcon - ) + return handleUpdateAccountHeaderAction(state: state, action: action) case MainMenuActionType.updateCurrentTabInfo: - guard let action = action as? MainMenuAction, - let currentTabInfo = action.currentTabInfo - else { return defaultState(from: state) } - - return MainMenuState( - windowUUID: state.windowUUID, - menuElements: state.menuConfigurator.generateMenuElements( - with: currentTabInfo, - for: state.currentSubmenuView, - and: state.windowUUID - ), - currentTabInfo: currentTabInfo, - accountData: state.accountData, - accountIcon: state.accountIcon - ) + return handleUpdateCurrentTabInfoAction(state: state, action: action) case MainMenuActionType.tapShowDetailsView: - guard let action = action as? MainMenuAction else { return defaultState(from: state) } - return MainMenuState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - currentTabInfo: state.currentTabInfo, - submenuDestination: action.detailsViewToShow, - accountData: state.accountData, - accountIcon: state.accountIcon - ) + return handleTapShowDetailsViewAction(state: state, action: action) case MainMenuActionType.tapNavigateToDestination: - guard let action = action as? MainMenuAction else { return defaultState(from: state) } - return MainMenuState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - currentTabInfo: state.currentTabInfo, - navigationDestination: action.navigationDestination, - accountData: state.accountData, - accountIcon: state.accountIcon - ) + return handleTapNavigateToDestinationAction(state: state, action: action) case MainMenuActionType.tapToggleUserAgent, MainMenuActionType.tapCloseMenu: - return MainMenuState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - currentTabInfo: state.currentTabInfo, - shouldDismiss: true, - accountData: state.accountData, - accountIcon: state.accountIcon - ) + return handleTapToggleUserAgentAndTapCloseMenuAction(state: state) default: return defaultState(from: state) } @@ -204,4 +152,81 @@ struct MainMenuState: ScreenState, Equatable { accountIcon: state.accountIcon ) } + + private static func handleViewDidLoadAction(state: MainMenuState) -> MainMenuState { + return MainMenuState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + currentTabInfo: state.currentTabInfo, + accountData: state.accountData, + accountIcon: state.accountIcon + ) + } + + private static func handleUpdateAccountHeaderAction(state: MainMenuState, action: Action) -> MainMenuState { + guard let action = action as? MainMenuAction else { return defaultState(from: state) } + + return MainMenuState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + currentTabInfo: state.currentTabInfo, + accountData: action.accountData, + accountIcon: action.accountIcon + ) + } + + private static func handleUpdateCurrentTabInfoAction(state: MainMenuState, action: Action) -> MainMenuState { + guard let action = action as? MainMenuAction, + let currentTabInfo = action.currentTabInfo + else { return defaultState(from: state) } + + return MainMenuState( + windowUUID: state.windowUUID, + menuElements: state.menuConfigurator.generateMenuElements( + with: currentTabInfo, + for: state.currentSubmenuView, + and: state.windowUUID + ), + currentTabInfo: currentTabInfo, + accountData: state.accountData, + accountIcon: state.accountIcon + ) + } + + private static func handleTapShowDetailsViewAction(state: MainMenuState, action: Action) -> MainMenuState { + guard let action = action as? MainMenuAction else { return defaultState(from: state) } + + return MainMenuState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + currentTabInfo: state.currentTabInfo, + submenuDestination: action.detailsViewToShow, + accountData: state.accountData, + accountIcon: state.accountIcon + ) + } + + private static func handleTapNavigateToDestinationAction(state: MainMenuState, action: Action) -> MainMenuState { + guard let action = action as? MainMenuAction else { return defaultState(from: state) } + + return MainMenuState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + currentTabInfo: state.currentTabInfo, + navigationDestination: action.navigationDestination, + accountData: state.accountData, + accountIcon: state.accountIcon + ) + } + + private static func handleTapToggleUserAgentAndTapCloseMenuAction(state: MainMenuState) -> MainMenuState { + return MainMenuState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + currentTabInfo: state.currentTabInfo, + shouldDismiss: true, + accountData: state.accountData, + accountIcon: state.accountIcon + ) + } } From e47bb03bb4dd6035e61e3984e57a847394eb5854 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 09:09:37 -0600 Subject: [PATCH 04/45] Firefox Localize [v136] String import 2025-01-13 (#24096) Localize [v136] String import 2025-01-13 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- firefox-ios/Client.xcodeproj/project.pbxproj | 8 ++ .../da.lproj/Bookmarks.strings | 3 + .../da.lproj/Onboarding.strings | 9 +++ .../el.lproj/Bookmarks.strings | 3 + .../el.lproj/Onboarding.strings | 12 +++ .../el.lproj/Settings.strings | 3 + .../hy-AM.lproj/Settings.strings | 3 + .../Supporting Files/lo.lproj/Alerts.strings | 3 + .../ml.lproj/SocialShare.strings | 3 + .../EnhancedTrackingProtection.strings | 2 +- .../EnhancedTrackingProtection.strings | 12 +++ .../tt.lproj/LibraryPanel.strings | 3 + .../tt.lproj/MainMenu.strings | 75 +++++++++++++++++++ .../tt.lproj/Microsurvey.strings | 39 ++++++++++ .../tt.lproj/NativeErrorPage.strings | 3 + .../tt.lproj/Onboarding.strings | 6 ++ .../tt.lproj/Settings.strings | 39 ++++++++++ .../tt.lproj/Shopping.strings | 12 +++ .../tt.lproj/SnackBar.strings | 3 + .../tt.lproj/TabLocation.strings | 6 ++ .../Supporting Files/tt.lproj/Toolbar.strings | 9 +++ .../tt.lproj/UpdateCard.strings | 6 ++ firefox-ios/Shared/el.lproj/Menu.strings | 3 + .../Shared/ml.lproj/Localizable.strings | 22 +++++- firefox-ios/Shared/ml.lproj/Menu.strings | 2 +- 25 files changed, 285 insertions(+), 4 deletions(-) create mode 100644 firefox-ios/Shared/Supporting Files/ml.lproj/SocialShare.strings create mode 100644 firefox-ios/Shared/Supporting Files/tt.lproj/LibraryPanel.strings create mode 100644 firefox-ios/Shared/Supporting Files/tt.lproj/Microsurvey.strings create mode 100644 firefox-ios/Shared/Supporting Files/tt.lproj/NativeErrorPage.strings diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 761cb0a5307e..fe0d21e5bab2 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -3626,6 +3626,7 @@ 4320BE4729D1B24B00D0B308 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/EditCard.strings; sourceTree = ""; }; 4320BE4829D1B24B00D0B308 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/ErrorState.strings; sourceTree = ""; }; 4320BE4929D1B24B00D0B308 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Onboarding.strings; sourceTree = ""; }; + 4320DD052D352B5E00FC5C5E /* ml */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ml; path = ml.lproj/SocialShare.strings; sourceTree = ""; }; 4320E17E2A16E5A9009A4B5F /* CreditCardExtras.ios.mjs */ = {isa = PBXFileReference; lastKnownFileType = text; path = CreditCardExtras.ios.mjs; sourceTree = ""; }; 43211DCD2C3C045A00E4CA4D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/EditAddress.strings; sourceTree = ""; }; 43211DCE2C3C045A00E4CA4D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/ScanQRCode.strings; sourceTree = ""; }; @@ -3921,6 +3922,9 @@ 4335F6DC2AEFC78D00A661E9 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Shopping.strings; sourceTree = ""; }; 43360C3E2B0B770A002A8FDA /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/FirefoxHomepage.strings"; sourceTree = ""; }; 43365FED2BA85920004817F2 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/BottomSheet.strings; sourceTree = ""; }; + 433698052D352BF20093893E /* tt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tt; path = tt.lproj/LibraryPanel.strings; sourceTree = ""; }; + 433698062D352BF20093893E /* tt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tt; path = tt.lproj/Microsurvey.strings; sourceTree = ""; }; + 433698072D352BF20093893E /* tt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tt; path = tt.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43369A9D29BA6A3C007A80E3 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/EngagementNotification.strings"; sourceTree = ""; }; 43369A9E29BA6A3C007A80E3 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Onboarding.strings"; sourceTree = ""; }; 43369A9F29BA6A3C007A80E3 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/ResearchSurface.strings"; sourceTree = ""; }; @@ -19076,6 +19080,7 @@ 43DF725F2D197F8000279CAA /* ml */, 4385A43B2D1980CB0003475A /* scn */, 43D85F9C2D22B7D900E4238A /* es-MX */, + 433698052D352BF20093893E /* tt */, ); name = LibraryPanel.strings; sourceTree = ""; @@ -19404,6 +19409,7 @@ 4385A4502D1980CC0003475A /* scn */, 43944C1E2D22B6C40022CC56 /* co */, 43D85F9F2D22B7DA00E4238A /* es-MX */, + 4320DD052D352B5E00FC5C5E /* ml */, ); name = SocialShare.strings; sourceTree = ""; @@ -19767,6 +19773,7 @@ 432EF1E12D197ED0003E6E2B /* ka */, 43DF72632D197F8000279CAA /* ml */, 4385A43F2D1980CC0003475A /* scn */, + 433698072D352BF20093893E /* tt */, ); name = NativeErrorPage.strings; sourceTree = ""; @@ -21284,6 +21291,7 @@ 43DF72622D197F8000279CAA /* ml */, 4385A43E2D1980CC0003475A /* scn */, 437B39712D2BF4FE008CBAC3 /* lo */, + 433698062D352BF20093893E /* tt */, ); name = Microsurvey.strings; sourceTree = ""; diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/Bookmarks.strings b/firefox-ios/Shared/Supporting Files/da.lproj/Bookmarks.strings index e3eb01fa2ca1..87669c9e12df 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/Bookmarks.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/Bookmarks.strings @@ -7,6 +7,9 @@ /* The body text for the placeholder screen shown when there are no saved bookmarks, located at the root level of the bookmarks panel within the libray modal */ "Bookmarks.EmptyState.Root.Body.v135" = "Gem websteder, mens du browser. Vi henter også bogmærker fra andre synkroniserede enheder."; +/* The button title for the sign in button on the placeholder screen shown when there are no saved bookmarks, located at the root level of the bookmarks panel within the library modal. This button triggers the sign in flow, allowing users to sign in to their Mozilla Account to sync data */ +"Bookmarks.EmptyState.Root.ButtonTitle.v135" = "Log ind for at synkronisere"; + /* The title for the placeholder screen shown when there are no saved bookmarks, located at the root level of the bookmarks panel within the libray modal */ "Bookmarks.EmptyState.Root.Title.v135" = "Ingen bogmærker endnu"; diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/da.lproj/Onboarding.strings index 3052e45f0e66..23d63d4afb26 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/Onboarding.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/Onboarding.strings @@ -115,6 +115,9 @@ /* Agreement text for Privacy Notice in the Terms of Service screen. First placeholder is for the app name. The second placeholder is for the Privacy Notice link button that redirect the user to the Privacy Notice page */ "Onboarding.TermsOfService.PrivacyNoticeAgreement.v135" = "%1$@ passer på dit privatliv. Læs mere i vores %2$@"; +/* Title for the Privacy Notice button link, in the Terms of Service screen for redirecting the user to the Privacy Notice page. */ +"Onboarding.TermsOfService.PrivacyNoticeLink.v135" = "privatlivserklæring."; + /* A text that indicate to the user, a link button is available to be clicked for reading more information about the option that is going to choose in Manage Privacy Preferences screen, where user can choose from the option to send data to Firefox or not. */ "Onboarding.TermsOfService.PrivacyPreferences.LearnMore.v135" = "Læs mere."; @@ -127,6 +130,9 @@ /* Title for the send crash reports switch option in Manage Privacy Preferences screen, where user can choose from the option to send data to Firefox or not. */ "Onboarding.TermsOfService.PrivacyPreferences.SendCrashReportsTitle.v135" = "Send automatisk fejlrapporter"; +/* Description for the technical and interaction data switch option in Manage Privacy Preferences screen, where user can choose from the option to send data to Firefox or not. First placeholder is for the app name. The second placeholder is for the Learn more button link, to open a link where user can find more information about this send technical and interaction data option. */ +"Onboarding.TermsOfService.PrivacyPreferences.SendTechnicalDataDescription.v135" = "Data om din enhed, hardwarekonfiguration og hvordan du bruger %1$@ hjælper med at forbedre funktioner, ydeevne og stabilitet for alle. %2$@"; + /* Title for the send technical and interaction data switch option in Manage Privacy Preferences screen, where user can choose from the option to send data to Firefox or not. Placeholder will be replaced the company name of Mozilla. */ "Onboarding.TermsOfService.PrivacyPreferences.SendTechnicalDataTitle.v135" = "Send tekniske data og data om brug til %@"; @@ -139,6 +145,9 @@ /* Agreement text for Terms of Service in the Terms of Service screen. Placeholder is for the Terms of Service link button that redirect the user to the Terms of Service page */ "Onboarding.TermsOfService.TermsOfServiceAgreement.v135" = "Ved at fortsætte accepterer du %@"; +/* Title for the Terms of Service button link, in the Terms of Service screen for redirecting the user to the Terms of Service page. Placeholder is for the app name. */ +"Onboarding.TermsOfService.TermsOfServiceLink.v135" = "tjenestevilkår for %@."; + /* Title for the Terms of Service screen in the onboarding process. Placeholder is for app name. */ "Onboarding.TermsOfService.Title.v135" = "Velkommen til %@"; diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/Bookmarks.strings b/firefox-ios/Shared/Supporting Files/el.lproj/Bookmarks.strings index 421c1c203727..d2cedf9dc271 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/Bookmarks.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/Bookmarks.strings @@ -25,6 +25,9 @@ /* When a bookmark is longpressed in the bookmarks menu, an `Edit Bookmark` button is present. */ "Bookmarks.Menu.EditBookmark.v131" = "Επεξεργασία σελιδοδείκτη"; +/* When editing a bookmark, the right button in the navigation bar indicating that the edited bookmark will be saved. */ +"Bookmarks.Menu.EditBookmarkSave.v135" = "Αποθήκευση"; + /* When editing a bookmark, you can select the folder that the bookmark will be saved in. The label for this section of the view is `Save in`. */ "Bookmarks.Menu.EditBookmarkSaveIn.v131" = "Αποθήκευση σε"; diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/el.lproj/Onboarding.strings index 46dc5e360efd..ed7539d6b8f5 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/Onboarding.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/Onboarding.strings @@ -103,6 +103,18 @@ /* String used to describes the title of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. */ "Onboarding.Sync.Title.v120" = "Διατηρήστε την κρυπτογράφηση κατά την εναλλαγή των συσκευών"; +/* Title for the confirmation button for Terms of Service agreement, in the Terms of Service screen. */ +"Onboarding.TermsOfService.AgreementButtonTitle.v135" = "Αποδοχή και συνέχεια"; + +/* Title for the Manage button link, in the Terms of Service screen for redirecting the user to the Manage data collection preferences screen. */ +"Onboarding.TermsOfService.ManageLink.v135" = "Διαχείριση"; + +/* A text that indicate to the user, a link button is available to be clicked for reading more information about the option that is going to choose in Manage Privacy Preferences screen, where user can choose from the option to send data to Firefox or not. */ +"Onboarding.TermsOfService.PrivacyPreferences.LearnMore.v135" = "Μάθετε περισσότερα."; + +/* A text that indicate to the user, a link button is available to be clicked for reading more information about the option that is going to choose in Manage Privacy Preferences screen, where user can choose from the option to send data to Firefox or not. */ +"Onboarding.TermsOfService.PrivacyPreferences.LearnMore.v136" = "Μάθετε περισσότερα"; + /* Accessibility label for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that which type of wallpaper they are seeing. */ "Onboarding.Wallpaper.Accessibility.Classic.v114" = "Κλασική ταπετσαρία"; diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings index c286ae99e5fb..97622a753f66 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings @@ -73,6 +73,9 @@ /* Title for a link that explains how Mozilla send crash reports. */ "Settings.CrashReports.Link.v136" = "Μάθετε περισσότερα"; +/* On the Settings screen, this is the title text for a toggle which controls automatically sending crash reports. */ +"Settings.CrashReports.Title.v135" = "Αυτόματη αποστολή αναφορών κατάρρευσης"; + /* Title for a link that explains how Mozilla send daily usage ping. */ "Settings.DailyUsagePing.Link.v135" = "Μάθετε περισσότερα."; diff --git a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Settings.strings index 3f1f468d675f..05b621caa26f 100644 --- a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Settings.strings @@ -199,3 +199,6 @@ /* On the Settings screen, this is the title text for a toggle which controls sending technical and interaction data. */ "Settings.TechnicalData.Title.v135" = "Տեխնիկական և փոխազդեցության տվյալներ"; +/* On the Settings screen, this is the title text for a toggle which controls sending technical and interaction data. */ +"Settings.TechnicalData.Title.v136" = "Ուղարկել տեխնիկական և փոխազդեցության տվյալներ"; + diff --git a/firefox-ios/Shared/Supporting Files/lo.lproj/Alerts.strings b/firefox-ios/Shared/Supporting Files/lo.lproj/Alerts.strings index fb640ea94e3c..57456e195692 100644 --- a/firefox-ios/Shared/Supporting Files/lo.lproj/Alerts.strings +++ b/firefox-ios/Shared/Supporting Files/lo.lproj/Alerts.strings @@ -4,6 +4,9 @@ /* When tapping on a link, on a website in order to download a file and that file is a calendar file, an alert comes up asking to confirm if you want to add the event to the device calendar. This is the affirmative action for the alert, confirming that you do want to add the event to the calendar. */ "Alerts.AddToCalendar.Button.Add.v134" = "ເພີ່ມ"; +/* When tapping on a link, on a website in order to download a file and that file is a calendar file, an alert comes up asking to confirm if you want to add the event to the device calendar. This is the title for the alert. */ +"Alerts.AddToCalendar.Title.v134" = "ເພີ່ມໃສ່ປະຕິທິນບໍ?"; + /* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the body text for the alert. */ "Alerts.FeltDeletion.Body.v122" = "ປິດແຖບສ່ວນຕົວທັງໝົດ ແລະ ລຶບປະຫວັດ, ຄຸກກີ້ ແລະ ຂໍ້ມູນເວັບໄຊທ໌ອື່ນໆທັງໝົດ."; diff --git a/firefox-ios/Shared/Supporting Files/ml.lproj/SocialShare.strings b/firefox-ios/Shared/Supporting Files/ml.lproj/SocialShare.strings new file mode 100644 index 000000000000..3f733baa4aab --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ml.lproj/SocialShare.strings @@ -0,0 +1,3 @@ +/* On the Settings screen, this is the title text for a toggle which controls adding additional text to links shared to social media apps. The first parameter is the Firefox app name. The second parameter is the social media app name (e.g. WhatsApp). */ +"SentFromFirefox.SocialShare.SettingsToggle.Title.v134" = "%2$@ പങ്കിടലുകളിൽ %1$@ ഇറക്കിവയ്ക്കൽ-കണ്ണി ഉൾപ്പെടുത്തുക"; + diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/EnhancedTrackingProtection.strings index 21f258eb0850..f257347f7bdb 100644 --- a/firefox-ios/Shared/Supporting Files/ru.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/EnhancedTrackingProtection.strings @@ -38,7 +38,7 @@ "Menu.EnhancedTrackingProtection.ClearData.AlertOkButton.v128" = "Очистить"; /* The text for the clear cookies and site data alert inside the enhanced tracking protection screen. The placeholder will be replaced with the user's currently visited website */ -"Menu.EnhancedTrackingProtection.ClearData.AlertText.v128" = "Удаление кук и данных сайтов для %@ может привести к разрегистрации вас на веб-сайтах и очищению корзины покупок."; +"Menu.EnhancedTrackingProtection.ClearData.AlertText.v128" = "Удаление кук и данных сайтов для %@ может привести к выходу из аккаунта на веб-сайтах и очищению корзины покупок."; /* The title for the clear cookies and site data alert inside the enhanced tracking protection screen. */ "Menu.EnhancedTrackingProtection.ClearData.AlertTitle.v128" = "Удалить куки и данные сайтов"; diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/EnhancedTrackingProtection.strings index d4bc67b74b28..819de9b2f5b6 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/EnhancedTrackingProtection.strings @@ -79,3 +79,15 @@ /* The title for the button that allows users to view certificates inside the enhanced tracking protection details screen. */ "Menu.EnhancedTrackingProtection.Details.ViewCertificatesTitle.v131" = "Сертификатны карау"; +/* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Бу сайтта сак булыгыз"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected. The placeholder will have the value of the app name */ +"Menu.EnhancedTrackingProtection.On.Title.v128" = "%@ сакта тора"; + +/* The title for the privacy settings button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.PrivacySettings.Title.v128" = "Хосусыйлык көйләүләре"; + +/* Title for the switch to enable/disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.Switch.Title.v128" = "Күзәтелүдән Көчәйтелгән Саклау"; + diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/LibraryPanel.strings new file mode 100644 index 000000000000..d541c2a44d40 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Соңгы сәгать"; + diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/MainMenu.strings index 760c55535a39..b2f7ea2e3ddc 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/MainMenu.strings @@ -1,3 +1,51 @@ +/* The accessibility label for the back button in the Main menu header navigation view. */ +"MainMenu.Account.AccessibilityLabels.BackButton.v132" = "Кире"; + +/* The accessibility label for the close button in the Main menu. */ +"MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "Ябу"; + +/* On the main menu, at the top, when the user is signed out. The title for the sign in action */ +"MainMenu.Account.SignedOut.Title.v131" = "Керү"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The description subtitle for the sync error state. */ +"MainMenu.Account.SyncError.Description.v131" = "Синхронлау туктатылды"; + +/* On the main menu, the accessibility label for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Bookmarks.v132" = "Кыстыргычлар"; + +/* On the main menu, the accessibility label for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Downloads.v132" = "Иңдерүләр"; + +/* On the main menu, the accessibility label for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.History.v132" = "Тарих"; + +/* On the main menu, the accessibility label for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Passwords.v132" = "Серсүзләр"; + +/* On the main menu, the title for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.Bookmarks.Title.v131" = "Кыстыргычлар"; + +/* On the main menu, the title for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.Downloads.Title.v131" = "Иңдерүләр"; + +/* On the main menu, the title for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.History.Title.v131" = "Тарих"; + +/* On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.Passwords.Title.v131" = "Серсүзләр"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Customize Homepage section in the settings screen. */ +"MainMenu.SettingsSection.AccessibilityLabels.CustomizeHomepage.v132" = "Баш битне көйләү"; + +/* On the main menu, the accessibility labels for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.AccessibilityLabels.GetHelp.v132" = "Ярдәм алу"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "Көйләүләр"; + +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "%@ яңалыклары"; + /* On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen. */ "MainMenu.SettingsSection.CustomizeHomepage.Title.v131" = "Баш битне көйләү"; @@ -148,3 +196,30 @@ /* On the main menu, the title for the action that will turn the reader view on for the current website. */ "MainMenu.Submenus.Tools.ReaderView.Off.Title.v131" = "Уку режимын сүндерү"; +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.On.Title.v131" = "Уку режимын кабызу"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.ReaderView.Subtitle.v131" = "Уку режимы"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Subtitle.v131" = "Шикаять итү"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Share tool. */ +"MainMenu.Submenus.Tools.Share.Subtitle.v131" = "Уртаклашу"; + +/* On the main menu, the title for the action that will take the user to the Share module in the application. */ +"MainMenu.Submenus.Tools.Share.Title.v131" = "Уртаклашу"; + +/* On the main menu, a string below the Tool submenu title, indicating what kind of tools are available in that menu. This string is for the Zoom tool. */ +"MainMenu.Submenus.Tools.Zoom.Subtitle.v131" = "Масштаб"; + +/* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.Zoom.Title.v131" = "Масштаб (%@)"; + +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "Төп меню"; + +/* On the main menu, the accessibility label for the action that will create a new private tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "Яңа хосусый таб"; + diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/Microsurvey.strings new file mode 100644 index 000000000000..ddd9b8455958 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/Microsurvey.strings @@ -0,0 +1,39 @@ +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the title for the header on the microsurvey when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.HeaderLabel.v127" = "Сораулык тәмамланды"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "Зинһар, сораулыкны тәмамлагыз"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "%@ логотибы"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Dissatisfied.v132" = "Начар"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Neutral.v132" = "Битараф"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.NotApplicable.v132" = "Мин аны кулланмыйм"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Satisfied.v132" = "Яхшы"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VeryDissatisfied.v132" = "Бик начар"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VerySatisfied.v132" = "Бик яхшы"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ +"Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%2$@ эченнән %1$@"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of a link on the survey and allows the user to navigate to our privacy policy details. */ +"Microsurvey.Survey.PrivacyPolicyLink.v127" = "Хосусыйлык аңлатмасы"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ +"Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Сайланмаган"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Сораулык"; + diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..fdbc62b08a5d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/NativeErrorPage.strings @@ -0,0 +1,3 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Яңарту"; + diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/Onboarding.strings index 7a1cb0251a2e..1f082c1fda08 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/Onboarding.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/Onboarding.strings @@ -34,6 +34,9 @@ /* String used to describe the option to skip the theme customization in Firefox Onboarding screens. */ "Onboarding.Customization.Theme.Skip.Action.v123" = "Калдырып тору"; +/* String used to describe the title of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Title.v123" = "Тема сайлагыз"; + /* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the bottom of the screen. */ "Onboarding.Customization.Toolbar.Bottom.Action.v123" = "Аста"; @@ -88,6 +91,9 @@ /* A text that indicate to the user, a link button is available to be clicked for reading more information about the option that is going to choose in Manage Privacy Preferences screen, where user can choose from the option to send data to Firefox or not. */ "Onboarding.TermsOfService.PrivacyPreferences.LearnMore.v136" = "Күбрәк белү"; +/* Title for the Terms of Service button link, in the Terms of Service screen for redirecting the user to the Terms of Service page. Placeholder is for the app name. */ +"Onboarding.TermsOfService.TermsOfServiceLink.v135" = "%@ Куллану Шартлары."; + /* Title for the Terms of Service screen in the onboarding process. Placeholder is for app name. */ "Onboarding.TermsOfService.Title.v135" = "%@ кушымтасына рәхим итегез"; diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/Settings.strings index 1f61306159bf..021e5b7dc148 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/Settings.strings @@ -19,18 +19,51 @@ /* Description label for when there are no credit cards shown in credit card list in autofill settings screen. */ "CreditCard.Settings.EmptyListDescription.v112" = "Киләчәктә тизрәк түләү өчен картагызның язуларын хәвефсез саклагыз."; +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string indicates to users that they can deny Firefox from remembering the card that is being used. */ +"CreditCard.Settings.NotNow.v122" = "Хәзер түгел"; + +/* When a user is in the process or has finished making a purchase with a remembered card, and if the credit card information doesn't match the contents of the stored information of that card, we show this string. We ask this user if they would like Firefox update the staled information of that credit card. */ +"CreditCard.Settings.UpdateThisCard.v122" = "Картаны яңарту кирәкме?"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string asks users to confirm if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.Yes.v122" = "Яңарту"; + /* When a user is in the process of making a purchase and has at least one saved credit card, a view above the keyboard shows actions a user can take. When tapping this label, the keyboard will dismiss from view. */ "CreditCards.Settings.Done.v114" = "Әзер"; /* When a user is in the process or has finished making a purchase, and has at least one card saved, we show this tappable string. This indicates to users that they can navigate to their list of stored credit cards in the app's credit card list screen. */ "CreditCards.Settings.ManageCards.v112" = "Карталар белән идарә итү"; +/* When a user is in the process of making a purchase, and has at least one saved card, we show this label used as a title. This indicates to the user that there are stored cards available for use on this pending purchase. */ +"CreditCards.Settings.UseASavedCard.v122" = "Сакланган картаны куллану"; + /* When a user is in the process of making a purchase, and has at least one saved card, we show this label inside the keyboard hint. This indicates to the user that there are stored cards available for use on this pending purchase. */ "CreditCards.Settings.UseSavedCardFromKeyboard.v112" = "Сакланган картаны куллану"; /* Settings section title for the old Firefox account */ "FxA.FirefoxAccount.v119" = "Хисап язмасы"; +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v126" = "Адреслар"; + +/* Label used as an item in Settings screen. When touched, it will take user to credit card settings page to that will allows to add or modify saved credit cards to allow for autofill in a webpage. */ +"Settings.AutofillCreditCard.Title.v122" = "Түләү ысуллары"; + +/* Title for a link that explains how Mozilla send crash reports. */ +"Settings.CrashReports.Link.v135" = "Күбрәк белү."; + +/* Title for a link that explains how Mozilla send crash reports. */ +"Settings.CrashReports.Link.v136" = "Күбрәк белү"; + +/* On the Settings screen, this is the title text for a toggle which controls automatically sending crash reports. */ +"Settings.CrashReports.Title.v135" = "Ватылу турындагы хәбәрләрне автоматик рәвештә җибәрү"; + +/* Title for a link that explains how Mozilla send daily usage ping. */ +"Settings.DailyUsagePing.Link.v135" = "Күбрәк белү."; + +/* Title for a link that explains how Mozilla send daily usage ping. */ +"Settings.DailyUsagePing.Link.v136" = "Күбрәк белү"; + /* Title displayed in header of the FxA settings panel. */ "Settings.FxA.Title.v119" = "Хисап язмасы"; @@ -61,6 +94,12 @@ /* Accessibility label for default search engine setting. */ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Стандарт эзләү системасы"; +/* Accessibility label for Learn more about Firefox Suggest. */ +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Firefox Suggest турында күбрәк белү"; + +/* Title for alternate search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Альтернатив эзләү системалары"; + /* Title for the `default search engine` settings section in the Search page in the Settings menu. */ "Settings.Search.DefaultSearchEngine.Title.v121" = "Стандарт эзләү системасы"; diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/Shopping.strings index 3da9a5b94373..ed6e07245d47 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/Shopping.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/Shopping.strings @@ -67,6 +67,9 @@ /* This title is displayed on the information card as a confirmation message after a user reports that a previously out-of-stock product is now available. It's meant to acknowledge the user's contribution and encourage community engagement by letting them know their report has been successfully submitted. */ "Shopping.InfoCard.ReportSubmittedByCurrentUser.Title.v121" = "Белдерүегез өчен рәхмәт!"; +/* Text for the analyzer button displayed when an analysis can be updated for a product. */ +"Shopping.NoAnalysisCard.AnalyzerButton.Title.v120" = "Бәяләмә сыйфатын тикшерү"; + /* Label for the Learn more button in the Shopping Experience Opt In onboarding Card (Fakespot) */ "Shopping.OptInCard.LearnMoreButtonTitle.Title.v120" = "Күбрәк белү"; @@ -79,6 +82,9 @@ /* Text for the secondary button of the Shopping Experience Opt In onboarding Card (Fakespot) */ "Shopping.OptInCard.SecondaryButton.Title.v120" = "Хәзер түгел"; +/* Show Fakespot Terms of Use page in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Fakespot name. */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v123" = "%@-ның куллану шартлары"; + /* Accessibility label for the Grade labels used in 'How we determine review quality' card and 'How reliable are these reviews' card displayed in the shopping review quality bottom sheet. The placeholder will be replaced by a grade letter (e.g. A). The grading system contains letters from A-F. */ "Shopping.ReliabilityScore.Grade.A11y.Label.v120" = "Билге %@"; @@ -91,6 +97,12 @@ /* Description of the reliability ratings for rating 'D' and 'F' displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQuality.ReliabilityRating.DF.Description.v120" = "Ышанычсыз бәяләмәләр"; +/* Accessibility label for the up chevron icon used to collapse or minimize the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Collapse.AccessibilityLabel.v120" = "\"Көйләүләр\" картасын төрү"; + +/* Accessibility label for the down chevron icon used to expand or show the details of the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Expand.AccessibilityLabel.v120" = "\"Көйләүләр\" картасын җәю"; + /* Title of the settings card displayed in the shopping review quality bottom sheet. */ "Shopping.SettingsCard.Label.Title.v120" = "Көйләүләр"; diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/SnackBar.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/SnackBar.strings index a27a7e173bd6..2670306d8446 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/SnackBar.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/SnackBar.strings @@ -4,3 +4,6 @@ /* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets saved successfully */ "CreditCard.SnackBar.SavedCardLabel.v112" = "Яңа карта сакланды"; +/* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets updated successfully */ +"CreditCard.SnackBar.UpdatedCardLabel.v122" = "Кредит картасы турындагы мәгълүмат яңартылды"; + diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/TabLocation.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/TabLocation.strings index 5c69fefd7d51..057ea9dc6247 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/TabLocation.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/TabLocation.strings @@ -1,6 +1,12 @@ /* Accessibility hint for the reload button */ "Address.Bar.Reload.A11y.Hint.v124" = "Күбрәк опцияләр өчен ике тапкыр басып, тотып торыгыз"; +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.NotSecure.A11y.Label.v119" = "Бәйләнеш хәвефсез түгел. Күзәтелүдән көчәйтелгән саклау сүнгән."; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.Secure.A11y.Label.v119" = "Хәвефсез бәйләнеш. Күзәтелүдән көчәйтелгән саклау сүнгән."; + /* Accessibility label for the security icon in url bar */ "TabLocation.ETP.On.NotSecure.A11y.Label.v119" = "Бәйләнеш хәвефсез түгел"; diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/Toolbar.strings index 53cee93a633b..d8cf9fa2cd78 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/Toolbar.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/Toolbar.strings @@ -1,3 +1,12 @@ +/* Accessibility label for the Main Menu button in the toolbar, specifing that the button will open Main Menu */ +"Toolbar.Menu.Button.A11y.Label.v135" = "Төп меню"; + /* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ "Toolbar.NewTab.Button.v130" = "Яңа таб"; +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Бу табны ябу"; + +/* Accessibility label for the tabs button in the toolbar, specifing the number of tabs open. */ +"Toolbar.Tabs.Button.A11y.Label.v135" = "Ачык таблар"; + diff --git a/firefox-ios/Shared/Supporting Files/tt.lproj/UpdateCard.strings b/firefox-ios/Shared/Supporting Files/tt.lproj/UpdateCard.strings index 904cfddc72ef..be31875a9e0e 100644 --- a/firefox-ios/Shared/Supporting Files/tt.lproj/UpdateCard.strings +++ b/firefox-ios/Shared/Supporting Files/tt.lproj/UpdateCard.strings @@ -1,9 +1,15 @@ /* This value is used as the toast message for the saving success alert in the remember credit card page */ "CreditCard.RememberCard.SecondaryButtonTitle.v116" = "Кредит картасы турындагы мәгълүмат яңартылды"; +/* This value is used as the title for the update card page */ +"CreditCard.UpdateCard.MainTitle.v122" = "Картаны яңарту кирәкме?"; + /* This value is used as the title for the Manage Cards button from the update credit card page */ "CreditCard.UpdateCard.ManageCardsButtonTitle.v115" = "Карталар белән идарә итү"; /* This value is used as the title for the Not Now button in the update credit card page */ "CreditCard.UpdateCard.NotNowButtonTitle.v115" = "Хәзер түгел"; +/* This value is used as the title for the button in the update credit card page. It indicates the action to update the details f9 the card. */ +"CreditCard.UpdateCard.YesButtonTitle.v122" = "Яңарту"; + diff --git a/firefox-ios/Shared/el.lproj/Menu.strings b/firefox-ios/Shared/el.lproj/Menu.strings index bdffed51d53d..9ce5914ff791 100644 --- a/firefox-ios/Shared/el.lproj/Menu.strings +++ b/firefox-ios/Shared/el.lproj/Menu.strings @@ -10,6 +10,9 @@ /* Label for the button, displayed in the menu, downloads a pdf when pressed. */ "Menu.DownloadPDF.Label.v129" = "Λήψη PDF"; +/* Label for the edit bookmark button in the legacy menu. Pressing this button opens the bookmark editing screen for the current page's bookmark. Please keep the text as short as possible for this label. */ +"Menu.EditBookmark.Label.v135" = "Επεξεργασία"; + /* Label for the button, displayed in the menu, used to open the toolbar to search for text within the current page. */ "Menu.FindInPageAction.Title" = "Εύρεση στη σελίδα"; diff --git a/firefox-ios/Shared/ml.lproj/Localizable.strings b/firefox-ios/Shared/ml.lproj/Localizable.strings index 4e16aea5df03..f9f0af1b50a5 100644 --- a/firefox-ios/Shared/ml.lproj/Localizable.strings +++ b/firefox-ios/Shared/ml.lproj/Localizable.strings @@ -251,7 +251,7 @@ "Done" = "സമ്പൂര്‍ണം"; /* Panel accessibility label */ -"Downloads" = "ഡൌണ്‍ലോഡുകള്‍"; +"Downloads" = "ഇറക്കിവയ്ക്കലുകൾ"; /* The label of the button the user will press to start downloading a file */ "Downloads.Alert.DownloadNow" = "ഇപ്പോള്‍ ഇറക്കിവയ്ക്കുക"; @@ -260,7 +260,7 @@ "Downloads.CancelDialog.Cancel" = "റദ്ദാക്കുക"; /* Alert dialog body when the user taps the cancel download icon. */ -"Downloads.CancelDialog.Message" = "ഈ ഡൗണ്‍ലോഡ് റദ്ദാക്കണമെന്ന് ഉറപ്പാ​ണോ?"; +"Downloads.CancelDialog.Message" = "ഈ ഇറക്കിവയ്ക്കൽ റദ്ദാക്കണമെന്ന് ഉറപ്പാ​ണോ?"; /* Button declining the cancellation of the download. */ "Downloads.CancelDialog.Resume" = "തുടരുക"; @@ -1285,9 +1285,27 @@ /* In the settings menu, on the Firefox homepage customization section, this is the title for the option that allows users to access the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper" = "ചുവർകടലാസു്"; +/* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "ഫയർഫോക്സു് ചുവർക്കടലാസു്, കുപ്പിക്കല്ലു് രചന."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "ഫയർഫോക്സു് ചുവർക്കടലാസു്, കടല്ത്തീരക്കുന്നുകളുള്ള രചന."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "ഫയർഫോക്സു് ചുവർക്കടലാസു്, ആകാശനീല രചന."; + /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "തനതു തെളിഞ്ഞ ചുവർകടലാസു്."; +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "ഫയർഫോക്സു് ചുവർക്കടലാസു്, കിഴക്കരചന."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "ഫയർഫോക്സു് ചുവർക്കടലാസു്, സന്ധ്യക്കുന്നരചന."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ +"Settings.Home.Option.Wallpaper.Classic.Title.v106" = "ക്ലാസിക് %@"; + /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the section that allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.CollectionTitle" = "ആദ്യ തിര"; diff --git a/firefox-ios/Shared/ml.lproj/Menu.strings b/firefox-ios/Shared/ml.lproj/Menu.strings index d8e2da70d47c..ff35c5792d9a 100644 --- a/firefox-ios/Shared/ml.lproj/Menu.strings +++ b/firefox-ios/Shared/ml.lproj/Menu.strings @@ -26,7 +26,7 @@ "Menu.OpenBookmarksAction.AccessibilityLabel.v2" = "അടയാളക്കുറിപ്പുകള്‍"; /* Accessibility label for the button, displayed in the menu, used to open the Downloads home panel. Please keep as short as possible, <15 chars of space available. */ -"Menu.OpenDownloadsAction.AccessibilityLabel.v2" = "ഡൗണ്‍ലോഡുകള്‍"; +"Menu.OpenDownloadsAction.AccessibilityLabel.v2" = "ഇറക്കിവയ്ക്കലുകൾ"; /* Accessibility label for the button, displayed in the menu, used to open the History home panel. Please keep as short as possible, <15 chars of space available. */ "Menu.OpenHistoryAction.AccessibilityLabel.v2" = "നാള്‍വഴി"; From 199c0037a5cdf660761978180319b029e40a8c1e Mon Sep 17 00:00:00 2001 From: Litianu Razvan Date: Mon, 13 Jan 2025 17:59:55 +0200 Subject: [PATCH 05/45] Add FXIOS-10683 Focus iOS: Add new setting to control crash reporting (#23812) * Add crash toggle * format * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin * Update focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift Co-authored-by: Nishant Bhasin --------- Co-authored-by: Nishant Bhasin --- focus-ios/Blockzilla/AppDelegate.swift | 2 +- .../Controller/SettingsViewController.swift | 63 ++++++++++++++----- .../Blockzilla/Utilities/SupportUtils.swift | 3 + focus-ios/Shared/Settings.swift | 2 + 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/focus-ios/Blockzilla/AppDelegate.swift b/focus-ios/Blockzilla/AppDelegate.swift index e64fa6693e7d..f594cb3b5437 100644 --- a/focus-ios/Blockzilla/AppDelegate.swift +++ b/focus-ios/Blockzilla/AppDelegate.swift @@ -327,7 +327,7 @@ private let SentryDSNKey = "SentryDSN" extension AppDelegate { func setupCrashReporting() { // Do not enable crash reporting if collection of anonymous usage data is disabled. - if !Settings.getToggle(.sendAnonymousUsageData) { + if !Settings.getToggle(.crashToggle) { return } diff --git a/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift b/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift index 61d4098db304..fd6e8c3db8cf 100644 --- a/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift +++ b/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift @@ -14,7 +14,7 @@ import DesignSystem class SettingsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { enum Section: String { - case defaultBrowser, general, privacy, usageData, studies, search, siri, integration, mozilla, secret + case defaultBrowser, general, privacy, usageData, crashReports, studies, search, siri, integration, mozilla, secret var headerText: String? { switch self { @@ -28,14 +28,33 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi case .integration: return UIConstants.strings.toggleSectionSafari case .mozilla: return UIConstants.strings.toggleSectionMozilla case .secret: return nil + case .crashReports: return nil } } static func getSections() -> [Section] { - var sections = [.defaultBrowser, .general, .privacy, .usageData, .studies, .search, .siri, integration, .mozilla] + var sections: [Section] = [ + .defaultBrowser, + .general, + .privacy, + .usageData, + .studies, + .crashReports + ] + + // FXIOS-10900 Add the TOS section to the list of sections + + sections.append(contentsOf: [ + .search, + .siri, + integration, + .mozilla + ]) + if Settings.getToggle(.displaySecretMenu) { sections.append(.secret) } + return sections } } @@ -106,6 +125,11 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi let studiesToggle = BlockerToggle(label: UIConstants.strings.labelStudies, setting: SettingsToggle.studies, subtitle: studiesSubtitle) let usageDataSubtitle = String(format: UIConstants.strings.detailTextSendUsageData, AppInfo.productName) let usageDataToggle = BlockerToggle(label: UIConstants.strings.labelSendAnonymousUsageData, setting: SettingsToggle.sendAnonymousUsageData, subtitle: usageDataSubtitle) + let crashToggle = BlockerToggle( + label: UIConstants.strings.labelCrashReports, + setting: SettingsToggle.crashToggle, + subtitle: UIConstants.strings.detailTextCrashReports + ) let searchSuggestionSubtitle = String(format: UIConstants.strings.detailTextSearchSuggestion, AppInfo.productName) let searchSuggestionToggle = BlockerToggle(label: UIConstants.strings.settingsSearchSuggestions, setting: SettingsToggle.enableSearchSuggestions, subtitle: searchSuggestionSubtitle) let safariToggle = BlockerToggle(label: UIConstants.strings.toggleSafari, setting: SettingsToggle.safari) @@ -124,6 +148,9 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi if let studiesIndex = getSectionIndex(Section.studies) { toggles[studiesIndex] = [0: studiesToggle] } + if let crashIndex = getSectionIndex(.crashReports) { + toggles[crashIndex] = [0: crashToggle] + } if let searchIndex = getSectionIndex(Section.search) { toggles[searchIndex] = [2: searchSuggestionToggle] } @@ -355,6 +382,8 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi case .secret: cell = SettingsTableViewCell(style: .subtitle, reuseIdentifier: "secretSettingsCell") cell.textLabel?.text = "Internal Settings" + case .crashReports: + cell = setupToggleCell(indexPath: indexPath, navigationController: navigationController) } cell.textLabel?.textColor = .primaryText @@ -378,6 +407,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi case .integration: return 1 case .mozilla: return 3 case .secret: return 1 + case .crashReports: return 1 } } @@ -394,34 +424,32 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + // If a toggle subtitle exists, create a standard footer with optional learn more actions if let text = toggles[section]?.first?.value.subtitle { let footer = ActionFooterView(frame: .zero) footer.textLabel.text = text - - if section == getSectionIndex(.usageData) || section == getSectionIndex(.studies) || section == getSectionIndex(.search) { - var selector: Selector? - if section == getSectionIndex(.usageData) { - selector = #selector(tappedLearnMoreFooter) - } else if section == getSectionIndex(.search) { - selector = #selector(tappedLearnMoreSearchSuggestionsFooter) - } else if section == getSectionIndex(.studies) { - selector = #selector(tappedLearnMoreStudies) - } - + let learnMoreActions: [Int?: Selector] = [ + getSectionIndex(.usageData): #selector(tappedLearnMoreFooter), + getSectionIndex(.search): #selector(tappedLearnMoreSearchSuggestionsFooter), + getSectionIndex(.studies): #selector(tappedLearnMoreStudies), + getSectionIndex(.crashReports): #selector(tappedLearnMoreCrashReports) + ] + if let selector = learnMoreActions[section] { let tapGesture = UITapGestureRecognizer(target: self, action: selector) footer.detailTextButton.setTitle(UIConstants.strings.learnMore, for: .normal) footer.detailTextButton.addGestureRecognizer(tapGesture) } return footer - } else if section == getSectionIndex(.defaultBrowser) { + } + if section == getSectionIndex(.defaultBrowser) { let footer = ActionFooterView(frame: .zero) footer.textLabel.text = String(format: UIConstants.strings.setAsDefaultBrowserDescriptionLabel, AppInfo.productName) return footer - } else { - return nil } + return nil } + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return sections[section] == .privacy ? 50 : 30 } @@ -517,6 +545,9 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi func tappedLearnMoreStudies(gestureRecognizer: UIGestureRecognizer) { tappedFooter(forSupportTopic: .studies) } + @objc func tappedLearnMoreCrashReports() { + tappedFooter(forSupportTopic: .mobileCrashReports) + } @objc private func dismissSettings() { diff --git a/focus-ios/Blockzilla/Utilities/SupportUtils.swift b/focus-ios/Blockzilla/Utilities/SupportUtils.swift index 8760ae09353f..64b4bbd9d1e0 100644 --- a/focus-ios/Blockzilla/Utilities/SupportUtils.swift +++ b/focus-ios/Blockzilla/Utilities/SupportUtils.swift @@ -11,6 +11,7 @@ public enum SupportTopic: CaseIterable { case autofillDomain case trackingProtection case addSearchEngine + case mobileCrashReports public var slug: String { switch self { @@ -26,6 +27,8 @@ public enum SupportTopic: CaseIterable { return "tracking-protection-focus-ios" case .addSearchEngine: return "add-search-engine-ios" + case .mobileCrashReports: + return "mobile-crash-reports" } } diff --git a/focus-ios/Shared/Settings.swift b/focus-ios/Shared/Settings.swift index 01d00b3fe546..8d13180d07b3 100644 --- a/focus-ios/Shared/Settings.swift +++ b/focus-ios/Shared/Settings.swift @@ -16,6 +16,7 @@ enum SettingsToggle: String, Equatable { case safari = "Safari" case sendAnonymousUsageData = "SendAnonymousUsageData" case studies = "Studies" + case crashToggle = "CrashToggle" case enableDomainAutocomplete = "enableDomainAutocomplete" case enableCustomDomainAutocomplete = "enableCustomDomainAutocomplete" case enableSearchSuggestions = "enableSearchSuggestions" @@ -64,6 +65,7 @@ struct Settings { case .enableCustomDomainAutocomplete: return true case .enableSearchSuggestions: return false case .displaySecretMenu: return false + case .crashToggle: return true } } From 40026054c2c12892a982087fa6fc10ccd1689eca Mon Sep 17 00:00:00 2001 From: Isabella Date: Mon, 13 Jan 2025 13:45:09 -0600 Subject: [PATCH 06/45] Bugfix FXIOS-11019 [Sent from Firefox] WhatsApp Shares should not append webpage title to the end (#24104) * Add exception never to share the title of a webpage to WhatsApp when Sent from Firefox is enabled. * Fix existing unit tests. Add additional unit tests. Fix unit test membership for TitleActivityItemProviderTests.swift. --- firefox-ios/Client.xcodeproj/project.pbxproj | 2 + .../Client/Frontend/Share/ShareManager.swift | 7 +- .../Share/TitleActivityItemProvider.swift | 11 ++- .../Sharing/ShareManagerTests.swift | 72 +++++++++++++++---- .../TitleActivityItemProviderTests.swift | 36 ++++++++++ 5 files changed, 113 insertions(+), 15 deletions(-) diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index fe0d21e5bab2..76cc1d18fe2f 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -1871,6 +1871,7 @@ ED67B2962D0C921600BDC599 /* ShareTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED67B2952D0C921600BDC599 /* ShareTelemetry.swift */; }; ED67B2982D0C940C00BDC599 /* ShareTelemetryActivityItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED67B2972D0C940C00BDC599 /* ShareTelemetryActivityItemProvider.swift */; }; ED6C8DAC2CE6A4BB00D7F7F3 /* Sentry-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = ED6C8DAB2CE6A4BB00D7F7F3 /* Sentry-Dynamic */; }; + ED6D46E02D3573F80045E4ED /* TitleActivityItemProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED67B2932D0B80E200BDC599 /* TitleActivityItemProviderTests.swift */; }; ED70CD812CE3BD2C0018761B /* MockStoreForMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED70CD802CE3BD2C0018761B /* MockStoreForMiddleware.swift */; }; ED7A08DB2CF674730035EC8F /* ShareMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED7A08DA2CF674730035EC8F /* ShareMessage.swift */; }; ED7A08DD2CF6749B0035EC8F /* ShareType.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED7A08DC2CF6749B0035EC8F /* ShareType.swift */; }; @@ -17327,6 +17328,7 @@ 8A6B799B2CDBCF3D003C3077 /* TopSitesManagerTests.swift in Sources */, 21737FB72878A4BD000A9A92 /* HistoryPanelViewModelTests.swift in Sources */, 43B658D929CE251C00C9EF08 /* CreditCardInputViewModelTests.swift in Sources */, + ED6D46E02D3573F80045E4ED /* TitleActivityItemProviderTests.swift in Sources */, C8501F5128510DA1003B09AB /* WallpaperMigrationUtilityTests.swift in Sources */, 5A70EF1D295E3C3500790249 /* TestSetup.swift in Sources */, E1442FDA294782F7003680B0 /* UIPasteboard+Extension.swift in Sources */, diff --git a/firefox-ios/Client/Frontend/Share/ShareManager.swift b/firefox-ios/Client/Frontend/Share/ShareManager.swift index 538d1a255820..daff903be9cf 100644 --- a/firefox-ios/Client/Frontend/Share/ShareManager.swift +++ b/firefox-ios/Client/Frontend/Share/ShareManager.swift @@ -126,7 +126,12 @@ class ShareManager: NSObject, FeatureFlaggable { } else { // For feature parity with Safari, we use this provider to decide to which apps we should (or should not) // share a display title and/or subject line - activityItems.append(TitleActivityItemProvider(title: tab.displayTitle)) + activityItems.append( + TitleActivityItemProvider( + title: tab.displayTitle, + applySentFromFirefoxTreatment: isSentFromFirefoxEnabled + ) + ) } } diff --git a/firefox-ios/Client/Frontend/Share/TitleActivityItemProvider.swift b/firefox-ios/Client/Frontend/Share/TitleActivityItemProvider.swift index 8278c3729693..3d879e0dc762 100644 --- a/firefox-ios/Client/Frontend/Share/TitleActivityItemProvider.swift +++ b/firefox-ios/Client/Frontend/Share/TitleActivityItemProvider.swift @@ -16,7 +16,12 @@ import Foundation /// Note that not all applications use the Subject. For example OmniFocus ignores it, so we need to do both. class TitleActivityItemProvider: UIActivityItemProvider, @unchecked Sendable { + private struct ActivityIdentifiers { + static let whatsApp = "net.whatsapp.WhatsApp.ShareExtension" + } + private let title: String + private let applySentFromFirefoxTreatment: Bool // FXIOS-9879 For the Sent from Firefox experiment /// We do not want to append titles to website URL shares to the pasteboard, Messages, and Mail body. /// However, this provider will append the title to the Mail subject line. @@ -26,8 +31,9 @@ class TitleActivityItemProvider: UIActivityItemProvider, @unchecked Sendable { UIActivity.ActivityType.mail ] - init(title: String) { + init(title: String, applySentFromFirefoxTreatment: Bool = false) { self.title = title + self.applySentFromFirefoxTreatment = applySentFromFirefoxTreatment super.init(placeholderItem: title) } @@ -39,6 +45,9 @@ class TitleActivityItemProvider: UIActivityItemProvider, @unchecked Sendable { // For excluded activites, we don't want to provide any content if let activityType = activityType, TitleActivityItemProvider.activityTypesToIgnore.contains(activityType) { return NSNull() + } else if applySentFromFirefoxTreatment, activityType?.rawValue == ActivityIdentifiers.whatsApp { + // FXIOS-9879 For the Sent from Firefox experiment, we never want a title, just the explicit share text + return NSNull() } return title diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Sharing/ShareManagerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Sharing/ShareManagerTests.swift index 9fb0980dd0be..e595ac9773ff 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Sharing/ShareManagerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Sharing/ShareManagerTests.swift @@ -356,10 +356,9 @@ final class ShareManagerTests: XCTestCase { XCTAssertEqual(activityItems.count, 5) XCTAssertEqual(urlDataIdentifier, UTType.plainText.identifier) XCTAssertEqual(itemForActivity as? String, expectedShareContentA) - XCTAssertEqual( - itemForTitleActivity as? String, - testWebpageDisplayTitle, - "When no explicit share message is set, we expect to see the webpage's title." + XCTAssertTrue( + itemForTitleActivity is NSNull, + "With Sent from Firefox shares to WhatsApp, never append an additional title." ) XCTAssertTrue(itemForShareActivity is NSNull) } @@ -407,15 +406,14 @@ final class ShareManagerTests: XCTestCase { XCTAssertEqual(activityItems.count, 5) XCTAssertEqual(urlDataIdentifier, UTType.plainText.identifier) XCTAssertEqual(itemForActivity as? String, expectedShareContentB) - XCTAssertEqual( - itemForTitleActivity as? String, - testWebpageDisplayTitle, - "When no explicit share message is set, we expect to see the webpage's title." + XCTAssertTrue( + itemForTitleActivity is NSNull, + "With Sent from Firefox shares to WhatsApp, never append an additional title." ) XCTAssertTrue(itemForShareActivity is NSNull) } - func testGetActivityItems_forTab_withSentFromFirefoxEnabled_doesNotImpactOtherShares() throws { + func testGetActivityItems_forTab_withSentFromFirefoxEnabled_doesNotImpactMail() throws { setupNimbusSentFromFirefoxTesting(isEnabled: true, isTreatmentA: true) let mailActivity = UIActivity.ActivityType.mail @@ -463,6 +461,55 @@ final class ShareManagerTests: XCTestCase { XCTAssertTrue(itemForShareActivity is NSNull) } + func testGetActivityItems_forTab_withSentFromFirefoxEnabled_doesNotImpactAirDrop() throws { + setupNimbusSentFromFirefoxTesting(isEnabled: true, isTreatmentA: true) + + let mailActivity = UIActivity.ActivityType.airDrop + + let activityItems = ShareManager.getActivityItems( + forShareType: .tab(url: testWebURL, tab: testTab), + withExplicitShareMessage: nil + ) + + // Check we get all types of share items for tabs below: + let urlActivityItemProvider = try XCTUnwrap(activityItems[safe: 0] as? URLActivityItemProvider) + let urlDataIdentifier = urlActivityItemProvider.activityViewController( + createStubActivityViewController(), + dataTypeIdentifierForActivityType: mailActivity + ) + let itemForActivity = urlActivityItemProvider.activityViewController( + createStubActivityViewController(), + itemForActivityType: mailActivity + ) + + // The rest of the content should be unchanged from other tests: + _ = try XCTUnwrap(activityItems[safe: 1] as? TabPrintPageRenderer) + + _ = try XCTUnwrap(activityItems[safe: 2] as? TabWebView) + + let titleActivityItemProvider = try XCTUnwrap(activityItems[safe: 3] as? TitleActivityItemProvider) + let itemForTitleActivity = titleActivityItemProvider.activityViewController( + createStubActivityViewController(), + itemForActivityType: mailActivity + ) + + let telemetryActivityItemProvider = try XCTUnwrap(activityItems[safe: 4] as? ShareTelemetryActivityItemProvider) + let itemForShareActivity = telemetryActivityItemProvider.activityViewController( + createStubActivityViewController(), + itemForActivityType: mailActivity + ) + + XCTAssertEqual(activityItems.count, 5) + XCTAssertEqual(urlDataIdentifier, UTType.url.identifier) + XCTAssertEqual(itemForActivity as? URL, testWebURL) + XCTAssertEqual( + itemForTitleActivity as? String, + testWebpageDisplayTitle, + "When no explicit share message is set, we expect to see the webpage's title." + ) + XCTAssertTrue(itemForShareActivity is NSNull) + } + func testGetActivityItems_forTab_withSentFromFirefoxDisabled_DoesNotOverride() throws { setupNimbusSentFromFirefoxTesting(isEnabled: false, isTreatmentA: true) @@ -621,10 +668,9 @@ final class ShareManagerTests: XCTestCase { XCTAssertEqual(activityItems.count, 5) XCTAssertEqual(urlDataIdentifier, UTType.plainText.identifier) XCTAssertEqual(itemForActivity as? String, expectedShareContentA) - XCTAssertEqual( - itemForTitleActivity as? String, - testWebpageDisplayTitle, - "When no explicit share message is set, we expect to see the webpage's title." + XCTAssertTrue( + itemForTitleActivity is NSNull, + "With Sent from Firefox shares to WhatsApp, never append an additional title." ) XCTAssertTrue(itemForShareActivity is NSNull) } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Sharing/TitleActivityItemProviderTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Sharing/TitleActivityItemProviderTests.swift index cbde6d46d62c..0a103756b0e9 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Sharing/TitleActivityItemProviderTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Sharing/TitleActivityItemProviderTests.swift @@ -77,6 +77,42 @@ final class TitleActivityItemProviderTests: XCTestCase { XCTAssertEqual(subtitle, testMessage) } + // MARK: - Sent from Firefox experiment WhatsApp tab share override + + func testOveridesWhatsAppShareItem_forApplySentFromFirefoxTreatment() { + let testActivityType = UIActivity.ActivityType(rawValue: "net.whatsapp.WhatsApp.ShareExtension") + + let titleActivityItemProvider = TitleActivityItemProvider(title: testMessage, applySentFromFirefoxTreatment: true) + let itemForActivity = titleActivityItemProvider.activityViewController( + createStubActivityViewController(), + itemForActivityType: testActivityType + ) + let subtitle = titleActivityItemProvider.activityViewController( + createStubActivityViewController(), + subjectForActivityType: testActivityType + ) + + XCTAssertTrue(itemForActivity is NSNull, "When applying Sent from Firefox treatment, don't append title to items") + XCTAssertEqual(subtitle, testMessage) + } + + func testBasicBehaviour_whenApplySentFromFirefoxTreatment_forUnrelatedActivity() { + let testActivityType = UIActivity.ActivityType.mail + + let titleActivityItemProvider = TitleActivityItemProvider(title: testMessage, applySentFromFirefoxTreatment: true) + let itemForActivity = titleActivityItemProvider.activityViewController( + createStubActivityViewController(), + itemForActivityType: testActivityType + ) + let subtitle = titleActivityItemProvider.activityViewController( + createStubActivityViewController(), + subjectForActivityType: testActivityType + ) + + XCTAssertTrue(itemForActivity is NSNull, "When applying Sent from Firefox treatment, don't append title to items") + XCTAssertEqual(subtitle, testMessage) + } + // MARK: - Helpers private func createStubActivityViewController() -> UIActivityViewController { From e9f02a05b9664d45bd82394e8f371f4474f2c20d Mon Sep 17 00:00:00 2001 From: Matt Lichtenstein Date: Mon, 13 Jan 2025 16:55:35 -0500 Subject: [PATCH 07/45] Add FXIOS-11032 [Bookmarks Evolution] A11y label for "..." disclosure button (#24084) --- .../Frontend/Library/Bookmarks/BookmarksViewController.swift | 1 + firefox-ios/Shared/Strings.swift | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift index c05928510282..2297045dfac1 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift @@ -388,6 +388,7 @@ class BookmarksViewController: SiteTableViewController, let contextButton = UIButton() contextButton.configuration = buttonConfig contextButton.frame = CGRect(width: 44, height: 44) + contextButton.accessibilityLabel = .Bookmarks.Menu.MoreOptionsA11yLabel return contextButton } diff --git a/firefox-ios/Shared/Strings.swift b/firefox-ios/Shared/Strings.swift index 246e21206025..2ee20d69aef3 100644 --- a/firefox-ios/Shared/Strings.swift +++ b/firefox-ios/Shared/Strings.swift @@ -197,6 +197,11 @@ extension String { tableName: "Bookmarks", value: "Saved in “Bookmarks”", comment: "The label displayed in the toast notification when saving a bookmark via the menu to the default folder. \"Bookmarks\" is the name of the default folder where the bookmark will be saved to.") + public static let MoreOptionsA11yLabel = MZLocalizedString( + key: "Bookmarks.Menu.MoreOptionsA11yLabel.v136", + tableName: "Bookmarks", + value: "More options", + comment: "Accessibility label for the \"...\" disclosure button located within every bookmark site cell in the bookmarks panel. Pressing this button opens a modal with more actions.") } public struct EmptyState { From 87bc76f98d3ac600fb991af4f50d03380273a856 Mon Sep 17 00:00:00 2001 From: Litianu Razvan Date: Tue, 14 Jan 2025 12:23:01 +0200 Subject: [PATCH 08/45] Add FXIOS-10896 iOS Add additional fields to the usage ping (#23950) * FXIOS-10896 #23785 iOS Add additional fields to the usage ping * Update links * Add GleanUsageReporting * Swiftlint fix * Swiftlint fix * Use glean timer * Update metrics with glean links * Add instalation usage * Add a way to stop observing --- .../Sources/Shared/InstallationUtils.swift | 16 ++ firefox-ios/Client.xcodeproj/project.pbxproj | 8 + .../Telemetry/GleanUsageReporting.swift | 108 +++++++++++ firefox-ios/Client/metrics.yaml | 175 ++++++++++++++++++ ...nUsageReportingLifecycleObserverTest.swift | 80 ++++++++ 5 files changed, 387 insertions(+) create mode 100644 BrowserKit/Sources/Shared/InstallationUtils.swift create mode 100644 firefox-ios/Client/Telemetry/GleanUsageReporting.swift create mode 100644 firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanUsageReportingLifecycleObserverTest.swift diff --git a/BrowserKit/Sources/Shared/InstallationUtils.swift b/BrowserKit/Sources/Shared/InstallationUtils.swift new file mode 100644 index 000000000000..4b670908dfe6 --- /dev/null +++ b/BrowserKit/Sources/Shared/InstallationUtils.swift @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct InstallationUtils { + /// Fetches the app's inferred installation date from the creation date of the Documents directory. + public static var inferredDateInstalledOn: Date? { + guard + let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last, + let attributes = try? FileManager.default.attributesOfItem(atPath: documentsURL.path) + else { return nil } + return attributes[.creationDate] as? Date + } +} diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 76cc1d18fe2f..9907aea4ed68 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -1122,6 +1122,8 @@ 8C92DE8B2A711ED60090BD28 /* FakespotClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C92DE8A2A711ED60090BD28 /* FakespotClient.swift */; }; 8C92DE912A7128CB0090BD28 /* ProductAnalysisResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C92DE902A7128CB0090BD28 /* ProductAnalysisResponse.swift */; }; 8C92DE932A7128DE0090BD28 /* ProductAdsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C92DE922A7128DE0090BD28 /* ProductAdsResponse.swift */; }; + 8CA4E80D2D22C066007207C1 /* GleanUsageReporting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA4E80C2D22C066007207C1 /* GleanUsageReporting.swift */; }; + 8CA4E80F2D22D2C7007207C1 /* GleanUsageReportingLifecycleObserverTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA4E80E2D22D2C7007207C1 /* GleanUsageReportingLifecycleObserverTest.swift */; }; 8CAF29A02AA5E76B00DC3486 /* FakespotMessageCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAF299F2AA5E76B00DC3486 /* FakespotMessageCardView.swift */; }; 8CBDE8E32AB09804001985BF /* ProductAnalyzeResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CBDE8E22AB09804001985BF /* ProductAnalyzeResponse.swift */; }; 8CC033FA2BA476840033449E /* FormAutofillHelper.js in Resources */ = {isa = PBXBuildFile; fileRef = 8CC033F92BA476840033449E /* FormAutofillHelper.js */; }; @@ -7950,6 +7952,8 @@ 8C92DE8A2A711ED60090BD28 /* FakespotClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotClient.swift; sourceTree = ""; }; 8C92DE902A7128CB0090BD28 /* ProductAnalysisResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductAnalysisResponse.swift; sourceTree = ""; }; 8C92DE922A7128DE0090BD28 /* ProductAdsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductAdsResponse.swift; sourceTree = ""; }; + 8CA4E80C2D22C066007207C1 /* GleanUsageReporting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GleanUsageReporting.swift; sourceTree = ""; }; + 8CA4E80E2D22D2C7007207C1 /* GleanUsageReportingLifecycleObserverTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GleanUsageReportingLifecycleObserverTest.swift; sourceTree = ""; }; 8CA841E0986514988B705D97 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Menu.strings; sourceTree = ""; }; 8CAF299F2AA5E76B00DC3486 /* FakespotMessageCardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FakespotMessageCardView.swift; sourceTree = ""; }; 8CBDE8E22AB09804001985BF /* ProductAnalyzeResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductAnalyzeResponse.swift; sourceTree = ""; }; @@ -14118,6 +14122,7 @@ 8A44F20D2B585E1F0016BC81 /* HomepageTelemetry.swift */, 8A95FF632B1E969E00AC303D /* TelemetryContextualIdentifier.swift */, EBF47E6F1F7979DF00899189 /* TelemetryWrapper.swift */, + 8CA4E80C2D22C066007207C1 /* GleanUsageReporting.swift */, 1DD4B26D2CA4D09100B51945 /* TabErrorTelemetryHelper.swift */, 8A0727452B4890B50071BB9F /* WebviewTelemetry.swift */, 8A359EF42A1FD4CF004A5BB7 /* Wrapper */, @@ -14354,6 +14359,7 @@ F84B21D91A090F8100AAB793 /* ClientTests.swift */, 8A86DAD7277298DE00D7BFFF /* ClosedTabsStoreTests.swift */, C2200A692B7D148C00DC062A /* ContentBlockerTests.swift */, + 8CA4E80E2D22D2C7007207C1 /* GleanUsageReportingLifecycleObserverTest.swift */, C889D7CD2858C4B500121E1D /* ContextMenuHelperTests.swift */, 8A93F86329D37314004159D9 /* Coordinators */, 43B658D729CE249D00C9EF08 /* CreditCard */, @@ -16767,6 +16773,7 @@ 6669B5E2211418A200CA117B /* WebsiteDataSearchResultsViewController.swift in Sources */, D51EA5CF26406D8300334331 /* ExperimentsViewController.swift in Sources */, ED7A08DB2CF674730035EC8F /* ShareMessage.swift in Sources */, + 8CA4E80D2D22C066007207C1 /* GleanUsageReporting.swift in Sources */, 1DFE57FB27B2CB870025DE58 /* HighlightItem.swift in Sources */, CA77ABFD24773C92005079F9 /* BreachAlertsManager.swift in Sources */, 8AB8572727D93AEC0075C173 /* TopSiteHistoryManager.swift in Sources */, @@ -17247,6 +17254,7 @@ 2165B2C42860CB34004C0786 /* MockAdjustTelemetryData.swift in Sources */, C29B64872AD69D0200F3244B /* QRCodeCoordinatorTests.swift in Sources */, 0BA8964B1A250E6500C1010C /* ProfileTest.swift in Sources */, + 8CA4E80F2D22D2C7007207C1 /* GleanUsageReportingLifecycleObserverTest.swift in Sources */, 8AE80BAF2891960300BC12EA /* MockTraitCollection.swift in Sources */, E19443F82AF953B000964EA5 /* MockSidebarEnabledView.swift in Sources */, 8A55E8042BFBA9BE006DBD85 /* MicrosurveyCoordinatorTests.swift in Sources */, diff --git a/firefox-ios/Client/Telemetry/GleanUsageReporting.swift b/firefox-ios/Client/Telemetry/GleanUsageReporting.swift new file mode 100644 index 000000000000..c526df5cf7e6 --- /dev/null +++ b/firefox-ios/Client/Telemetry/GleanUsageReporting.swift @@ -0,0 +1,108 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import UIKit +import Glean +import Shared + +enum UsageReason: String { + case active + case inactive +} + +protocol GleanUsageReportingApi { + func setUsageReason(_ usageReason: UsageReason) + func submitPing() +} + +class GleanUsageReporting: GleanUsageReportingApi { + func setUsageReason(_ usageReason: UsageReason) { + GleanMetrics.Usage.reason.set(usageReason.rawValue) + } + + func submitPing() { + setUsageConstantValues() + GleanMetrics.Pings.shared.usageReporting.submit() + } + + private func setUsageConstantValues() { + GleanMetrics.Usage.os.set("iOS") + GleanMetrics.Usage.osVersion.set(UIDevice.current.systemVersion) + GleanMetrics.Usage.appDisplayVersion.set(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "") + GleanMetrics.Usage.appBuild.set(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "") + GleanMetrics.Usage.appChannel.set(AppConstants.buildChannel.rawValue) + if let date = InstallationUtils.inferredDateInstalledOn { + GleanMetrics.Usage.firstRunDate.set(date) + } + } +} + +class GleanLifecycleObserver { + private let gleanUsageReportingApi: GleanUsageReportingApi + private var id: TimerId? + private var isObserving: Bool = false + + init(gleanUsageReportingApi: GleanUsageReportingApi = GleanUsageReporting()) { + self.gleanUsageReportingApi = gleanUsageReportingApi + } + + func startObserving() { + guard !isObserving else { return } + isObserving = true + + NotificationCenter.default.addObserver( + self, + selector: #selector(appWillEnterForeground), + name: UIApplication.willEnterForegroundNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(appDidEnterBackground), + name: UIApplication.didEnterBackgroundNotification, + object: nil + ) + } + + func stopObserving() { + guard isObserving else { return } + isObserving = false + + NotificationCenter.default.removeObserver( + self, + name: UIApplication.willEnterForegroundNotification, + object: nil + ) + + NotificationCenter.default.removeObserver( + self, + name: UIApplication.didEnterBackgroundNotification, + object: nil + ) + } + + @objc + private func appWillEnterForeground(notification: NSNotification) { + handleForegroundEvent() + } + + @objc + private func appDidEnterBackground(notification: NSNotification) { + handleBackgroundEvent() + } + + func handleForegroundEvent() { + id = GleanMetrics.Usage.duration.start() + gleanUsageReportingApi.setUsageReason(.active) + gleanUsageReportingApi.submitPing() + } + + func handleBackgroundEvent() { + id.map(GleanMetrics.Usage.duration.stopAndAccumulate) + gleanUsageReportingApi.setUsageReason(.inactive) + gleanUsageReportingApi.submitPing() + } +} diff --git a/firefox-ios/Client/metrics.yaml b/firefox-ios/Client/metrics.yaml index fc509f99ce30..5144825a23e9 100755 --- a/firefox-ios/Client/metrics.yaml +++ b/firefox-ios/Client/metrics.yaml @@ -753,6 +753,181 @@ usage: send_in_pings: - usage-reporting + duration: + type: timing_distribution + time_unit: millisecond + description: | + The duration of the last foreground session. + time_unit: second + send_in_pings: + - usage-reporting + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23785 + - https://bugzilla.mozilla.org/1497894 + - https://bugzilla.mozilla.org/1519120 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23950 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1512938#c3 + data_sensitivity: + - technical + - interaction + notification_emails: + - fx-ios-data-stewards@mozilla.com + - glean-team@mozilla.com + expires: never + + reason: + type: string + lifetime: ping + send_in_pings: + - usage-reporting + description: | + The optional reason the ping was submitted. + The specific values for reason are specific to each ping, and are + documented in the ping's pings.yaml file. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23785 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1557048 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23950 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1609218#c4 + data_sensitivity: + - technical + notification_emails: + - fx-ios-data-stewards@mozilla.com + - glean-team@mozilla.com + expires: never + + os: + type: string + lifetime: application + send_in_pings: + - usage-reporting + description: | + The name of the operating system. + Possible values: + Android, iOS, Linux, Darwin, Windows, + FreeBSD, NetBSD, OpenBSD, Solaris, Unknown + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23785 + - https://bugzilla.mozilla.org/1497894 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23950 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1512938#c3 + data_sensitivity: + - technical + notification_emails: + - fx-ios-data-stewards@mozilla.com + - glean-team@mozilla.com + expires: never + + os_version: + type: string + lifetime: application + send_in_pings: + - usage-reporting + description: | + The user-visible version of the operating system (e.g. "1.2.3"). + If the version detection fails, this metric gets set to `Unknown`. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23785 + - https://bugzilla.mozilla.org/1497894 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23950 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1512938#c3 + data_sensitivity: + - technical + notification_emails: + - fx-ios-data-stewards@mozilla.com + - glean-team@mozilla.com + expires: never + + app_display_version: + type: string + lifetime: application + send_in_pings: + - usage-reporting + description: | + The user visible version string (e.g. "1.0.3"). + If the value was not provided through configuration, + this metric gets set to `Unknown`. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23785 + - https://bugzilla.mozilla.org/1508305 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23950 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1508305#c9 + data_sensitivity: + - technical + notification_emails: + - fx-ios-data-stewards@mozilla.com + - glean-team@mozilla.com + expires: never + + app_channel: + type: string + lifetime: application + send_in_pings: + - usage-reporting + description: | + The channel the application is being distributed on. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23785 + - https://bugzilla.mozilla.org/1520741 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23950 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1520741#c18 + data_sensitivity: + - technical + notification_emails: + notification_emails: + - fx-ios-data-stewards@mozilla.com + - glean-team@mozilla.com + expires: never + + first_run_date: + type: datetime + lifetime: user + send_in_pings: + - usage-reporting + time_unit: day + description: | + The date of the first run of the application. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23785 + - https://bugzilla.mozilla.org/1525045 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23950 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1525045#c18 + data_sensitivity: + - technical + notification_emails: + - fx-ios-data-stewards@mozilla.com + - glean-team@mozilla.com + expires: never + + app_build: + type: string + lifetime: application + send_in_pings: + - usage-reporting + description: | + The build identifier generated by the CI system (e.g. "1234/A"). + If the value was not provided through configuration, + this metric gets set to `Unknown`. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23785 + - https://bugzilla.mozilla.org/1508305 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23950 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1512938#c3 + data_sensitivity: + - technical + notification_emails: + - fx-ios-data-stewards@mozilla.com + - glean-team@mozilla.com + expires: never + # Downloads downloads: download_now_button_tapped: diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanUsageReportingLifecycleObserverTest.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanUsageReportingLifecycleObserverTest.swift new file mode 100644 index 000000000000..1c9055e75c16 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanUsageReportingLifecycleObserverTest.swift @@ -0,0 +1,80 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client + +class GleanUsageReportingApiMock: GleanUsageReportingApi { + var pingSubmitCount = 0 + var lastUsageReason: String? + var lastDurationMillis: Int64? + + func setUsageReason(_ usageReason: UsageReason) { + lastUsageReason = usageReason.rawValue + } + + func submitPing() { + pingSubmitCount += 1 + } + + func setDuration(_ durationMillis: Int64) { + lastDurationMillis = durationMillis + } +} + +class GleanUsageReportingLifecycleObserverTest: XCTestCase { + private var fakeGleanUsageReportingApi: GleanUsageReportingApiMock! + + override func setUp() { + super.setUp() + fakeGleanUsageReportingApi = GleanUsageReportingApiMock() + } + + func testNoPingsSubmittedBeforeLifecycleChanges() { + _ = createObserver() + XCTAssertEqual(fakeGleanUsageReportingApi.pingSubmitCount, 0) + } + + func testNoUsageReasonSetBeforeLifecycleChanges() { + _ = createObserver() + XCTAssertNil(fakeGleanUsageReportingApi.lastUsageReason) + } + + func testSetUsageReasonToActiveOnStart() { + let observer = createObserver() + observer.handleForegroundEvent() + XCTAssertEqual(fakeGleanUsageReportingApi.lastUsageReason, "active") + } + + func testSubmitPingOnStart() { + let observer = createObserver() + observer.handleForegroundEvent() + XCTAssertEqual(fakeGleanUsageReportingApi.pingSubmitCount, 1) + } + + func testSetUsageReasonToInactiveOnStop() { + let observer = createObserver() + observer.handleBackgroundEvent() + XCTAssertEqual(fakeGleanUsageReportingApi.lastUsageReason, "inactive") + } + + func testSubmitPingOnStop() { + let observer = createObserver() + observer.handleForegroundEvent() + observer.handleBackgroundEvent() + XCTAssertEqual(fakeGleanUsageReportingApi.pingSubmitCount, 2) + } + + func testDoNotSubmitDurationIfNotSet() { + let observer = createObserver() + observer.handleBackgroundEvent() + XCTAssertNil(fakeGleanUsageReportingApi.lastDurationMillis) + } + + private func createObserver() -> GleanLifecycleObserver { + return GleanLifecycleObserver( + gleanUsageReportingApi: fakeGleanUsageReportingApi + ) + } +} From eabf819d72775e84f98c85928b202371f89883f3 Mon Sep 17 00:00:00 2001 From: dicarobinho <61138287+dicarobinho@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:42:25 +0200 Subject: [PATCH 09/45] Bugfix FXIOS-11058 [ToS] Firefox iOS: Update Crash Reports setting option subtitle (#24119) FXIOS-11058 Firefox iOS: Update Crash Reports setting option subtitle --- .../Main/AppSettingsTableViewController.swift | 2 +- firefox-ios/Shared/Strings.swift | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift index 2e5c278e720b..88ccf10d802a 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift @@ -262,7 +262,7 @@ class AppSettingsTableViewController: SettingsTableViewController, theme: themeManager.getCurrentTheme(for: windowUUID), settingsDelegate: parentCoordinator, title: .SendCrashReportsSettingTitle, - message: String(format: .SendCrashReportsSettingMessage, MozillaName.shortName.rawValue), + message: String(format: .SendCrashReportsSettingMessageV2, MozillaName.shortName.rawValue), linkedText: isTermsOfServiceFeatureEnabled ? .SendCrashReportsSettingLinkV2 : .SendCrashReportsSettingLink, prefKey: AppConstants.prefSendCrashReports, a11yId: AccessibilityIdentifiers.Settings.SendData.sendCrashReportsTitle, diff --git a/firefox-ios/Shared/Strings.swift b/firefox-ios/Shared/Strings.swift index 2ee20d69aef3..ef204716bf19 100644 --- a/firefox-ios/Shared/Strings.swift +++ b/firefox-ios/Shared/Strings.swift @@ -5399,10 +5399,10 @@ extension String { tableName: "Settings", value: "Learn More", comment: "Title for a link that explains how Mozilla send crash reports.") - public static let SendCrashReportsSettingMessage = MZLocalizedString( - key: "Settings.CrashReports.Message.v135", + public static let SendCrashReportsSettingMessageV2 = MZLocalizedString( + key: "Settings.CrashReports.Message.v136", tableName: "Settings", - value: "Crash reports allow us diagnose and fix issues with the browser.", + value: "This helps us diagnose and fix issues with the browser.", comment: "On the Settings screen, this is the subtitle text for a toggle which controls automatically sending crash reports.") public static let SendDailyUsagePingSettingTitle = MZLocalizedString( key: "Settings.DailyUsagePing.Title.v135", @@ -7676,6 +7676,11 @@ extension String { tableName: "Onboarding", value: "%@ Terms of Service.", comment: "Title for the Terms of Service button link, in the Terms of Service screen for redirecting the user to the Terms of Service page. Placeholder is for the app name.") + public static let SendCrashReportsSettingMessage = MZLocalizedString( + key: "Settings.CrashReports.Message.v135", + tableName: "Settings", + value: "Crash reports allow us diagnose and fix issues with the browser.", + comment: "On the Settings screen, this is the subtitle text for a toggle which controls automatically sending crash reports.") } } } From 54ac8d44f4500d9d6772e6acfede99b2d358d989 Mon Sep 17 00:00:00 2001 From: Clare So <1740517+clarmso@users.noreply.github.com> Date: Tue, 14 Jan 2025 08:54:43 -0500 Subject: [PATCH 10/45] Bugfix MTE-4076 Bump python version for Bitrise Xcode Check and Update (#24103) * Bump python version? * Add workflow_dispatch * Can't workflow_dispatch :shrug: --- .github/workflows/firefox-ios-update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/firefox-ios-update.yml b/.github/workflows/firefox-ios-update.yml index ce687afefd83..05fdc3651304 100644 --- a/.github/workflows/firefox-ios-update.yml +++ b/.github/workflows/firefox-ios-update.yml @@ -10,7 +10,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.7] + python-version: [3.7.17] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} From 2d8fe7b072f31ecbfe22edceb63cb42e4e2f1afb Mon Sep 17 00:00:00 2001 From: Ione Souza Junior Date: Tue, 14 Jan 2025 11:13:20 -0300 Subject: [PATCH 11/45] Refactor FXIOS-7301 [Swiftlint] Remove 1 closure_body_length violation from MainMenuDetailState.swift and decrease threshold (#24100) * Decrease warning and error threshold * Remove closure body violation from MainMenuDetailState.swift --- .swiftlint.yml | 4 +- .../MainMenu/Redux/MainMenuDetailState.swift | 128 ++++++++++-------- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 7bdcc4d32d91..92e874d0e960 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -102,8 +102,8 @@ line_length: ignores_interpolated_strings: true closure_body_length: - warning: 68 - error: 68 + warning: 64 + error: 64 file_header: required_string: | diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuDetailState.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuDetailState.swift index cdfc3c906fdf..c867f7773d55 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuDetailState.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuDetailState.swift @@ -82,40 +82,9 @@ struct MainMenuDetailsState: ScreenState, Equatable { switch action.actionType { case ScreenActionType.showScreen: - guard let screenAction = action as? ScreenAction, - screenAction.screen == .mainMenuDetails, - let menuState = store.state.screenState( - MainMenuState.self, - for: .mainMenu, - window: action.windowUUID), - let currentTabInfo = menuState.currentTabInfo, - let currentSubmenu = menuState.currentSubmenuView - // let toolbarState = store.state.screenState( - // ToolbarState.self, - // for: .toolbar, - // window: action.windowUUID), - // let readerModeState = toolbarState.addressToolbar.readerModeState - else { return defaultState(from: state) } - - return MainMenuDetailsState( - windowUUID: state.windowUUID, - menuElements: state.menuConfigurator.generateMenuElements( - with: currentTabInfo, - for: currentSubmenu, - and: action.windowUUID, - readerState: nil - ), - submenuType: currentSubmenu, - isHomepage: state.isHomepage - ) + return handleShowScreenAction(action: action, state: state) case MainMenuDetailsActionType.tapBackToMainMenu: - return MainMenuDetailsState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - submenuType: state.submenuType, - isHomepage: state.isHomepage, - shouldGoBackToMenu: true - ) + return handleTapBackToMainMenuAction(state: state) case MainMenuDetailsActionType.tapDismissView, MainMenuDetailsActionType.tapAddToBookmarks, MainMenuDetailsActionType.tapAddToShortcuts, @@ -124,34 +93,85 @@ struct MainMenuDetailsState: ScreenState, Equatable { MainMenuDetailsActionType.tapRemoveFromReadingList, MainMenuDetailsActionType.tapToggleNightMode, GeneralBrowserActionType.showReaderMode: - return MainMenuDetailsState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - submenuType: state.submenuType, - isHomepage: state.isHomepage, - shouldDismiss: true - ) + return handleDismissableAction(state: state) case MainMenuDetailsActionType.tapEditBookmark: - return MainMenuDetailsState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - submenuType: state.submenuType, - isHomepage: state.isHomepage, - navigationDestination: MenuNavigationDestination(.editBookmark) - ) + return handleTapEditBookmarkAction(state: state) case MainMenuDetailsActionType.tapZoom: - return MainMenuDetailsState( - windowUUID: state.windowUUID, - menuElements: state.menuElements, - submenuType: state.submenuType, - isHomepage: state.isHomepage, - navigationDestination: MenuNavigationDestination(.zoom) - ) + return handleTapZoomAction(state: state) default: return defaultState(from: state) } } + private static func handleShowScreenAction(action: Action, state: Self) -> MainMenuDetailsState { + guard let screenAction = action as? ScreenAction, + screenAction.screen == .mainMenuDetails, + let menuState = store.state.screenState( + MainMenuState.self, + for: .mainMenu, + window: action.windowUUID), + let currentTabInfo = menuState.currentTabInfo, + let currentSubmenu = menuState.currentSubmenuView + // let toolbarState = store.state.screenState( + // ToolbarState.self, + // for: .toolbar, + // window: action.windowUUID), + // let readerModeState = toolbarState.addressToolbar.readerModeState + else { return defaultState(from: state) } + + return MainMenuDetailsState( + windowUUID: state.windowUUID, + menuElements: state.menuConfigurator.generateMenuElements( + with: currentTabInfo, + for: currentSubmenu, + and: action.windowUUID, + readerState: nil + ), + submenuType: currentSubmenu, + isHomepage: state.isHomepage + ) + } + + private static func handleTapBackToMainMenuAction(state: Self) -> MainMenuDetailsState { + return MainMenuDetailsState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + submenuType: state.submenuType, + isHomepage: state.isHomepage, + shouldGoBackToMenu: true + ) + } + + private static func handleDismissableAction(state: Self) -> MainMenuDetailsState { + return MainMenuDetailsState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + submenuType: state.submenuType, + isHomepage: state.isHomepage, + shouldDismiss: true + ) + } + + private static func handleTapEditBookmarkAction(state: Self) -> MainMenuDetailsState { + return MainMenuDetailsState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + submenuType: state.submenuType, + isHomepage: state.isHomepage, + navigationDestination: MenuNavigationDestination(.editBookmark) + ) + } + + private static func handleTapZoomAction(state: Self) -> MainMenuDetailsState { + return MainMenuDetailsState( + windowUUID: state.windowUUID, + menuElements: state.menuElements, + submenuType: state.submenuType, + isHomepage: state.isHomepage, + navigationDestination: MenuNavigationDestination(.zoom) + ) + } + static func defaultState(from state: MainMenuDetailsState) -> MainMenuDetailsState { return MainMenuDetailsState( windowUUID: state.windowUUID, From 3137c1235e6af34e577436ac87c44f6127980696 Mon Sep 17 00:00:00 2001 From: isabelrios Date: Tue, 14 Jan 2025 16:25:45 +0100 Subject: [PATCH 12/45] Upgrade python in github actions with 3.7 (#24126) --- .github/workflows/check-rust-component-dependency.yml | 2 +- .../workflows/firefox-ios-update-effective-tld-names-file.yml | 2 +- .github/workflows/firefox-ios-update.yml | 2 +- .../workflows/firefox-ios-update_credential_provider_script.yml | 2 +- .../firefox-ios-update_remote_settings_data_script.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check-rust-component-dependency.yml b/.github/workflows/check-rust-component-dependency.yml index 93ce27a36443..2d9df7c1703c 100644 --- a/.github/workflows/check-rust-component-dependency.yml +++ b/.github/workflows/check-rust-component-dependency.yml @@ -14,7 +14,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.7] + python-version: [3.10.16] steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/firefox-ios-update-effective-tld-names-file.yml b/.github/workflows/firefox-ios-update-effective-tld-names-file.yml index d68576f2475a..73214d32b82c 100644 --- a/.github/workflows/firefox-ios-update-effective-tld-names-file.yml +++ b/.github/workflows/firefox-ios-update-effective-tld-names-file.yml @@ -14,7 +14,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.9] + python-version: [3.10.16] steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/firefox-ios-update.yml b/.github/workflows/firefox-ios-update.yml index 05fdc3651304..2c92febcee6d 100644 --- a/.github/workflows/firefox-ios-update.yml +++ b/.github/workflows/firefox-ios-update.yml @@ -10,7 +10,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.7.17] + python-version: [3.10.16] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/firefox-ios-update_credential_provider_script.yml b/.github/workflows/firefox-ios-update_credential_provider_script.yml index 8a9bd6a16ea1..51416ea56732 100644 --- a/.github/workflows/firefox-ios-update_credential_provider_script.yml +++ b/.github/workflows/firefox-ios-update_credential_provider_script.yml @@ -14,7 +14,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.7] + python-version: [3.10.16] steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/firefox-ios-update_remote_settings_data_script.yml b/.github/workflows/firefox-ios-update_remote_settings_data_script.yml index 8909d76a6397..c25973b78834 100644 --- a/.github/workflows/firefox-ios-update_remote_settings_data_script.yml +++ b/.github/workflows/firefox-ios-update_remote_settings_data_script.yml @@ -17,7 +17,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.7] + python-version: [3.10.16] steps: - uses: actions/checkout@v4 with: From 591b9cd41c5fab5dc900eaddb57df453e00ee69e Mon Sep 17 00:00:00 2001 From: Litianu Razvan Date: Tue, 14 Jan 2025 19:26:27 +0200 Subject: [PATCH 13/45] Add FXIOS-11057 Focus iOS: Change detail string for crash reporting toggle (#24120) * Add FXIOS-11057 Focus iOS: Change detail string for crash reporting toggle * Remove old strings --- .../Settings/Controller/SettingsViewController.swift | 2 +- focus-ios/Blockzilla/UIComponents/UIConstants.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift b/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift index fd6e8c3db8cf..6ff8286c289d 100644 --- a/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift +++ b/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift @@ -128,7 +128,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi let crashToggle = BlockerToggle( label: UIConstants.strings.labelCrashReports, setting: SettingsToggle.crashToggle, - subtitle: UIConstants.strings.detailTextCrashReports + subtitle: UIConstants.strings.detailTextCrashReportsV2 ) let searchSuggestionSubtitle = String(format: UIConstants.strings.detailTextSearchSuggestion, AppInfo.productName) let searchSuggestionToggle = BlockerToggle(label: UIConstants.strings.settingsSearchSuggestions, setting: SettingsToggle.enableSearchSuggestions, subtitle: searchSuggestionSubtitle) diff --git a/focus-ios/Blockzilla/UIComponents/UIConstants.swift b/focus-ios/Blockzilla/UIComponents/UIConstants.swift index bfc2eca61318..75645a3be5ab 100644 --- a/focus-ios/Blockzilla/UIComponents/UIConstants.swift +++ b/focus-ios/Blockzilla/UIComponents/UIConstants.swift @@ -208,9 +208,9 @@ struct UIConstants { value: "Automatically Send Crash Reports", comment: "Label for Crash Reports toggle on settings screen" ) - static let detailTextCrashReports = NSLocalizedString( - "Settings.detailTextCrashReports", - value: "Crash reports allow us diagnose and fix issues with the browser.", + static let detailTextCrashReportsV2 = NSLocalizedString( + "Settings.detailTextCrashReports.V2", + value: "This helps us diagnose and fix issues with the browser.", comment: "Description associated with the Crash Reports toggle on settings screen" ) From 2d9bc743847100e4c930432686fe080db6b38118 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:35:10 -0500 Subject: [PATCH 14/45] Refactor [v136] Auto update SPM with latest rust-component 136.0.20250113194724 (#24135) Auto update SPM with latest rust-component release 136.0.20250113194724 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- firefox-ios/Client.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- focus-ios/Blockzilla.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 9907aea4ed68..7436f400e4d0 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -26461,7 +26461,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift.git"; requirement = { kind = exactVersion; - version = 136.0.20250108050329; + version = 136.0.20250113194724; }; }; 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */ = { diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7840d80bc97b..42a91bedaa9e 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/rust-components-swift.git", "state" : { - "revision" : "4bf41b6108467bc3e9acbb8e1ab5d5c2eccc4fd0", - "version" : "136.0.20250108050329" + "revision" : "ce80ed1d87747988cecb49695537eeda645deb92", + "version" : "136.0.20250113194724" } }, { diff --git a/focus-ios/Blockzilla.xcodeproj/project.pbxproj b/focus-ios/Blockzilla.xcodeproj/project.pbxproj index c1c102c02cfa..10d9f99d27f3 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.pbxproj +++ b/focus-ios/Blockzilla.xcodeproj/project.pbxproj @@ -7198,7 +7198,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift"; requirement = { kind = exactVersion; - version = 136.0.20250108050329; + version = 136.0.20250113194724; }; }; 8A0E7F2C2BA0F0E0006BC6B6 /* XCRemoteSwiftPackageReference "Fuzi" */ = { diff --git a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b974a5155a89..1c75cd78c414 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/mozilla/rust-components-swift", "state": { "branch": null, - "revision": "4bf41b6108467bc3e9acbb8e1ab5d5c2eccc4fd0", - "version": "136.0.20250108050329" + "revision": "ce80ed1d87747988cecb49695537eeda645deb92", + "version": "136.0.20250113194724" } }, { From f7f4ce17c8f1c02505ac3adeacfe3e631a3101b6 Mon Sep 17 00:00:00 2001 From: Matt Lichtenstein Date: Tue, 14 Jan 2025 13:35:42 -0500 Subject: [PATCH 15/45] Add FXIOS-11030 [Bookmarks Evolution] Support dynamic type for bookmarks accessory views (#24115) --- .../Browser/Search/SearchViewController.swift | 1 + .../Bookmarks/BookmarksViewController.swift | 1 - .../Widgets/OneLineTableViewCell.swift | 52 +++++++++++++++---- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/firefox-ios/Client/Frontend/Browser/Search/SearchViewController.swift b/firefox-ios/Client/Frontend/Browser/Search/SearchViewController.swift index f10ee7cb23e7..12aa46126aa3 100644 --- a/firefox-ios/Client/Frontend/Browser/Search/SearchViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Search/SearchViewController.swift @@ -726,6 +726,7 @@ class SearchViewController: SiteTableViewController, appendButton.frame = CGRect(width: SearchViewControllerUX.AppendButtonSize, height: SearchViewControllerUX.AppendButtonSize) oneLineCell.accessoryView = indexPath.row > 0 ? appendButton : nil + oneLineCell.isAccessoryViewInteractive = true cell = oneLineCell } case .openedTabs: diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift index 2297045dfac1..c092b9263a9b 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift @@ -387,7 +387,6 @@ class BookmarksViewController: SiteTableViewController, buttonConfig.automaticallyUpdateForSelection = true let contextButton = UIButton() contextButton.configuration = buttonConfig - contextButton.frame = CGRect(width: 44, height: 44) contextButton.accessibilityLabel = .Bookmarks.Menu.MoreOptionsA11yLabel return contextButton diff --git a/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift b/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift index 3de7ce96acaf..cdf7bfc09c77 100644 --- a/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift +++ b/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift @@ -36,10 +36,9 @@ class OneLineTableViewCell: UITableViewCell, static let shortLeadingMargin: CGFloat = 5 static let longLeadingMargin: CGFloat = 13 static let cornerRadius: CGFloat = 5 - static let accessoryViewTrailingPaddingForImage: CGFloat = 16 - // Icon buttons typically have a minimum padding of 44px, so for them to be vertically aligned with image - // accessory views (24px width), they would need 10px less trailing padding - static let accessoryViewTrailingPaddingForButton: CGFloat = 6 + static let accessoryViewIconSize: CGFloat = 24 + static let accessoryViewSize: CGFloat = 44 + static let accessoryViewTrailingPadding: CGFloat = 6 } var reorderControlImageView: UIImageView? { @@ -69,6 +68,8 @@ class OneLineTableViewCell: UITableViewCell, separatorLine.isHidden = true } + var isAccessoryViewInteractive = false + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupLayout() @@ -90,9 +91,7 @@ class OneLineTableViewCell: UITableViewCell, updateReorderControl() if let accessoryView { - let accessoryPadding = accessoryView is UIButton ? UX.accessoryViewTrailingPaddingForButton - : UX.accessoryViewTrailingPaddingForImage - accessoryView.frame.origin.x = frame.width - accessoryView.frame.width - accessoryPadding + accessoryView.frame.origin.x = frame.width - accessoryView.frame.width - UX.accessoryViewTrailingPadding } } @@ -183,6 +182,39 @@ class OneLineTableViewCell: UITableViewCell, selectedBackgroundView = selectedView } + private func createAccessoryView(accessoryView: UIView?) -> UIView? { + guard let accessoryView else { return nil } + let isButton = accessoryView is UIButton + let iconSize = min(UIFontMetrics.default.scaledValue(for: UX.accessoryViewIconSize), UX.accessoryViewIconSize * 2) + let accessoryViewSize = isButton ? UX.accessoryViewSize : iconSize + + let customAccessoryView: UIView = { + let view = UIView() + view.addSubview(accessoryView) + + if isButton { + let button = accessoryView as? UIButton + var buttonConfig = button?.configuration + let image = buttonConfig?.image?.createScaled(CGSize(width: iconSize, height: iconSize)) + buttonConfig?.image = image + button?.configuration = buttonConfig + } + + accessoryView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + accessoryView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + accessoryView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + accessoryView.widthAnchor.constraint(equalToConstant: accessoryViewSize), + accessoryView.heightAnchor.constraint(equalToConstant: accessoryViewSize) + ]) + + return view + }() + + customAccessoryView.frame = CGRect(x: 0, y: 0, width: UX.accessoryViewSize, height: UX.accessoryViewSize) + return customAccessoryView + } + override func prepareForReuse() { super.prepareForReuse() @@ -196,10 +228,12 @@ class OneLineTableViewCell: UITableViewCell, // To simplify setup, OneLineTableViewCell now has a viewModel // Use it for new code, replace when possible in old code func configure(viewModel: OneLineTableViewCellViewModel) { + isAccessoryViewInteractive = viewModel.accessoryView is UIButton + titleLabel.text = viewModel.title - accessoryView = viewModel.accessoryView + accessoryView = createAccessoryView(accessoryView: viewModel.accessoryView) accessoryType = viewModel.accessoryType - editingAccessoryView = viewModel.editingAccessoryView + editingAccessoryView = createAccessoryView(accessoryView: viewModel.editingAccessoryView) if let image = viewModel.leftImageView { leftImageView.manuallySetImage(image) From 703ef66d8f452f927a2d84aafef35084d79370ce Mon Sep 17 00:00:00 2001 From: Daniel Dervishi <58835213+DanielDervishi@users.noreply.github.com> Date: Tue, 14 Jan 2025 14:48:08 -0500 Subject: [PATCH 16/45] Bugfix FXIOS-11013 [Bookmarks Evolution] Incorrect edit bookmark and folder theming in landscape dark mode (#24137) * Completed ticket * Fixing random swiftlint issue --- .../Library/Bookmarks/Edit Bookmark/EditBookmarkCell.swift | 2 +- .../Frontend/Library/Bookmarks/Edit Folder/EditFolderCell.swift | 2 +- firefox-ios/Client/Telemetry/GleanUsageReporting.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkCell.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkCell.swift index 05f4d82348e2..d8d1bf440afe 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkCell.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkCell.swift @@ -101,7 +101,7 @@ class EditBookmarkCell: UITableViewCell, urlTextfield.applyTheme(theme: theme) titleTextfield.applyTheme(theme: theme) textFieldsDivder.backgroundColor = theme.colors.borderPrimary - contentView.backgroundColor = theme.colors.layer2 + backgroundColor = theme.colors.layer5 } private func urlTextFieldDidChane() { diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Folder/EditFolderCell.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Folder/EditFolderCell.swift index bb29f42ba0b2..c04b0c84cccb 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Folder/EditFolderCell.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Folder/EditFolderCell.swift @@ -60,6 +60,6 @@ class EditFolderCell: UITableViewCell, func applyTheme(theme: any Theme) { titleTextField.applyTheme(theme: theme) - contentView.backgroundColor = theme.colors.layer2 + backgroundColor = theme.colors.layer5 } } diff --git a/firefox-ios/Client/Telemetry/GleanUsageReporting.swift b/firefox-ios/Client/Telemetry/GleanUsageReporting.swift index c526df5cf7e6..82c5999e450d 100644 --- a/firefox-ios/Client/Telemetry/GleanUsageReporting.swift +++ b/firefox-ios/Client/Telemetry/GleanUsageReporting.swift @@ -42,7 +42,7 @@ class GleanUsageReporting: GleanUsageReportingApi { class GleanLifecycleObserver { private let gleanUsageReportingApi: GleanUsageReportingApi private var id: TimerId? - private var isObserving: Bool = false + private var isObserving = false init(gleanUsageReportingApi: GleanUsageReportingApi = GleanUsageReporting()) { self.gleanUsageReportingApi = gleanUsageReportingApi From 594a960468ed94b852859168ec3af89e8b41299b Mon Sep 17 00:00:00 2001 From: Daniel Dervishi <58835213+DanielDervishi@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:24:56 -0500 Subject: [PATCH 17/45] Bugfix FXIOS-11054 [Bookmarks Evolution] "..." disclosure icon is themed incorrectly in private browsing (#24139) Finished ticket --- firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift b/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift index cdf7bfc09c77..3f5d742e9433 100644 --- a/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift +++ b/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift @@ -195,7 +195,8 @@ class OneLineTableViewCell: UITableViewCell, if isButton { let button = accessoryView as? UIButton var buttonConfig = button?.configuration - let image = buttonConfig?.image?.createScaled(CGSize(width: iconSize, height: iconSize)) + let image = buttonConfig?.image?.createScaled( + CGSize(width: iconSize, height: iconSize)).withRenderingMode(.alwaysTemplate) buttonConfig?.image = image button?.configuration = buttonConfig } From d7e840c191010aba16a1cf84423299aa6186ff27 Mon Sep 17 00:00:00 2001 From: Isabella Date: Tue, 14 Jan 2025 14:59:03 -0600 Subject: [PATCH 18/45] Refactor FXIOS-XXXX Annotate some old FIXME notes with tracked tickets (#24131) Annotate some old FIXME notes with tracked tickets. --- .../SiteImageView/ImageProcessing/ImageHandler.swift | 4 ++-- firefox-ios/Storage/DefaultSuggestedSites.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BrowserKit/Sources/SiteImageView/ImageProcessing/ImageHandler.swift b/BrowserKit/Sources/SiteImageView/ImageProcessing/ImageHandler.swift index c2c7422b61f3..e0c8ed7e3e05 100644 --- a/BrowserKit/Sources/SiteImageView/ImageProcessing/ImageHandler.swift +++ b/BrowserKit/Sources/SiteImageView/ImageProcessing/ImageHandler.swift @@ -141,8 +141,8 @@ class DefaultImageHandler: ImageHandler { } let image = try await letterImageGenerator.generateLetterImage(siteString: siteString) - // FIXME Do we really want to cache letter icons and never attempt to get a favicon again? - // We can drop into here on a network timeout. + // FIXME FXIOS-11063 Do we really want to cache letter icons and never attempt to get a favicon again? + // We can drop into here on a network timeout. await imageCache.cacheImage(image: image, cacheKey: imageModel.cacheKey, type: imageModel.imageType) return image } catch { diff --git a/firefox-ios/Storage/DefaultSuggestedSites.swift b/firefox-ios/Storage/DefaultSuggestedSites.swift index 698d6aa1c3e4..40e296758b30 100644 --- a/firefox-ios/Storage/DefaultSuggestedSites.swift +++ b/firefox-ios/Storage/DefaultSuggestedSites.swift @@ -84,12 +84,12 @@ open class DefaultSuggestedSites { ) ) ], - "zh_CN": [ // FIXME Do we still want this as a special case localization? Android doesn't compile this vers. anymore + "zh_CN": [ // FXIOS-11064 Do we still want this as a special case localization? Android doesn't compile this anymore SuggestedSite( url: "http://mozilla.com.cn", title: "火狐社区", trackingId: 700, - // FIXME We need a higher quality favicon link + // FXIOS-11064 We need a higher quality favicon link faviconResource: .remoteURL(url: URL(string: "http://mozilla.com.cn/favicon.ico")!) ), SuggestedSite( @@ -125,7 +125,7 @@ open class DefaultSuggestedSites { """, title: "京东", trackingId: 705, - // FIXME We need a higher quality favicon link + // FXIOS-11064 We need a higher quality favicon link faviconResource: .remoteURL(url: URL(string: "https://corporate.jd.com/favicon.ico")!) ) ] From 7ce94a17cbc5a700688533cbf957adcc2f7a9b4a Mon Sep 17 00:00:00 2001 From: Matt Lichtenstein Date: Tue, 14 Jan 2025 18:12:22 -0500 Subject: [PATCH 19/45] Add FXIOS-11070 [Bookmarks Evolution] Record missing telemetry (#24141) --- .../Views/BrowserViewController.swift | 9 ++------- .../Library/Bookmarks/Utitlity/BookmarksSaver.swift | 5 +++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift index c481d16408b2..39c295f9ed81 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift @@ -1749,6 +1749,8 @@ class BrowserViewController: UIViewController, internal func openBookmarkEditPanel() { guard !profile.isShutdown else { return } + TelemetryWrapper.recordEvent(category: .action, method: .change, object: .bookmark, value: .addBookmarkToast) + // Open refactored bookmark edit view if isBookmarkRefactorEnabled { guard let url = tabManager.selectedTab?.url else { return } @@ -1762,13 +1764,6 @@ class BrowserViewController: UIViewController, } // Open legacy bookmark edit view } else { - TelemetryWrapper.recordEvent( - category: .action, - method: .change, - object: .bookmark, - value: .addBookmarkToast - ) - // Fetch the last added bookmark in the mobile folder, which is the default location for all bookmarks // added on mobile when the bookmark refactor is not enabled profile.places.getBookmarksTree( diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/BookmarksSaver.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/BookmarksSaver.swift index abf3c11f571d..118b11d776af 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/BookmarksSaver.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/BookmarksSaver.swift @@ -55,6 +55,11 @@ struct DefaultBookmarksSaver: BookmarksSaver, BookmarksRefactorFeatureFlagProvid guard let folder = bookmark as? BookmarkFolderData else { return deferMaybe(nil) } if folder.parentGUID == nil { + TelemetryWrapper.recordEvent(category: .action, + method: .tap, + object: .bookmark, + value: .bookmarkAddFolder) + let position: UInt32? = parentFolderGUID == BookmarkRoots.MobileFolderGUID ? 0 : nil return profile.places.createFolder(parentGUID: parentFolderGUID, title: folder.title, From fc0ddcc0b1cc8a4e42e34b0768882aa23c9d0688 Mon Sep 17 00:00:00 2001 From: Matt Lichtenstein Date: Tue, 14 Jan 2025 18:40:51 -0500 Subject: [PATCH 20/45] Add FXIOS-11033 [Bookmarks Evolution] A11y button traits for tableview cells (#24086) --- .../Frontend/Library/Bookmarks/BookmarksViewController.swift | 1 + .../Bookmarks/Edit Bookmark/EditBookmarkViewController.swift | 1 + .../Library/Bookmarks/Edit Folder/EditFolderViewController.swift | 1 + 3 files changed, 3 insertions(+) diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift index c092b9263a9b..441429d0f238 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift @@ -507,6 +507,7 @@ class BookmarksViewController: SiteTableViewController, viewModel.accessoryView = contextButton } + cell.accessibilityTraits = .button cell.configure(viewModel: viewModel) cell.applyTheme(theme: currentTheme()) return cell diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewController.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewController.swift index 05d5bbf37cea..b8c324c60f46 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewController.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewController.swift @@ -237,6 +237,7 @@ class EditBookmarkViewController: UIViewController, let canShowAccessoryView = viewModel.shouldShowDisclosureIndicator(isFolderSelected: isFolderSelected) cell.accessoryType = canShowAccessoryView ? .checkmark : .none cell.selectionStyle = .default + cell.accessibilityTraits = .button cell.customization = .regular cell.applyTheme(theme: theme) } diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Folder/EditFolderViewController.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Folder/EditFolderViewController.swift index b1fdc293c174..cccfdc3bbe02 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Folder/EditFolderViewController.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Folder/EditFolderViewController.swift @@ -207,6 +207,7 @@ class EditFolderViewController: UIViewController, let canShowAccessoryView = viewModel.shouldShowDisclosureIndicator(isFolderSelected: isFolderSelected) cell.accessoryType = canShowAccessoryView ? .checkmark : .none cell.selectionStyle = .default + cell.accessibilityTraits = .button cell.customization = .regular cell.applyTheme(theme: theme) } From 476a828da38ff5c3ce3ab0edabbe7b5dcfac3952 Mon Sep 17 00:00:00 2001 From: Ione Souza Junior Date: Wed, 15 Jan 2025 09:03:03 -0300 Subject: [PATCH 21/45] Refactor FXIOS-7301 [Swiftlint] Remove 1 closure_body_length violation from TopSitesSectionState.swift and decrease threshold (#24128) * Remove closure body violation from TopSiteSectionState * Decrease closure_body treshold --- .swiftlint.yml | 4 +- .../TopSites/TopSitesSectionState.swift | 126 ++++++++++-------- 2 files changed, 73 insertions(+), 57 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index 92e874d0e960..e3104565b5b6 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -102,8 +102,8 @@ line_length: ignores_interpolated_strings: true closure_body_length: - warning: 64 - error: 64 + warning: 60 + error: 60 file_header: required_string: | diff --git a/firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSitesSectionState.swift b/firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSitesSectionState.swift index b30ebd94430f..fe7dc5ed29bd 100644 --- a/firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSitesSectionState.swift +++ b/firefox-ios/Client/Frontend/Home/Homepage Rebuild/TopSites/TopSitesSectionState.swift @@ -54,69 +54,85 @@ struct TopSitesSectionState: StateType, Equatable { switch action.actionType { case TopSitesMiddlewareActionType.retrievedUpdatedSites: - guard let topSitesAction = action as? TopSitesAction, - let sites = topSitesAction.topSites - else { - return defaultState(from: state) - } - let numberOfTilesPerRow = topSitesAction.numberOfTilesPerRow ?? state.numberOfTilesPerRow - let filteredSites = filter(sites: sites, with: state.numberOfRows, and: numberOfTilesPerRow) - return TopSitesSectionState( - windowUUID: state.windowUUID, - topSitesData: filteredSites, - numberOfRows: state.numberOfRows, - numberOfTilesPerRow: numberOfTilesPerRow, - shouldShowSection: !filteredSites.isEmpty && state.shouldShowSection - ) + return handleRetrievedUpdatedSitesAction(action: action, state: state) case TopSitesActionType.updatedNumberOfRows: - guard let topSitesAction = action as? TopSitesAction, - let numberOfRows = topSitesAction.numberOfRows - else { - return defaultState(from: state) - } - - let filteredSites = filter(sites: state.topSitesData, with: numberOfRows, and: state.numberOfTilesPerRow) - return TopSitesSectionState( - windowUUID: state.windowUUID, - topSitesData: filteredSites, - numberOfRows: numberOfRows, - numberOfTilesPerRow: state.numberOfTilesPerRow, - shouldShowSection: state.shouldShowSection - ) + return handleUpdatedNumberOfRowsAction(action: action, state: state) case TopSitesActionType.updatedNumberOfTilesPerRow: - guard let topSitesAction = action as? TopSitesAction, - let numberOfTilesPerRow = topSitesAction.numberOfTilesPerRow - else { - return defaultState(from: state) - } - - let filteredSites = filter(sites: state.topSitesData, with: state.numberOfRows, and: numberOfTilesPerRow) - return TopSitesSectionState( - windowUUID: state.windowUUID, - topSitesData: filteredSites, - numberOfRows: state.numberOfRows, - numberOfTilesPerRow: numberOfTilesPerRow, - shouldShowSection: state.shouldShowSection - ) + return handleUpdatedNumberOfTilesPerRowAction(action: action, state: state) case TopSitesActionType.toggleShowSectionSetting: - guard let topSitesAction = action as? TopSitesAction, - let isEnabled = topSitesAction.isEnabled - else { - return defaultState(from: state) - } - - return TopSitesSectionState( - windowUUID: state.windowUUID, - topSitesData: state.topSitesData, - numberOfRows: state.numberOfRows, - numberOfTilesPerRow: state.numberOfTilesPerRow, - shouldShowSection: isEnabled - ) + return handleToggleShowSectionSettingAction(action: action, state: state) default: return defaultState(from: state) } } + private static func handleRetrievedUpdatedSitesAction(action: Action, state: Self) -> TopSitesSectionState { + guard let topSitesAction = action as? TopSitesAction, + let sites = topSitesAction.topSites + else { + return defaultState(from: state) + } + let numberOfTilesPerRow = topSitesAction.numberOfTilesPerRow ?? state.numberOfTilesPerRow + let filteredSites = filter(sites: sites, with: state.numberOfRows, and: numberOfTilesPerRow) + return TopSitesSectionState( + windowUUID: state.windowUUID, + topSitesData: filteredSites, + numberOfRows: state.numberOfRows, + numberOfTilesPerRow: numberOfTilesPerRow, + shouldShowSection: !filteredSites.isEmpty && state.shouldShowSection + ) + } + + private static func handleUpdatedNumberOfRowsAction(action: Action, state: Self) -> TopSitesSectionState { + guard let topSitesAction = action as? TopSitesAction, + let numberOfRows = topSitesAction.numberOfRows + else { + return defaultState(from: state) + } + + let filteredSites = filter(sites: state.topSitesData, with: numberOfRows, and: state.numberOfTilesPerRow) + return TopSitesSectionState( + windowUUID: state.windowUUID, + topSitesData: filteredSites, + numberOfRows: numberOfRows, + numberOfTilesPerRow: state.numberOfTilesPerRow, + shouldShowSection: state.shouldShowSection + ) + } + + private static func handleUpdatedNumberOfTilesPerRowAction(action: Action, state: Self) -> TopSitesSectionState { + guard let topSitesAction = action as? TopSitesAction, + let numberOfTilesPerRow = topSitesAction.numberOfTilesPerRow + else { + return defaultState(from: state) + } + + let filteredSites = filter(sites: state.topSitesData, with: state.numberOfRows, and: numberOfTilesPerRow) + return TopSitesSectionState( + windowUUID: state.windowUUID, + topSitesData: filteredSites, + numberOfRows: state.numberOfRows, + numberOfTilesPerRow: numberOfTilesPerRow, + shouldShowSection: state.shouldShowSection + ) + } + + private static func handleToggleShowSectionSettingAction(action: Action, state: Self) -> TopSitesSectionState { + guard let topSitesAction = action as? TopSitesAction, + let isEnabled = topSitesAction.isEnabled + else { + return defaultState(from: state) + } + + return TopSitesSectionState( + windowUUID: state.windowUUID, + topSitesData: state.topSitesData, + numberOfRows: state.numberOfRows, + numberOfTilesPerRow: state.numberOfTilesPerRow, + shouldShowSection: isEnabled + ) + } + /// Filters the top sites to be displayed in the view based on user preferences and layout configuration. /// - Parameters: /// - sites: The full list of sites fetched from the top sites manager. From b37ab186c099879e69686920f71eac0f669db342 Mon Sep 17 00:00:00 2001 From: dragosb01 <134391433+dragosb01@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:28:49 +0200 Subject: [PATCH 22/45] Add MTE-4097 - tests for share from toolbar (#24147) --- firefox-ios/Client.xcodeproj/project.pbxproj | 4 + .../ExperimentIntegrationTests.xctestplan | 1 + .../Tests/PerformanceTestPlan.xctestplan | 1 + .../Tests/Smoketest1.xctestplan | 1 + .../Tests/Smoketest2.xctestplan | 1 + .../Tests/Smoketest3.xctestplan | 1 + .../Tests/Smoketest4.xctestplan | 1 + .../Tests/SyncIntegrationTestPlan.xctestplan | 1 + .../Tests/XCUITests/ShareMenuTests.swift | 18 +- .../Tests/XCUITests/ShareToolbarTests.swift | 164 ++++++++++++++++++ 10 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareToolbarTests.swift diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 7436f400e4d0..40fe8cfdb31a 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -1223,6 +1223,7 @@ B12DDFED2A8DE825008CE9CF /* ToolbarMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12DDFEC2A8DE825008CE9CF /* ToolbarMenuTests.swift */; }; B15058812AA0A878008B7382 /* OpeningScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15058802AA0A878008B7382 /* OpeningScreenTests.swift */; }; B1664E9E2B163B7A005D4C71 /* CreditCardsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1664E9D2B163B7A005D4C71 /* CreditCardsTests.swift */; }; + B1B95C622D3680DB00FD1337 /* ShareToolbarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B95C612D3680DB00FD1337 /* ShareToolbarTests.swift */; }; B1CA62822C0DB43600D31625 /* MultiWindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */; }; B1E156A62CD5080E009DF9E5 /* AddressesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E156A52CD5080E009DF9E5 /* AddressesTests.swift */; }; B1F90EC12BB3F6B600A4D431 /* ZoomingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F90EC02BB3F6B600A4D431 /* ZoomingTests.swift */; }; @@ -8337,6 +8338,7 @@ B1864C62B7F68815AA1FE0C2 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/HistoryPanel.strings; sourceTree = ""; }; B18D4F2291BA5459431EDCA9 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Shared.strings"; sourceTree = ""; }; B19648D5B3667FE0CB25D069 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = my.lproj/Menu.strings; sourceTree = ""; }; + B1B95C612D3680DB00FD1337 /* ShareToolbarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareToolbarTests.swift; sourceTree = ""; }; B1C044CA95EB3F5258D2EC51 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiWindowTests.swift; sourceTree = ""; }; B1D14C77AD55FE126B7904D8 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = oc; path = oc.lproj/PrivateBrowsing.strings; sourceTree = ""; }; @@ -10868,6 +10870,7 @@ B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */, 78F28FBF2CB81FDF00DA862E /* InactiveTabsTest.swift */, B126B8A92D2FFD93002F4EFC /* ShareMenuTests.swift */, + B1B95C612D3680DB00FD1337 /* ShareToolbarTests.swift */, ); path = XCUITests; sourceTree = ""; @@ -16127,6 +16130,7 @@ 39C261CC2018DE21009D97BD /* FxScreenGraphTests.swift in Sources */, 787EDD852943EE75002B93AE /* JumpBackInTests.swift in Sources */, B15058812AA0A878008B7382 /* OpeningScreenTests.swift in Sources */, + B1B95C622D3680DB00FD1337 /* ShareToolbarTests.swift in Sources */, B1F90EC12BB3F6B600A4D431 /* ZoomingTests.swift in Sources */, 8AEAD9F92C3DB0CD001A2C5A /* MicrosurveyTests.swift in Sources */, E143BF652CE36FFD00A1D2D9 /* TabTrayTests.swift in Sources */, diff --git a/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan b/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan index 80b7ba508d95..6f5566a844bb 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan @@ -99,6 +99,7 @@ "ScreenGraphTest", "SearchSettingsUITests", "ShareMenuTests", + "ShareToolbarTests", "SiteLoadTest", "SyncUITests", "ThirdPartySearchTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan b/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan index a1656be13805..17e776398330 100644 --- a/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan @@ -81,6 +81,7 @@ "SearchTests", "SettingsTests", "ShareMenuTests", + "ShareToolbarTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan index e6818ebd58eb..ca7dd5b387f9 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan @@ -210,6 +210,7 @@ "SearchTests\/testSearchWithFirefoxOption()", "SettingsTests", "ShareMenuTests", + "ShareToolbarTests", "SiteLoadTest", "SyncUITests", "SyncUITests\/testCreateAnAccountLink()", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan index 3850b95c79a0..a3994a4f2281 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan @@ -110,6 +110,7 @@ "SettingsTests\/testOpenMailAppSettings()", "SettingsTests\/testOpenSiriOption()", "ShareMenuTests", + "ShareToolbarTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan index d375172210b2..59385dbdd83e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan @@ -145,6 +145,7 @@ "SettingsTests\/testOpenSiriOption()", "SettingsTests\/testSettingsOptionSubtitles()", "ShareMenuTests", + "ShareToolbarTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan index 7a6298d3e000..2b332749a5f7 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan @@ -150,6 +150,7 @@ "SearchTests\/testSearchWithFirefoxOption()", "SettingsTests", "ShareMenuTests", + "ShareToolbarTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan b/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan index d54a8bd78b50..701c813d6f9a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan @@ -99,6 +99,7 @@ "SearchTests", "SettingsTests", "ShareMenuTests", + "ShareToolbarTests", "SiteLoadTest", "SyncUITests", "TabCounterTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift index 05a710ebcd9e..7db984f829fb 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift @@ -71,14 +71,16 @@ class ShareMenuTests: BaseTestCase { // https://mozilla.testrail.io/index.php?/cases/view/2864073 func testShareWebsiteReaderModeReminders() { - reachReaderModeShareMenuLayoutAndSelectOption(option: "Reminders") - // The URL of the website is added in a new reminder - waitForElementsToExist( - [ - app.navigationBars["Reminders"], - app.links.elementContainingText("test-mozilla-book.html") - ] - ) + if #available(iOS 17, *) { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Reminders") + // The URL of the website is added in a new reminder + waitForElementsToExist( + [ + app.navigationBars["Reminders"], + app.links.elementContainingText("test-mozilla-book.html") + ] + ) + } } // https://mozilla.testrail.io/index.php?/cases/view/2864082 diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareToolbarTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareToolbarTests.swift new file mode 100644 index 000000000000..39b817451dab --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareToolbarTests.swift @@ -0,0 +1,164 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +class ShareToolbarTests: BaseTestCase { + // https://mozilla.testrail.io/index.php?/cases/view/2864270 + func testShareNormalWebsiteTabReminders() { + if #available(iOS 17, *) { + tapToolbarShareButtonAndSelectOption(option: "Reminders") + // The URL of the website is added in a new reminder + waitForElementsToExist( + [ + app.navigationBars["Reminders"], + app.links["http://" + url_3] + ] + ) + } + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864279 + func testShareNormalWebsitePrint() { + tapToolbarShareButtonAndSelectOption(option: "Print") + // The Print dialog appears + waitForElementsToExist( + [ + app.staticTexts["Printer"], + app.staticTexts["Copies"], + app.staticTexts["Paper Size"], + app.staticTexts["Letter"], + app.staticTexts["Orientation"], + app.staticTexts["Layout"] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864277 + func testShareNormalWebsiteSendLinkToDevice() { + tapToolbarShareButtonAndSelectOption(option: "Send Link to Device") + // If not signed in, the browser prompts you to sign in + waitForElementsToExist( + [ + app.staticTexts["You are not signed in to your account."], + app.staticTexts["Please open Firefox, go to Settings and sign in to continue."] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864278 + func testShareNormalWebsiteMarkup() { + tapToolbarShareButtonAndSelectOption(option: "Markup") + // The Markup tool opens + waitForElementsToExist( + [ + app.buttons["Undo"], + app.buttons["Redo"], + app.buttons["autofill"], + app.buttons["Done"], + app.buttons["Color picker"] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864276 + func testShareNormalWebsiteCopyUrl() { + tapToolbarShareButtonAndSelectOption(option: "Copy") + openNewTabAndValidateURLisPaste(url: url_3) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864301 + func testShareWebsiteReaderModeReminders() { + if #available(iOS 17, *) { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Reminders") + // The URL of the website is added in a new reminder + waitForElementsToExist( + [ + app.navigationBars["Reminders"], + app.links.elementContainingText("test-mozilla-book.html") + ] + ) + } + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864310 + func testShareWebsiteReaderModePrint() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Print") + // The Print dialog appears + waitForElementsToExist( + [ + app.staticTexts["Printer"], + app.staticTexts["Copies"], + app.staticTexts["Paper Size"], + app.staticTexts["Letter"], + app.staticTexts["Orientation"], + app.staticTexts["Layout"] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864307 + func testShareWebsiteReaderModeCopy() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Copy") + openNewTabAndValidateURLisPaste(url: "test-mozilla-book.html") + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864308 + func testShareWebsiteReaderModeSendLink() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Send Link to Device") + // If not signed in, the browser prompts you to sign in + waitForElementsToExist( + [ + app.staticTexts["You are not signed in to your account."], + app.staticTexts["Please open Firefox, go to Settings and sign in to continue."] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864309 + func testShareWebsiteReaderModeMarkup() { + reachReaderModeShareMenuLayoutAndSelectOption(option: "Markup") + // The Markup tool opens + waitForElementsToExist( + [ + app.buttons["Undo"], + app.buttons["Redo"], + app.buttons["autofill"], + app.buttons["Done"], + app.buttons["Color picker"] + ] + ) + } + + private func reachReaderModeShareMenuLayoutAndSelectOption(option: String) { + navigator.openURL(path(forTestPage: "test-mozilla-book.html")) + waitUntilPageLoad() + navigator.nowAt(BrowserTab) + mozWaitForElementToNotExist(app.staticTexts["Fennec pasted from XCUITests-Runner"]) + app.buttons["Reader View"].waitAndTap() + app.buttons[AccessibilityIdentifiers.Toolbar.shareButton].waitAndTap() + app.collectionViews.cells[option].waitAndTap() + } + + private func tapToolbarShareButtonAndSelectOption(option: String) { + navigator.openURL(url_3) + waitUntilPageLoad() + app.buttons[AccessibilityIdentifiers.Toolbar.shareButton].waitAndTap() + app.collectionViews.cells[option].waitAndTap() + } + + private func openNewTabAndValidateURLisPaste(url: String) { + app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() + if #available(iOS 17, *) { + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 1.5) + } else { + navigator.performAction(Action.CloseURLBarOpen) + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 2) + } + mozWaitForElementToExist(app.tables["Context Menu"]) + app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].waitAndTap() + let urlBar = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] + mozWaitForValueContains(urlBar, value: url) + } +} From dfa4fd05bdfc13d92912446fe57f81bb429d1934 Mon Sep 17 00:00:00 2001 From: Clare So <1740517+clarmso@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:00:39 -0500 Subject: [PATCH 23/45] Bugfix MTE-4100 Update file path for URLExtensions.swift (#24146) Update file path for URLExtensions.swift --- test-fixtures/ci/uri_update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-fixtures/ci/uri_update.py b/test-fixtures/ci/uri_update.py index be61fee4e57e..586daae87307 100644 --- a/test-fixtures/ci/uri_update.py +++ b/test-fixtures/ci/uri_update.py @@ -24,7 +24,7 @@ CONFIG = { "URI_WEBSITE": "https://www.iana.org/assignments/uri-schemes/uri-schemes-1.csv", - "IOS_URI_PATH": "firefox-ios/Shared/Extensions/", + "IOS_URI_PATH": "BrowserKit/Sources/Shared/Extensions/", "IOS_URIS_FILE": "URLExtensions.swift", "RETRIES": 2, "BACKOFF_FACTOR": 0.3, From 76c8df44c2b8b5cfb0fc5473d33356895bfb5423 Mon Sep 17 00:00:00 2001 From: Litianu Razvan Date: Wed, 15 Jan 2025 19:13:01 +0200 Subject: [PATCH 24/45] Add FXIOS-10905 Focus iOS: DAU Ping Setting (#24154) --- .../Controller/SettingsViewController.swift | 31 +++++++++++++------ .../Blockzilla/UIComponents/UIConstants.swift | 11 +++++-- .../Blockzilla/Utilities/SupportUtils.swift | 3 ++ focus-ios/Shared/Settings.swift | 2 ++ 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift b/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift index 6ff8286c289d..488d4f9fcde6 100644 --- a/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift +++ b/focus-ios/Blockzilla/Settings/Controller/SettingsViewController.swift @@ -14,7 +14,7 @@ import DesignSystem class SettingsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { enum Section: String { - case defaultBrowser, general, privacy, usageData, crashReports, studies, search, siri, integration, mozilla, secret + case defaultBrowser, general, privacy, usageData, crashReports, studies, dailyUsagePing, search, siri, integration, mozilla, secret var headerText: String? { switch self { @@ -29,6 +29,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi case .mozilla: return UIConstants.strings.toggleSectionMozilla case .secret: return nil case .crashReports: return nil + case .dailyUsagePing: return nil } } @@ -39,17 +40,13 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi .privacy, .usageData, .studies, - .crashReports - ] - - // FXIOS-10900 Add the TOS section to the list of sections - - sections.append(contentsOf: [ + .dailyUsagePing, + .crashReports, .search, .siri, integration, .mozilla - ]) + ] if Settings.getToggle(.displaySecretMenu) { sections.append(.secret) @@ -130,6 +127,11 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi setting: SettingsToggle.crashToggle, subtitle: UIConstants.strings.detailTextCrashReportsV2 ) + let dailyUsageToggle = BlockerToggle( + label: UIConstants.strings.labelDailyUsagePing, + setting: SettingsToggle.dailyUsagePing, + subtitle: UIConstants.strings.detailTextDailyUsagePing + ) let searchSuggestionSubtitle = String(format: UIConstants.strings.detailTextSearchSuggestion, AppInfo.productName) let searchSuggestionToggle = BlockerToggle(label: UIConstants.strings.settingsSearchSuggestions, setting: SettingsToggle.enableSearchSuggestions, subtitle: searchSuggestionSubtitle) let safariToggle = BlockerToggle(label: UIConstants.strings.toggleSafari, setting: SettingsToggle.safari) @@ -148,6 +150,9 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi if let studiesIndex = getSectionIndex(Section.studies) { toggles[studiesIndex] = [0: studiesToggle] } + if let dailyUsageIndex = getSectionIndex(.dailyUsagePing) { + toggles[dailyUsageIndex] = [0: dailyUsageToggle] + } if let crashIndex = getSectionIndex(.crashReports) { toggles[crashIndex] = [0: crashToggle] } @@ -384,6 +389,8 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi cell.textLabel?.text = "Internal Settings" case .crashReports: cell = setupToggleCell(indexPath: indexPath, navigationController: navigationController) + case .dailyUsagePing: + cell = setupToggleCell(indexPath: indexPath, navigationController: navigationController) } cell.textLabel?.textColor = .primaryText @@ -408,6 +415,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi case .mozilla: return 3 case .secret: return 1 case .crashReports: return 1 + case .dailyUsagePing: return 1 } } @@ -432,7 +440,8 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi getSectionIndex(.usageData): #selector(tappedLearnMoreFooter), getSectionIndex(.search): #selector(tappedLearnMoreSearchSuggestionsFooter), getSectionIndex(.studies): #selector(tappedLearnMoreStudies), - getSectionIndex(.crashReports): #selector(tappedLearnMoreCrashReports) + getSectionIndex(.crashReports): #selector(tappedLearnMoreCrashReports), + getSectionIndex(.dailyUsagePing): #selector(tappedLearnMoreDailyUsagePing) ] if let selector = learnMoreActions[section] { let tapGesture = UITapGestureRecognizer(target: self, action: selector) @@ -548,6 +557,10 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi @objc func tappedLearnMoreCrashReports() { tappedFooter(forSupportTopic: .mobileCrashReports) } + + @objc func tappedLearnMoreDailyUsagePing() { + tappedFooter(forSupportTopic: .usagePingSettingsMobile) + } @objc private func dismissSettings() { diff --git a/focus-ios/Blockzilla/UIComponents/UIConstants.swift b/focus-ios/Blockzilla/UIComponents/UIConstants.swift index 75645a3be5ab..52400233e58e 100644 --- a/focus-ios/Blockzilla/UIComponents/UIConstants.swift +++ b/focus-ios/Blockzilla/UIComponents/UIConstants.swift @@ -199,10 +199,8 @@ struct UIConstants { static let labelBlockFonts = NSLocalizedString("Settings.toggleBlockFonts", value: "Block web fonts", comment: "Label for toggle on main screen") static let labelSendAnonymousUsageData = NSLocalizedString("Settings.toggleSendUsageData", value: "Send usage data", comment: "Label for Send Usage Data toggle on main screen") static let detailTextSendUsageData = NSLocalizedString("Settings.detailTextSendUsageData", value: "Mozilla strives to collect only what we need to provide and improve %@ for everyone.", comment: "Description associated to the Send Usage Data toggle on main screen. %@ is the app name (Focus/Klar)") - static let labelStudies = NSLocalizedString("Settings.toggleStudies", value: "Studies", comment: "Label for Studies toggle on the settings screen") static let detailTextStudies = NSLocalizedString("Settings.detailTextStudies", value: "%@ may install and run studies from time to time.", comment: "Description associated to the Studies toggle on the settings screen. %@ is the app name (Focus/Klar)") - static let labelCrashReports = NSLocalizedString( "Settings.toggleCrashReports", value: "Automatically Send Crash Reports", @@ -213,7 +211,14 @@ struct UIConstants { value: "This helps us diagnose and fix issues with the browser.", comment: "Description associated with the Crash Reports toggle on settings screen" ) - + static let labelDailyUsagePing = NSLocalizedString( + "Settings.DailyUsagePing.Title", + value: "Daily Usage Ping", + comment: "On the Settings screen, this is the title text for a toggle which controls automatically sending daily usage ping.") + static let detailTextDailyUsagePing = NSLocalizedString( + "Settings.DailyUsagePing.Message", + value: "This helps Mozilla to estimate active users.", + comment: "On the Settings screen, this is the subtitle text for a toggle which controls sending daily usage ping. Placeholder will be replaced the company name of Mozilla.") static let general = NSLocalizedString("Settings.general", value: "General", comment: "Title for section in settings menu") static let theme = NSLocalizedString("Settings.theme", value: "Theme", comment: "Theme section in settings menu") static let systemTheme = NSLocalizedString("Settings.systemTheme", value: "System Theme", comment: "System value for theme section in settings menu") diff --git a/focus-ios/Blockzilla/Utilities/SupportUtils.swift b/focus-ios/Blockzilla/Utilities/SupportUtils.swift index 64b4bbd9d1e0..a685fe40ac66 100644 --- a/focus-ios/Blockzilla/Utilities/SupportUtils.swift +++ b/focus-ios/Blockzilla/Utilities/SupportUtils.swift @@ -12,6 +12,7 @@ public enum SupportTopic: CaseIterable { case trackingProtection case addSearchEngine case mobileCrashReports + case usagePingSettingsMobile public var slug: String { switch self { @@ -29,6 +30,8 @@ public enum SupportTopic: CaseIterable { return "add-search-engine-ios" case .mobileCrashReports: return "mobile-crash-reports" + case .usagePingSettingsMobile: + return "usage-ping-settings-mobile" } } diff --git a/focus-ios/Shared/Settings.swift b/focus-ios/Shared/Settings.swift index 8d13180d07b3..77b5a24fc7c0 100644 --- a/focus-ios/Shared/Settings.swift +++ b/focus-ios/Shared/Settings.swift @@ -15,6 +15,7 @@ enum SettingsToggle: String, Equatable { case showHomeScreenTips = "HomeScreenTips" case safari = "Safari" case sendAnonymousUsageData = "SendAnonymousUsageData" + case dailyUsagePing = "DailyUsagePing" case studies = "Studies" case crashToggle = "CrashToggle" case enableDomainAutocomplete = "enableDomainAutocomplete" @@ -66,6 +67,7 @@ struct Settings { case .enableSearchSuggestions: return false case .displaySecretMenu: return false case .crashToggle: return true + case .dailyUsagePing: return true } } From bd3d42bc03848e8bb45eff60f15bc17c3fc44561 Mon Sep 17 00:00:00 2001 From: Matt Lichtenstein Date: Wed, 15 Jan 2025 13:14:21 -0500 Subject: [PATCH 25/45] Refactor FXIOS-11076 [Swiftlint] Fix closure line length error in BookmarksSaver.swift (#24158) --- .../Bookmarks/Utitlity/BookmarksSaver.swift | 100 ++++++++++-------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/BookmarksSaver.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/BookmarksSaver.swift index 118b11d776af..0c7dd9685985 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/BookmarksSaver.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/BookmarksSaver.swift @@ -29,54 +29,9 @@ struct DefaultBookmarksSaver: BookmarksSaver, BookmarksRefactorFeatureFlagProvid let operation: Deferred>? = { switch bookmark.type { case .bookmark: - guard let bookmark = bookmark as? BookmarkItemData else { return deferMaybe(nil) } - - if bookmark.parentGUID == nil { - let position: UInt32? = parentFolderGUID == BookmarkRoots.MobileFolderGUID ? 0 : nil - return profile.places.createBookmark(parentGUID: parentFolderGUID, - url: bookmark.url, - title: bookmark.title, - position: position).bind { result in - return result.isFailure ? deferMaybe(BookmarkDetailPanelError()) - : deferMaybe(result.successValue) - } - } else { - let position: UInt32? = parentFolderGUID == bookmark.parentGUID ? bookmark.position : nil - return profile.places.updateBookmarkNode(guid: bookmark.guid, - parentGUID: parentFolderGUID, - position: position, - title: bookmark.title, - url: bookmark.url).bind { result in - return result.isFailure ? deferMaybe(BookmarkDetailPanelError()) : deferMaybe(nil) - } - } - + return saveBookmark(bookmark: bookmark, parentFolderGUID: parentFolderGUID) case .folder: - guard let folder = bookmark as? BookmarkFolderData else { return deferMaybe(nil) } - - if folder.parentGUID == nil { - TelemetryWrapper.recordEvent(category: .action, - method: .tap, - object: .bookmark, - value: .bookmarkAddFolder) - - let position: UInt32? = parentFolderGUID == BookmarkRoots.MobileFolderGUID ? 0 : nil - return profile.places.createFolder(parentGUID: parentFolderGUID, - title: folder.title, - position: position).bind { result in - return result.isFailure ? deferMaybe(BookmarkDetailPanelError()) - : deferMaybe(result.successValue) - } - } else { - let position: UInt32? = parentFolderGUID == folder.parentGUID ? folder.position : nil - return profile.places.updateBookmarkNode( guid: folder.guid, - parentGUID: parentFolderGUID, - position: position, - title: folder.title).bind { result in - return result.isFailure ? deferMaybe(BookmarkDetailPanelError()) : deferMaybe(nil) - } - } - + return saveFolder(bookmark: bookmark, parentFolderGUID: parentFolderGUID) default: return nil } @@ -153,4 +108,55 @@ struct DefaultBookmarksSaver: BookmarksSaver, BookmarksRefactorFeatureFlagProvid let parentGuid = (isBookmarkRefactorEnabled ? recentBookmarkFolderGuid : nil) ?? BookmarkRoots.MobileFolderGUID _ = await save(bookmark: bookmarkData, parentFolderGUID: parentGuid) } + + private func saveBookmark(bookmark: FxBookmarkNode, parentFolderGUID: String) -> Deferred>? { + guard let bookmark = bookmark as? BookmarkItemData else { return deferMaybe(nil) } + + if bookmark.parentGUID == nil { + let position: UInt32? = parentFolderGUID == BookmarkRoots.MobileFolderGUID ? 0 : nil + return profile.places.createBookmark(parentGUID: parentFolderGUID, + url: bookmark.url, + title: bookmark.title, + position: position).bind { result in + return result.isFailure ? deferMaybe(BookmarkDetailPanelError()) + : deferMaybe(result.successValue) + } + } else { + let position: UInt32? = parentFolderGUID == bookmark.parentGUID ? bookmark.position : nil + return profile.places.updateBookmarkNode(guid: bookmark.guid, + parentGUID: parentFolderGUID, + position: position, + title: bookmark.title, + url: bookmark.url).bind { result in + return result.isFailure ? deferMaybe(BookmarkDetailPanelError()) : deferMaybe(nil) + } + } + } + + private func saveFolder(bookmark: FxBookmarkNode, parentFolderGUID: String) -> Deferred>? { + guard let folder = bookmark as? BookmarkFolderData else { return deferMaybe(nil) } + + if folder.parentGUID == nil { + TelemetryWrapper.recordEvent(category: .action, + method: .tap, + object: .bookmark, + value: .bookmarkAddFolder) + + let position: UInt32? = parentFolderGUID == BookmarkRoots.MobileFolderGUID ? 0 : nil + return profile.places.createFolder(parentGUID: parentFolderGUID, + title: folder.title, + position: position).bind { result in + return result.isFailure ? deferMaybe(BookmarkDetailPanelError()) + : deferMaybe(result.successValue) + } + } else { + let position: UInt32? = parentFolderGUID == folder.parentGUID ? folder.position : nil + return profile.places.updateBookmarkNode( guid: folder.guid, + parentGUID: parentFolderGUID, + position: position, + title: folder.title).bind { result in + return result.isFailure ? deferMaybe(BookmarkDetailPanelError()) : deferMaybe(nil) + } + } + } } From 4dd3b3dcaaf3dbf0604b9e563e30cc8e9049b70e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:21:06 -0500 Subject: [PATCH 26/45] Refactor [v136] Auto update SPM with latest rust-component 136.0.20250115050329 (#24155) Auto update SPM with latest rust-component release 136.0.20250115050329 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- firefox-ios/Client.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- focus-ios/Blockzilla.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 40fe8cfdb31a..11bf3817a642 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -26465,7 +26465,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift.git"; requirement = { kind = exactVersion; - version = 136.0.20250113194724; + version = 136.0.20250115050329; }; }; 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */ = { diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 42a91bedaa9e..340abfa1bacf 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/rust-components-swift.git", "state" : { - "revision" : "ce80ed1d87747988cecb49695537eeda645deb92", - "version" : "136.0.20250113194724" + "revision" : "c4465a8c1975d149886ea3e7dfc0546c703a3210", + "version" : "136.0.20250115050329" } }, { diff --git a/focus-ios/Blockzilla.xcodeproj/project.pbxproj b/focus-ios/Blockzilla.xcodeproj/project.pbxproj index 10d9f99d27f3..7bc8d08a1e2d 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.pbxproj +++ b/focus-ios/Blockzilla.xcodeproj/project.pbxproj @@ -7198,7 +7198,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift"; requirement = { kind = exactVersion; - version = 136.0.20250113194724; + version = 136.0.20250115050329; }; }; 8A0E7F2C2BA0F0E0006BC6B6 /* XCRemoteSwiftPackageReference "Fuzi" */ = { diff --git a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1c75cd78c414..a3af9f3237d8 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/mozilla/rust-components-swift", "state": { "branch": null, - "revision": "ce80ed1d87747988cecb49695537eeda645deb92", - "version": "136.0.20250113194724" + "revision": "c4465a8c1975d149886ea3e7dfc0546c703a3210", + "version": "136.0.20250115050329" } }, { From 2e04a339969b0d9accadde03263d40ebd6dd4271 Mon Sep 17 00:00:00 2001 From: Matt Lichtenstein Date: Wed, 15 Jan 2025 17:03:35 -0500 Subject: [PATCH 27/45] Localize FXIOS-11077 [Bookmarks Evolution] Clarify "Sign in to Sync" comment (#24161) --- firefox-ios/Shared/Strings.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firefox-ios/Shared/Strings.swift b/firefox-ios/Shared/Strings.swift index ef204716bf19..7d9d8034c8bc 100644 --- a/firefox-ios/Shared/Strings.swift +++ b/firefox-ios/Shared/Strings.swift @@ -217,10 +217,10 @@ extension String { value: "Save sites as you browse. We’ll also grab bookmarks from other synced devices.", comment: "The body text for the placeholder screen shown when there are no saved bookmarks, located at the root level of the bookmarks panel within the libray modal") public static let ButtonTitle = MZLocalizedString( - key: "Bookmarks.EmptyState.Root.ButtonTitle.v135", + key: "Bookmarks.EmptyState.Root.ButtonTitle.v136", tableName: "Bookmarks", value: "Sign in to Sync", - comment: "The button title for the sign in button on the placeholder screen shown when there are no saved bookmarks, located at the root level of the bookmarks panel within the library modal. This button triggers the sign in flow, allowing users to sign in to their Mozilla Account to sync data") + comment: "The button title for the sign in button on the placeholder screen shown when there are no saved bookmarks, located at the root level of the bookmarks panel within the library modal. This button triggers the sign in flow, allowing users to sign in to their Mozilla Account to sync data. In this string, \"Sync\" is used as a verb, and is capitalized as per convention to title case text for buttons in iOS") } public struct Nested { public static let Title = MZLocalizedString( From 6dfb43a357696c544b45dd9f1df91b370264a9ba Mon Sep 17 00:00:00 2001 From: mattreaganmozilla <145381717+mattreaganmozilla@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:15:47 -0800 Subject: [PATCH 28/45] Bugfix FXIOS-11034 Avoid non-MT access in `closeTab()` (#24144) [FXIOS-11034] Address tabs being accessed from background thread. Partial work towards FXIOS-11034. --- .../Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift index b052c799dd2f..2a3aef801866 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift @@ -352,6 +352,7 @@ class TabManagerMiddleware: BookmarksRefactorFeatureFlagProvider { /// - Parameters: /// - tabUUID: UUID of the tab to be closed/removed /// - Returns: If is the last tab to be closed used to trigger dismissTabTray action + @MainActor private func closeTab(with tabUUID: TabUUID, uuid: WindowUUID, isPrivate: Bool) async -> Bool { let tabManager = tabManager(for: uuid) // In non-private mode, if: From 5785bd1793948915975ef0197558fc484f7d25a4 Mon Sep 17 00:00:00 2001 From: dicarobinho <61138287+dicarobinho@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:45:49 +0200 Subject: [PATCH 29/45] =?UTF-8?q?Bugfix=20FXIOS-10658=20=E2=81=83=20[Felt?= =?UTF-8?q?=20privacy-Unified=20panel]=20-=20Incorrect=20announcement=20of?= =?UTF-8?q?=20the=20trackers=20section=20(#24156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FXIOS-10658 #23318 ⁃ [Felt privacy-Unified panel] [Accessibility] - Incorrect announcement of the trackers section * Fixed SwiftLint warnings * Fixed BookmarksSaver save method * Fixed testLockIconCloseMenu test --- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- .../Application/AccessibilityIdentifiers.swift | 9 ++++++--- .../BlockedTrackersTableController.swift | 12 ++++++++++++ .../CertificatesViewController.swift | 8 +++++++- .../TrackingProtectionModel.swift | 6 +++--- ...ackingProtectionDetailsViewController.swift | 7 +++++++ ...TrackingProtectionBlockedTrackersView.swift | 9 ++++++--- ...rackingProtectionConnectionStatusView.swift | 6 ++++-- .../TrackingProtectionToggleView.swift | 18 ++++++++++++------ .../TrackingProtectionViewController.swift | 7 +++---- firefox-ios/Shared/Strings.swift | 13 +++++++++++++ .../XCUITests/TrackingProtectionTests.swift | 4 ++-- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- 13 files changed, 79 insertions(+), 28 deletions(-) diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 340abfa1bacf..42a91bedaa9e 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/rust-components-swift.git", "state" : { - "revision" : "c4465a8c1975d149886ea3e7dfc0546c703a3210", - "version" : "136.0.20250115050329" + "revision" : "ce80ed1d87747988cecb49695537eeda645deb92", + "version" : "136.0.20250113194724" } }, { diff --git a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift index a38e463a7287..5f5a546ed4dc 100644 --- a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift +++ b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift @@ -125,9 +125,9 @@ struct AccessibilityIdentifiers { static let domainHeaderLabel = "TrackingProtection.DomainHeaderLabel" static let statusTitleLabel = "TrackingProtection.ConnectionStatusTitleLabel" static let statusBodyLabel = "TrackingProtection.ConnectionStatusBodyLabel" - static let trackersBlockedLabel = "TrackingProtection.TrackersBlockedLabel" - static let securityStatusLabel = "TrackingProtection.ConnectionSecurityStatusLabel" - static let toggleViewTitleLabel = "TrackingProtection.ToggleViewTitleLabel" + static let trackersBlockedButton = "TrackingProtection.TrackersBlockedButton" + static let securityStatusButton = "TrackingProtection.ConnectionSecurityStatusButton" + static let toggleViewLabelsContainer = "TrackingProtection.ToggleViewLabelsContainer" static let toggleViewBodyLabel = "TrackingProtection.ToggleViewBodyLabel" static let closeButton = "TrackingProtection.CloseButton" static let faviconImage = "TrackingProtection.FaviconImage" @@ -139,6 +139,9 @@ struct AccessibilityIdentifiers { static let containerView = "TrackingProtectionDetails.BaseView" static let connectionView = "TrackingProtectionDetails.ConnectionView" static let certificatesButton = "TrackingProtectionDetails.CertificatesButton" + static let closeButton = "TrackingProtectionDetails.CloseButton" + static let backButton = "TrackingProtectionDetails.BackButton" + static let titleLabel = "TrackingProtectionDetails.TitleLabel" } struct BlockedTrackers { diff --git a/firefox-ios/Client/Frontend/TrackingProtection/BlockedTrackersTableController.swift b/firefox-ios/Client/Frontend/TrackingProtection/BlockedTrackersTableController.swift index 2fa87b4a9b7f..e7c9e50e219c 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/BlockedTrackersTableController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/BlockedTrackersTableController.swift @@ -81,6 +81,7 @@ class BlockedTrackersTableViewController: UIViewController, constraints.removeAll() setupNavigationView() setupTableView() + setupAccessibilityIdentifiers() setupHeaderViewActions() NSLayoutConstraint.activate(constraints) } @@ -194,6 +195,17 @@ class BlockedTrackersTableViewController: UIViewController, } } + // MARK: Accessibility + private func setupAccessibilityIdentifiers() { + navigationView.setupAccessibility( + closeButtonA11yLabel: .Menu.EnhancedTrackingProtection.AccessibilityLabels.CloseButton, + closeButtonA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.closeButton, + titleA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.titleLabel, + backButtonA11yLabel: .Menu.EnhancedTrackingProtection.AccessibilityLabels.BackButton, + backButtonA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.backButton + ) + } + // MARK: Notifications func handleNotifications(_ notification: Notification) { switch notification.name { diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift index b283f739134f..7ae766083153 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift @@ -264,7 +264,13 @@ class CertificatesViewController: UIViewController, // MARK: Accessibility private func setupAccessibilityIdentifiers() { - // TODO: FXIOS-9829 Enhanced Tracking Protection certificates details screen accessibility identifiers + headerView.setupAccessibility( + closeButtonA11yLabel: .Menu.EnhancedTrackingProtection.AccessibilityLabels.CloseButton, + closeButtonA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.closeButton, + titleA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.titleLabel, + backButtonA11yLabel: .Menu.EnhancedTrackingProtection.AccessibilityLabels.BackButton, + backButtonA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.backButton + ) } // MARK: View Transitions diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackigProtectionRedux/TrackingProtectionModel.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackigProtectionRedux/TrackingProtectionModel.swift index 4c44e849404b..67f3566db52a 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackigProtectionRedux/TrackingProtectionModel.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackigProtectionRedux/TrackingProtectionModel.swift @@ -44,9 +44,9 @@ class TrackingProtectionModel { let domainHeaderLabelA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.domainHeaderLabel let statusTitleLabelA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.statusTitleLabel let statusBodyLabelA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.statusBodyLabel - let trackersBlockedLabelA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.trackersBlockedLabel - let securityStatusLabelA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusLabel - let toggleViewTitleLabelA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.toggleViewTitleLabel + let trackersBlockedButtonA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.trackersBlockedButton + let securityStatusButtonA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusButton + let toggleViewContainerA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.toggleViewLabelsContainer let toggleViewBodyLabelA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.toggleViewBodyLabel let closeButtonA11yId = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.closeButton let closeButtonA11yLabel = String.Menu.EnhancedTrackingProtection.closeButtonAccessibilityLabel diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionDetailsViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionDetailsViewController.swift index a8fffe17b834..55a351d87e5d 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionDetailsViewController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionDetailsViewController.swift @@ -191,6 +191,13 @@ class TrackingProtectionDetailsViewController: UIViewController, Themeable { // MARK: Accessibility private func setupAccessibilityIdentifiers() { view.accessibilityIdentifier = AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.mainView + headerView.setupAccessibility( + closeButtonA11yLabel: .Menu.EnhancedTrackingProtection.AccessibilityLabels.CloseButton, + closeButtonA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.closeButton, + titleA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.titleLabel, + backButtonA11yLabel: .Menu.EnhancedTrackingProtection.AccessibilityLabels.BackButton, + backButtonA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.backButton + ) } // MARK: View Transitions diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionBlockedTrackersView.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionBlockedTrackersView.swift index f1113b096bad..b3866ab6398a 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionBlockedTrackersView.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionBlockedTrackersView.swift @@ -24,6 +24,7 @@ final class TrackingProtectionBlockedTrackersView: UIView, ThemeApplicable { label.font = FXFontStyles.Regular.body.scaledFont() label.numberOfLines = 0 label.adjustsFontForContentSizeCategory = true + label.isAccessibilityElement = false } private let trackersDetailArrow: UIImageView = .build { image in @@ -117,7 +118,9 @@ final class TrackingProtectionBlockedTrackersView: UIView, ThemeApplicable { } func setupDetails(for trackersBlocked: Int?) { - trackersLabel.text = getTrackerString(for: trackersBlocked) + let trackersString = getTrackerString(for: trackersBlocked) + trackersButton.accessibilityLabel = trackersString + trackersLabel.text = trackersString shieldImage.image = UIImage(imageLiteralResourceName: StandardImageIdentifiers.Large.shield) .withRenderingMode(.alwaysTemplate) if let trackersBlocked { @@ -126,10 +129,10 @@ final class TrackingProtectionBlockedTrackersView: UIView, ThemeApplicable { } func setupAccessibilityIdentifiers(arrowImageA11yId: String, - trackersBlockedLabelA11yId: String, + trackersBlockedButtonA11yId: String, shieldImageA11yId: String) { trackersDetailArrow.accessibilityIdentifier = arrowImageA11yId - trackersLabel.accessibilityIdentifier = trackersBlockedLabelA11yId + trackersButton.accessibilityIdentifier = trackersBlockedButtonA11yId shieldImage.accessibilityIdentifier = shieldImageA11yId } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionConnectionStatusView.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionConnectionStatusView.swift index b2a01c91f9db..75f5396ca08e 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionConnectionStatusView.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionConnectionStatusView.swift @@ -23,6 +23,7 @@ final class TrackingProtectionConnectionStatusView: UIView, ThemeApplicable { label.font = FXFontStyles.Regular.body.scaledFont() label.numberOfLines = 0 label.adjustsFontForContentSizeCategory = true + label.isAccessibilityElement = false } private let connectionDetailArrow: UIImageView = .build { image in @@ -103,9 +104,9 @@ final class TrackingProtectionConnectionStatusView: UIView, ThemeApplicable { NSLayoutConstraint.activate(viewConstraints) } - func setupAccessibilityIdentifiers(arrowImageA11yId: String, securityStatusLabelA11yId: String) { + func setupAccessibilityIdentifiers(arrowImageA11yId: String, securityStatusButtonA11yId: String) { connectionDetailArrow.accessibilityIdentifier = arrowImageA11yId - connectionStatusLabel.accessibilityIdentifier = securityStatusLabelA11yId + connectionButton.accessibilityIdentifier = securityStatusButtonA11yId } func adjustLayout() { @@ -136,6 +137,7 @@ final class TrackingProtectionConnectionStatusView: UIView, ThemeApplicable { theme: Theme) { connectionStatusImage.image = image connectionStatusLabel.text = text + connectionButton.accessibilityLabel = text connectionStatusImage.tintColor = theme.colors.iconSecondary connectionDetailArrow.isHidden = !isConnectionSecure } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionToggleView.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionToggleView.swift index e08b1931728b..ef62f030765d 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionToggleView.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionToggleView.swift @@ -17,12 +17,14 @@ final class TrackingProtectionToggleView: UIView, ThemeApplicable { stack.alignment = .leading stack.axis = .vertical stack.spacing = TPMenuUX.UX.headerLabelDistance + stack.isAccessibilityElement = true } private let toggleLabel: UILabel = .build { label in label.font = FXFontStyles.Regular.body.scaledFont() label.numberOfLines = 0 label.adjustsFontForContentSizeCategory = true + label.isAccessibilityElement = false } private let toggleSwitch: UISwitch = .build { toggleSwitch in @@ -33,6 +35,7 @@ final class TrackingProtectionToggleView: UIView, ThemeApplicable { label.font = FXFontStyles.Regular.caption1.scaledFont() label.numberOfLines = 0 label.adjustsFontForContentSizeCategory = true + label.isAccessibilityElement = false } private var viewConstraints: [NSLayoutConstraint] = [] @@ -109,15 +112,18 @@ final class TrackingProtectionToggleView: UIView, ThemeApplicable { } func setupDetails(isOn: Bool) { - toggleSwitch.isOn = isOn - toggleLabel.text = .Menu.EnhancedTrackingProtection.switchTitle - toggleStatusLabel.text = isOn ? + let title: String = .Menu.EnhancedTrackingProtection.switchTitle + let subtitle: String = isOn ? .Menu.EnhancedTrackingProtection.switchOnText : .Menu.EnhancedTrackingProtection.switchOffText + toggleSwitch.isOn = isOn + toggleLabel.text = title + toggleStatusLabel.text = subtitle + toggleLabelsContainer.accessibilityLabel = title + toggleLabelsContainer.accessibilityHint = subtitle } - func setupAccessibilityIdentifiers(toggleViewTitleLabelA11yId: String, toggleViewBodyLabelA11yId: String) { - toggleLabel.accessibilityIdentifier = toggleViewTitleLabelA11yId - toggleStatusLabel.accessibilityIdentifier = toggleViewBodyLabelA11yId + func setupAccessibilityIdentifiers(toggleViewLabelsContainerA11yId: String) { + toggleLabelsContainer.accessibilityIdentifier = toggleViewLabelsContainerA11yId } func setupActions() { diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift index 7bcbab45576b..6226f831843e 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift @@ -496,14 +496,13 @@ class TrackingProtectionViewController: UIViewController, connectionDetailsHeaderView.setupAccessibilityIdentifiers(foxImageA11yId: model.foxImageA11yId) trackersView.setupAccessibilityIdentifiers( arrowImageA11yId: model.arrowImageA11yId, - trackersBlockedLabelA11yId: model.trackersBlockedLabelA11yId, + trackersBlockedButtonA11yId: model.trackersBlockedButtonA11yId, shieldImageA11yId: model.settingsA11yId) connectionStatusView.setupAccessibilityIdentifiers( arrowImageA11yId: model.arrowImageA11yId, - securityStatusLabelA11yId: model.securityStatusLabelA11yId) + securityStatusButtonA11yId: model.securityStatusButtonA11yId) toggleView.setupAccessibilityIdentifiers( - toggleViewTitleLabelA11yId: model.toggleViewTitleLabelA11yId, - toggleViewBodyLabelA11yId: model.toggleViewBodyLabelA11yId) + toggleViewLabelsContainerA11yId: model.toggleViewContainerA11yId) headerContainer.setupAccessibility(closeButtonA11yLabel: model.closeButtonA11yLabel, closeButtonA11yId: model.closeButtonA11yId) clearCookiesButton.accessibilityIdentifier = model.clearCookiesButtonA11yId diff --git a/firefox-ios/Shared/Strings.swift b/firefox-ios/Shared/Strings.swift index 7d9d8034c8bc..b7b3daed960d 100644 --- a/firefox-ios/Shared/Strings.swift +++ b/firefox-ios/Shared/Strings.swift @@ -4957,6 +4957,19 @@ extension String { extension String { public struct Menu { public struct EnhancedTrackingProtection { + public struct AccessibilityLabels { + public static let CloseButton = MZLocalizedString( + key: "MainMenu.Account.AccessibilityLabels.CloseButton.v137", + tableName: "EnhancedTrackingProtection", + value: "Close", + comment: "The accessibility label for the close button in the EnhancedTrackingProtection screen header navigation view.") + public static let BackButton = MZLocalizedString( + key: "MainMenu.Account.AccessibilityLabels.BackButton.v137", + tableName: "EnhancedTrackingProtection", + value: "Back", + comment: "The accessibility label for the back button in the EnhancedTrackingProtection screen header navigation view.") + } + public static let onTitle = MZLocalizedString( key: "Menu.EnhancedTrackingProtection.On.Title.v128", tableName: "EnhancedTrackingProtection", diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift index 178e3e75ebc3..ca671dda77f4 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift @@ -253,10 +253,10 @@ class TrackingProtectionTests: BaseTestCase { navigator.nowAt(BrowserTab) navigator.goto(TrackingProtectionContextMenuDetails) mozWaitForElementToExist( - app.staticTexts[AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusLabel]) + app.staticTexts[AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusButton]) navigator.performAction(Action.CloseTPContextMenu) mozWaitForElementToNotExist( - app.staticTexts[AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusLabel]) + app.staticTexts[AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusButton]) } // https://mozilla.testrail.io/index.php?/cases/view/2307063 diff --git a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a3af9f3237d8..1c75cd78c414 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/mozilla/rust-components-swift", "state": { "branch": null, - "revision": "c4465a8c1975d149886ea3e7dfc0546c703a3210", - "version": "136.0.20250115050329" + "revision": "ce80ed1d87747988cecb49695537eeda645deb92", + "version": "136.0.20250113194724" } }, { From 2540e387330b151d77b820cbc628b3cbbb9bd9e4 Mon Sep 17 00:00:00 2001 From: dragosb01 <134391433+dragosb01@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:38:26 +0200 Subject: [PATCH 30/45] Add MTE-4110 - share tests from long press home (#24173) --- firefox-ios/Client.xcodeproj/project.pbxproj | 16 +++--- .../ExperimentIntegrationTests.xctestplan | 1 + .../Tests/PerformanceTestPlan.xctestplan | 1 + .../Tests/Smoketest1.xctestplan | 1 + .../Tests/Smoketest2.xctestplan | 1 + .../Tests/Smoketest3.xctestplan | 1 + .../Tests/Smoketest4.xctestplan | 1 + .../Tests/SyncIntegrationTestPlan.xctestplan | 1 + .../Tests/XCUITests/BaseTestCase.swift | 14 +++++ .../Tests/XCUITests/ShareLongPressTests.swift | 52 +++++++++++++++++++ .../Tests/XCUITests/ShareMenuTests.swift | 22 ++------ .../Tests/XCUITests/ShareToolbarTests.swift | 25 +++------ 12 files changed, 94 insertions(+), 42 deletions(-) create mode 100644 firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareLongPressTests.swift diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 11bf3817a642..c11a17e1ceae 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -1218,6 +1218,7 @@ ABEF80D52A254185003F52C4 /* CreditCardBottomSheetFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABEF80D42A254185003F52C4 /* CreditCardBottomSheetFooterView.swift */; }; ABEF80D92A2F283E003F52C4 /* CreditCardBottomSheetViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABEF80D82A2F283D003F52C4 /* CreditCardBottomSheetViewModelTests.swift */; }; B10997432A97251D00CC8860 /* UrlBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10997422A97251D00CC8860 /* UrlBarTests.swift */; }; + B10D0B692D38F00E00925997 /* ShareLongPressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10D0B682D38F00E00925997 /* ShareLongPressTests.swift */; }; B1158F2A2B5029F200AC9D70 /* URLValidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1158F292B5029F200AC9D70 /* URLValidationTests.swift */; }; B126B8AA2D2FFD93002F4EFC /* ShareMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B126B8A92D2FFD93002F4EFC /* ShareMenuTests.swift */; }; B12DDFED2A8DE825008CE9CF /* ToolbarMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12DDFEC2A8DE825008CE9CF /* ToolbarMenuTests.swift */; }; @@ -8330,6 +8331,7 @@ B0A34563B505CCAAE8BBA211 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/LoginManager.strings; sourceTree = ""; }; B0CB471BA31B6922E598AA88 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Today.strings; sourceTree = ""; }; B10997422A97251D00CC8860 /* UrlBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlBarTests.swift; sourceTree = ""; }; + B10D0B682D38F00E00925997 /* ShareLongPressTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareLongPressTests.swift; sourceTree = ""; }; B1158F292B5029F200AC9D70 /* URLValidationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLValidationTests.swift; sourceTree = ""; }; B126B8A92D2FFD93002F4EFC /* ShareMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareMenuTests.swift; sourceTree = ""; }; B12DDFEC2A8DE825008CE9CF /* ToolbarMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarMenuTests.swift; sourceTree = ""; }; @@ -10830,12 +10832,14 @@ 2C2A91281FA2410D002E36BD /* HistoryTests.swift */, 4FB4AF7426E7E789005FDF91 /* HomeButtonTests.swift */, 2C31A8461E8D447F00DAC646 /* HomePageSettingsUITest.swift */, + 78F28FBF2CB81FDF00DA862E /* InactiveTabsTest.swift */, 3BFE4B0B1D342FB900DDF53F /* Info.plist */, 0430A544203B372D00FDF76D /* IntegrationTests.swift */, 787EDD832943EE75002B93AE /* JumpBackInTests.swift */, D4C4BDCD2253725E00986F04 /* LibraryTests.swift */, 2CCB296620A99C9500121DD8 /* LoginsTests.swift */, 8AEAD9F62C3DB0BF001A2C5A /* MicrosurveyTests.swift */, + B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */, 2CF449A41E7BFE2C00FD7595 /* NavigationTest.swift */, 2CB1A6591FDEA8B60084E96D /* NewTabSettings.swift */, 3D9CA9831EF456A8002434DD /* NightModeTests.swift */, @@ -10846,11 +10850,14 @@ 2CF21D0820A4A163000D08B7 /* PocketTests.swift */, 2C2A5EF31E68469500F02659 /* PrivateBrowsingTest.swift */, 2C31A7A81E8BFB2200DAC646 /* ReadingListTests.swift */, + E1BDAC802B9F5DE40063E6BF /* ReportSiteTests.swift */, 39012F271F8ED262002E3D31 /* ScreenGraphTest.swift */, 2CEA6F781E93E3A600D4100E /* SearchSettingsUITest.swift */, 2CA16FDD1E5F089100332277 /* SearchTest.swift */, 2C3406C71E719F00000FD889 /* SettingsTests.swift */, - E1BDAC802B9F5DE40063E6BF /* ReportSiteTests.swift */, + B10D0B682D38F00E00925997 /* ShareLongPressTests.swift */, + B126B8A92D2FFD93002F4EFC /* ShareMenuTests.swift */, + B1B95C612D3680DB00FD1337 /* ShareToolbarTests.swift */, 0BC9C9C31F26F54D000E8AB5 /* SiteLoadTest.swift */, 2CEDADA120207EC400223A89 /* SyncFAUITests.swift */, 4F2A06BD26F8E46E0017DA05 /* TabCounterTests.swift */, @@ -10863,14 +10870,10 @@ 3DEFED071F55EBE300F8620C /* TrackingProtectionTests.swift */, B10997422A97251D00CC8860 /* UrlBarTests.swift */, B1158F292B5029F200AC9D70 /* URLValidationTests.swift */, - B1F90EC02BB3F6B600A4D431 /* ZoomingTests.swift */, 3BF4B8DA1D38493300493393 /* Utils */, EB7A651020699BD200B52A5F /* WebPagesForTesting.swift */, 0B9D40781E8D5AC80059E664 /* XCUITests-Bridging-Header.h */, - B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */, - 78F28FBF2CB81FDF00DA862E /* InactiveTabsTest.swift */, - B126B8A92D2FFD93002F4EFC /* ShareMenuTests.swift */, - B1B95C612D3680DB00FD1337 /* ShareToolbarTests.swift */, + B1F90EC02BB3F6B600A4D431 /* ZoomingTests.swift */, ); path = XCUITests; sourceTree = ""; @@ -16109,6 +16112,7 @@ 2C4A07DC20246EAD0083E320 /* DragAndDropTests.swift in Sources */, 2C2A5EF41E68469500F02659 /* PrivateBrowsingTest.swift in Sources */, D4C35391283500A600F7DC7D /* PerformanceTests.swift in Sources */, + B10D0B692D38F00E00925997 /* ShareLongPressTests.swift in Sources */, 2CF449A51E7BFE2C00FD7595 /* NavigationTest.swift in Sources */, 2C2A91291FA2410D002E36BD /* HistoryTests.swift in Sources */, 580B0C4221748CFE00448DF8 /* DataManagementTests.swift in Sources */, diff --git a/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan b/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan index 6f5566a844bb..fe4dc20e1a41 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan @@ -98,6 +98,7 @@ "ReportSiteTests", "ScreenGraphTest", "SearchSettingsUITests", + "ShareLongPressTests", "ShareMenuTests", "ShareToolbarTests", "SiteLoadTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan b/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan index 17e776398330..36a79793c256 100644 --- a/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/PerformanceTestPlan.xctestplan @@ -80,6 +80,7 @@ "SearchSettingsUITests", "SearchTests", "SettingsTests", + "ShareLongPressTests", "ShareMenuTests", "ShareToolbarTests", "SiteLoadTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan index ca7dd5b387f9..e332ed821109 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan @@ -209,6 +209,7 @@ "SearchTests\/testSearchIconOnAboutHome()", "SearchTests\/testSearchWithFirefoxOption()", "SettingsTests", + "ShareLongPressTests", "ShareMenuTests", "ShareToolbarTests", "SiteLoadTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan index a3994a4f2281..bd391222251a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan @@ -109,6 +109,7 @@ "SettingsTests\/testImageOnOff()", "SettingsTests\/testOpenMailAppSettings()", "SettingsTests\/testOpenSiriOption()", + "ShareLongPressTests", "ShareMenuTests", "ShareToolbarTests", "SiteLoadTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan index 59385dbdd83e..a63a255a563c 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest3.xctestplan @@ -144,6 +144,7 @@ "SettingsTests\/testOpenMailAppSettings()", "SettingsTests\/testOpenSiriOption()", "SettingsTests\/testSettingsOptionSubtitles()", + "ShareLongPressTests", "ShareMenuTests", "ShareToolbarTests", "SiteLoadTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan index 2b332749a5f7..e277566c8b39 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan @@ -149,6 +149,7 @@ "SearchTests\/testSearchSuggestions()", "SearchTests\/testSearchWithFirefoxOption()", "SettingsTests", + "ShareLongPressTests", "ShareMenuTests", "ShareToolbarTests", "SiteLoadTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan b/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan index 701c813d6f9a..bbc614dc3dc1 100644 --- a/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/SyncIntegrationTestPlan.xctestplan @@ -98,6 +98,7 @@ "SearchSettingsUITests", "SearchTests", "SettingsTests", + "ShareLongPressTests", "ShareMenuTests", "ShareToolbarTests", "SiteLoadTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift index 811090fff3da..3dbd8d747a86 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift @@ -423,6 +423,20 @@ class BaseTestCase: XCTestCase { app.buttons["Done"].waitAndTap() } + func openNewTabAndValidateURLisPaste(url: String) { + app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() + if #available(iOS 17, *) { + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 1.5) + } else { + navigator.performAction(Action.CloseURLBarOpen) + app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 2) + } + mozWaitForElementToExist(app.tables["Context Menu"]) + app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].waitAndTap() + let urlBar = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] + mozWaitForValueContains(urlBar, value: url) + } + func waitForElementsToExist(_ elements: [XCUIElement], timeout: TimeInterval = TIMEOUT, message: String? = nil) { var elementsDict = [XCUIElement: String]() for element in elements { diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareLongPressTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareLongPressTests.swift new file mode 100644 index 000000000000..318e13e6193e --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareLongPressTests.swift @@ -0,0 +1,52 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +class ShareLongPressTests: BaseTestCase { + // https://mozilla.testrail.io/index.php?/cases/view/2864317 + func testShareNormalWebsiteTabReminders() { + if #available(iOS 17, *) { + longPressPocketAndReachShareOptions(option: "Reminders") + // The URL of the website is added in a new reminder + waitForElementsToExist( + [ + app.navigationBars["Reminders"], + app.links.elementContainingText("https://www") + ] + ) + } + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864324 + func testShareNormalWebsiteSendLinkToDevice() { + longPressPocketAndReachShareOptions(option: "Send Link to Device") + // If not signed in, the browser prompts you to sign in + waitForElementsToExist( + [ + app.staticTexts[sendLinkMsg1], + app.staticTexts[sendLinkMsg2] + ] + ) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2864323 + func testShareNormalWebsiteCopyUrl() { + longPressPocketAndReachShareOptions(option: "Copy") + app.collectionViews + .cells.matching(identifier: AccessibilityIdentifiers.FirefoxHomepage.Pocket.itemCell) + .staticTexts.firstMatch.waitAndTap() + openNewTabAndValidateURLisPaste(url: "https://www") + } + + private func longPressPocketAndReachShareOptions(option: String) { + navigator.goto(NewTabScreen) + // Long tap on the first Pocket element + app.collectionViews + .cells.matching(identifier: AccessibilityIdentifiers.FirefoxHomepage.Pocket.itemCell) + .staticTexts.firstMatch.press(forDuration: 1.5) + app.tables["Context Menu"].cells.otherElements["shareLarge"].waitAndTap() + app.collectionViews.cells[option].waitAndTap() + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift index 7db984f829fb..a070a61a07e9 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareMenuTests.swift @@ -42,8 +42,8 @@ class ShareMenuTests: BaseTestCase { // If not signed in, the browser prompts you to sign in waitForElementsToExist( [ - app.staticTexts["You are not signed in to your account."], - app.staticTexts["Please open Firefox, go to Settings and sign in to continue."] + app.staticTexts[sendLinkMsg1], + app.staticTexts[sendLinkMsg2] ] ) } @@ -111,8 +111,8 @@ class ShareMenuTests: BaseTestCase { // If not signed in, the browser prompts you to sign in waitForElementsToExist( [ - app.staticTexts["You are not signed in to your account."], - app.staticTexts["Please open Firefox, go to Settings and sign in to continue."] + app.staticTexts[sendLinkMsg1], + app.staticTexts[sendLinkMsg2] ] ) } @@ -153,18 +153,4 @@ class ShareMenuTests: BaseTestCase { navigator.performAction(Action.ShareBrowserTabMenuOption) app.collectionViews.cells[option].waitAndTap() } - - private func openNewTabAndValidateURLisPaste(url: String) { - app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() - if #available(iOS 17, *) { - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 1.5) - } else { - navigator.performAction(Action.CloseURLBarOpen) - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 2) - } - mozWaitForElementToExist(app.tables["Context Menu"]) - app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].waitAndTap() - let urlBar = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] - mozWaitForValueContains(urlBar, value: url) - } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareToolbarTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareToolbarTests.swift index 39b817451dab..19851c5c2cd1 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareToolbarTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ShareToolbarTests.swift @@ -4,6 +4,9 @@ import Foundation +let sendLinkMsg1 = "You are not signed in to your account." +let sendLinkMsg2 = "Please open Firefox, go to Settings and sign in to continue." + class ShareToolbarTests: BaseTestCase { // https://mozilla.testrail.io/index.php?/cases/view/2864270 func testShareNormalWebsiteTabReminders() { @@ -41,8 +44,8 @@ class ShareToolbarTests: BaseTestCase { // If not signed in, the browser prompts you to sign in waitForElementsToExist( [ - app.staticTexts["You are not signed in to your account."], - app.staticTexts["Please open Firefox, go to Settings and sign in to continue."] + app.staticTexts[sendLinkMsg1], + app.staticTexts[sendLinkMsg2] ] ) } @@ -110,8 +113,8 @@ class ShareToolbarTests: BaseTestCase { // If not signed in, the browser prompts you to sign in waitForElementsToExist( [ - app.staticTexts["You are not signed in to your account."], - app.staticTexts["Please open Firefox, go to Settings and sign in to continue."] + app.staticTexts[sendLinkMsg1], + app.staticTexts[sendLinkMsg2] ] ) } @@ -147,18 +150,4 @@ class ShareToolbarTests: BaseTestCase { app.buttons[AccessibilityIdentifiers.Toolbar.shareButton].waitAndTap() app.collectionViews.cells[option].waitAndTap() } - - private func openNewTabAndValidateURLisPaste(url: String) { - app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].waitAndTap() - if #available(iOS 17, *) { - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 1.5) - } else { - navigator.performAction(Action.CloseURLBarOpen) - app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField].press(forDuration: 2) - } - mozWaitForElementToExist(app.tables["Context Menu"]) - app.tables.otherElements[AccessibilityIdentifiers.Photon.pasteAction].waitAndTap() - let urlBar = app.textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] - mozWaitForValueContains(urlBar, value: url) - } } From e46e1b56d300df81032e431411f17aec2bbb088e Mon Sep 17 00:00:00 2001 From: dragosb01 <134391433+dragosb01@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:46:44 +0200 Subject: [PATCH 31/45] Fix MTE-4139 - testLockIconCloseMenu test (#24175) --- .../Tests/XCUITests/TrackingProtectionTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift index ca671dda77f4..f2b82870e794 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift @@ -253,10 +253,10 @@ class TrackingProtectionTests: BaseTestCase { navigator.nowAt(BrowserTab) navigator.goto(TrackingProtectionContextMenuDetails) mozWaitForElementToExist( - app.staticTexts[AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusButton]) + app.buttons[AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusButton]) navigator.performAction(Action.CloseTPContextMenu) mozWaitForElementToNotExist( - app.staticTexts[AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusButton]) + app.buttons[AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.securityStatusButton]) } // https://mozilla.testrail.io/index.php?/cases/view/2307063 From a92522a4898b2ae4f81762712cc258f17dda46b3 Mon Sep 17 00:00:00 2001 From: FilippoZazzeroni <55580351+FilippoZazzeroni@users.noreply.github.com> Date: Thu, 16 Jan 2025 15:10:25 +0100 Subject: [PATCH 32/45] Refactor [Bookmarks] FXIOS-10985 implement diffable data source in EditBookmarkViewController (#24098) * Implement Diffable data source for EditBookmarkViewController * Implement UnitTests for Diffable data source --------- Co-authored-by: Filippo --- firefox-ios/Client.xcodeproj/project.pbxproj | 8 ++ .../EditBookmarkDiffableDataSource.swift | 42 ++++++++ .../EditBookmarkViewController.swift | 102 ++++++++---------- .../Edit Bookmark/EditBookmarkViewModel.swift | 15 ++- .../Utitlity/FolderHierarchyFetcher.swift | 6 +- .../EditBookmarkDataSourceTests.swift | 78 ++++++++++++++ .../EditBookmarkViewModelTests.swift | 19 +++- 7 files changed, 200 insertions(+), 70 deletions(-) create mode 100644 firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkDiffableDataSource.swift create mode 100644 firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/EditBookmarkDataSourceTests.swift diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index c11a17e1ceae..4499826c79af 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -50,6 +50,8 @@ 0B7C1E951F6097AD006A8869 /* TrackingProtectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C1E941F6097AD006A8869 /* TrackingProtectionTests.swift */; }; 0B7CF8872CAC1C4E00DC02F8 /* DefaultFolderHierarchyFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7CF8862CAC1C4E00DC02F8 /* DefaultFolderHierarchyFetcherTests.swift */; }; 0B7FC3D32CAE811F005C5CCE /* DefaultBookmarksSaverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7FC3D12CAE811B005C5CCE /* DefaultBookmarksSaverTests.swift */; }; + 0B8A39EF2D3514C100853E47 /* EditBookmarkDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B8A39EE2D3514C100853E47 /* EditBookmarkDiffableDataSource.swift */; }; + 0B8A39F12D351C2500853E47 /* EditBookmarkDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B8A39F02D351C2500853E47 /* EditBookmarkDataSourceTests.swift */; }; 0B8BF3702CA2D60B00E9812D /* BookmarksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B8BF36F2CA2D60B00E9812D /* BookmarksViewController.swift */; }; 0B8BF3722CA2DA4600E9812D /* EditBookmarkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B8BF3712CA2DA4600E9812D /* EditBookmarkViewController.swift */; }; 0B8BF3752CA2EFC000E9812D /* EditBookmarkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B8BF3742CA2EFC000E9812D /* EditBookmarkCell.swift */; }; @@ -2356,6 +2358,8 @@ 0B7CF8862CAC1C4E00DC02F8 /* DefaultFolderHierarchyFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultFolderHierarchyFetcherTests.swift; sourceTree = ""; }; 0B7FC3D12CAE811B005C5CCE /* DefaultBookmarksSaverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBookmarksSaverTests.swift; sourceTree = ""; }; 0B8749EB891EE229EA88FEF3 /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/ErrorPages.strings; sourceTree = ""; }; + 0B8A39EE2D3514C100853E47 /* EditBookmarkDiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBookmarkDiffableDataSource.swift; sourceTree = ""; }; + 0B8A39F02D351C2500853E47 /* EditBookmarkDataSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBookmarkDataSourceTests.swift; sourceTree = ""; }; 0B8BF36F2CA2D60B00E9812D /* BookmarksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksViewController.swift; sourceTree = ""; }; 0B8BF3712CA2DA4600E9812D /* EditBookmarkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBookmarkViewController.swift; sourceTree = ""; }; 0B8BF3742CA2EFC000E9812D /* EditBookmarkCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBookmarkCell.swift; sourceTree = ""; }; @@ -10130,6 +10134,7 @@ children = ( 8A9D31612D13545100171502 /* EditFolderViewModelTests.swift */, 8A9D31692D135EB000171502 /* EditBookmarkViewModelTests.swift */, + 0B8A39F02D351C2500853E47 /* EditBookmarkDataSourceTests.swift */, 0B7CF8862CAC1C4E00DC02F8 /* DefaultFolderHierarchyFetcherTests.swift */, 0B7FC3D12CAE811B005C5CCE /* DefaultBookmarksSaverTests.swift */, 8AED868228CA3B3400351A50 /* BookmarkPanelViewModelTests.swift */, @@ -10142,6 +10147,7 @@ isa = PBXGroup; children = ( 0B8BF3712CA2DA4600E9812D /* EditBookmarkViewController.swift */, + 0B8A39EE2D3514C100853E47 /* EditBookmarkDiffableDataSource.swift */, 0B8BF3742CA2EFC000E9812D /* EditBookmarkCell.swift */, 0B3F8C5D2CA4471C00DB5367 /* EditBookmarkViewModel.swift */, ); @@ -16642,6 +16648,7 @@ 8A46F5AD2C9E4389005B6422 /* RemoteSettingsFetchConfig.swift in Sources */, CDB3BE8724746787009320EE /* FirefoxAccountSignInViewController.swift in Sources */, 8A471183287F6D9C00F5A6EA /* BookmarksPanelViewModel.swift in Sources */, + 0B8A39EF2D3514C100853E47 /* EditBookmarkDiffableDataSource.swift in Sources */, 8A44F20E2B585E1F0016BC81 /* HomepageTelemetry.swift in Sources */, EDD2A7FA2CDBD1D100ED464C /* SearchEngineElement+initFromSearchEngine.swift in Sources */, B236204D2B8673DE000B1DE7 /* AddressScrollView.swift in Sources */, @@ -17607,6 +17614,7 @@ 43446CF02412DDBE00F5C643 /* UpdateViewModelTests.swift in Sources */, E19443F62AF9413300964EA5 /* FakespotUtilsTests.swift in Sources */, 8A93F86829D373B0004159D9 /* MockNavigationController.swift in Sources */, + 0B8A39F12D351C2500853E47 /* EditBookmarkDataSourceTests.swift in Sources */, 8AE1E1D927B1BD380024C45E /* UIStackViewExtensionsTests.swift in Sources */, 8A0727492B4898D20071BB9F /* WebviewTelemetryTests.swift in Sources */, 8A9D316A2D135EB000171502 /* EditBookmarkViewModelTests.swift in Sources */, diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkDiffableDataSource.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkDiffableDataSource.swift new file mode 100644 index 000000000000..a27c0c0d0719 --- /dev/null +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkDiffableDataSource.swift @@ -0,0 +1,42 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +typealias EditBookmarkTableSection = EditBookmarkDiffableDataSource.TableSection +typealias EditBookmarkTableCell = EditBookmarkDiffableDataSource.TableCell + +class EditBookmarkDiffableDataSource: UITableViewDiffableDataSource { + enum TableSection: Int, CaseIterable { + case main + case selectFolder + } + + enum TableCell: Hashable { + case bookmark + case folder(Folder, Bool) + case newFolder + } + + var onSnapshotUpdate: VoidReturnCallback? + + func updateSnapshot(isFolderCollapsed: Bool, folders: [Folder]) { + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([.main, .selectFolder]) + + snapshot.appendItems([.bookmark], toSection: .main) + + let folderItems = folders.map { TableCell.folder($0, isFolderCollapsed) } + snapshot.appendItems(folderItems, toSection: .selectFolder) + + // Add the New Folder section if not collapsed + if !isFolderCollapsed { + snapshot.appendItems([.newFolder], toSection: .selectFolder) + } + + apply(snapshot, animatingDifferences: true) { [weak self] in + self?.onSnapshotUpdate?() + } + } +} diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewController.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewController.swift index b8c324c60f46..b030a8b368e8 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewController.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewController.swift @@ -8,19 +8,14 @@ import MozillaAppServices class EditBookmarkViewController: UIViewController, UITableViewDelegate, - UITableViewDataSource, Themeable { - private enum Section: Int, CaseIterable { - case bookmark - case folder - case newFolder - } private struct UX { static let bookmarkCellTopPadding: CGFloat = 25.0 static let folderHeaderIdentifier = "folderHeaderIdentifier" static let folderHeaderHorizzontalPadding: CGFloat = 16.0 static let folderHeaderBottomPadding: CGFloat = 8.0 } + var currentWindowUUID: WindowUUID? var themeManager: any ThemeManager var themeObserver: (any NSObjectProtocol)? @@ -30,7 +25,6 @@ class EditBookmarkViewController: UIViewController, } private lazy var tableView: UITableView = .build { view in - view.dataSource = self view.delegate = self view.register(cellType: EditBookmarkCell.self) view.register(cellType: OneLineTableViewCell.self) @@ -41,7 +35,6 @@ class EditBookmarkViewController: UIViewController, size: CGSize(width: 0, height: UX.bookmarkCellTopPadding))) view.tableHeaderView = headerSpacerView } - private lazy var saveBarButton: UIBarButtonItem = { let button = UIBarButtonItem( title: String.Bookmarks.Menu.EditBookmarkSave, @@ -52,9 +45,16 @@ class EditBookmarkViewController: UIViewController, return button }() - var onViewWillDisappear: (() -> Void)? - var onViewWillAppear: (() -> Void)? + var onViewWillDisappear: VoidReturnCallback? + var onViewWillAppear: VoidReturnCallback? + private let viewModel: EditBookmarkViewModel + private lazy var dataSource: EditBookmarkDiffableDataSource = { + return EditBookmarkDiffableDataSource(tableView: tableView, + cellProvider: { [weak self] _, indexPath, item in + return self?.configureCells(at: indexPath, item: item) ?? UITableViewCell() + }) + }() init(viewModel: EditBookmarkViewModel, windowUUID: WindowUUID, @@ -78,23 +78,16 @@ class EditBookmarkViewController: UIViewController, super.viewDidLoad() title = .Bookmarks.Menu.EditBookmarkTitle viewModel.onFolderStatusUpdate = { [weak self] in - guard let self = self else { return } - - // TODO: FXIOS-10985 - replace batch updates with diffable data source - self.tableView.performBatchUpdates { - if self.viewModel.isFolderCollapsed { - self.tableView.deleteSections(IndexSet(integer: Section.newFolder.rawValue), with: .none) - } else { - self.tableView.insertSections(IndexSet(integer: Section.newFolder.rawValue), with: .none) - } - self.tableView.reloadSections(IndexSet(integer: Section.folder.rawValue), with: .automatic) - } + self?.reloadTableViewData() } navigationItem.rightBarButtonItem = saveBarButton - // The back button title sometimes doesn't allign with the chevron, force navigation bar layout + // The back button title sometimes doesn't align with the chevron, force navigation bar layout navigationController?.navigationBar.layoutIfNeeded() setupSubviews() + + dataSource.defaultRowAnimation = .fade + reloadTableViewData() } override func viewWillAppear(_ animated: Bool) { @@ -140,6 +133,11 @@ class EditBookmarkViewController: UIViewController, // MARK: - Actions + private func reloadTableViewData() { + dataSource.updateSnapshot(isFolderCollapsed: viewModel.isFolderCollapsed, + folders: viewModel.folderStructures) + } + @objc func saveButtonAction() { // If we are in the standalone version of edit bookmark, we should save before dismissing @@ -156,7 +154,7 @@ class EditBookmarkViewController: UIViewController, func applyTheme() { setTheme(theme) - tableView.reloadData() + reloadTableViewData() } private func setTheme(_ theme: any Theme) { @@ -174,15 +172,10 @@ class EditBookmarkViewController: UIViewController, tableView.backgroundColor = theme.colors.layer1 } - // MARK: - UITableViewDataSource & UITableViewDelegate + // MARK: - Configure Table View Cells - func numberOfSections(in tableView: UITableView) -> Int { - return viewModel.isFolderCollapsed ? Section.allCases.count - 1 : Section.allCases.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let section = Section(rawValue: indexPath.section) else { return UITableViewCell() } - switch section { + private func configureCells(at indexPath: IndexPath, item: EditBookmarkTableCell) -> UITableViewCell { + switch item { case .bookmark: guard let cell = tableView.dequeueReusableCell(withIdentifier: EditBookmarkCell.cellIdentifier, for: indexPath) as? EditBookmarkCell @@ -191,10 +184,10 @@ class EditBookmarkViewController: UIViewController, } configureEditBookmarkCell(cell) return cell - case .folder: + + case .folder(let folder, _): guard let cell = tableView.dequeueReusableCell(withIdentifier: OneLineTableViewCell.cellIdentifier, - for: indexPath) as? OneLineTableViewCell, - let folder = viewModel.folderStructures[safe: indexPath.row] + for: indexPath) as? OneLineTableViewCell else { return UITableViewCell() } @@ -204,6 +197,7 @@ class EditBookmarkViewController: UIViewController, configureParentFolderCell(cell, folder: folder) } return cell + case .newFolder: guard let cell = tableView.dequeueReusableCell(withIdentifier: OneLineTableViewCell.cellIdentifier, for: indexPath) as? OneLineTableViewCell, @@ -232,9 +226,8 @@ class EditBookmarkViewController: UIViewController, cell.titleLabel.text = folder.title let folderImage = UIImage(named: StandardImageIdentifiers.Large.folder)?.withRenderingMode(.alwaysTemplate) cell.leftImageView.image = folderImage - cell.indentationLevel = viewModel.folderStructures.count == 1 ? 0 : folder.indentation - let isFolderSelected = folder == viewModel.selectedFolder - let canShowAccessoryView = viewModel.shouldShowDisclosureIndicator(isFolderSelected: isFolderSelected) + cell.indentationLevel = viewModel.indentationForFolder(folder) + let canShowAccessoryView = viewModel.shouldShowDisclosureIndicatorForFolder(folder) cell.accessoryType = canShowAccessoryView ? .checkmark : .none cell.selectionStyle = .default cell.accessibilityTraits = .button @@ -262,25 +255,16 @@ class EditBookmarkViewController: UIViewController, cell.applyTheme(theme: theme) } + // MARK: - UITableViewDelegate + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return UITableView.automaticDimension } - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - guard let section = Section(rawValue: section) else { return 0 } - return switch section { - case .bookmark: - 1 - case .folder: - viewModel.folderStructures.count - case .newFolder: - viewModel.isFolderCollapsed ? 0 : 1 - } - } - func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - guard let sectionEnum = Section(rawValue: section), sectionEnum == .folder else { return nil } - guard let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: UX.folderHeaderIdentifier) + guard let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: UX.folderHeaderIdentifier), + let sectionEnum = EditBookmarkTableSection(rawValue: section), + sectionEnum == .selectFolder else { return nil } var configuration = UIListContentConfiguration.plainHeader() configuration.text = .Bookmarks.Menu.EditBookmarkSaveIn.uppercased() @@ -298,7 +282,8 @@ class EditBookmarkViewController: UIViewController, } func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat { - guard let section = Section(rawValue: section), section == .folder else { return 0 } + guard let section = EditBookmarkTableSection(rawValue: section), + section == .selectFolder else { return 0 } return UITableView.automaticDimension } @@ -310,12 +295,17 @@ class EditBookmarkViewController: UIViewController, } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) - guard let section = Section(rawValue: indexPath.section) else { return } - if section == .folder, let folder = viewModel.folderStructures[safe: indexPath.row] { + guard let item = dataSource.itemIdentifier(for: indexPath) else { return } + + switch item { + case .folder(let folder, _): viewModel.selectFolder(folder) - } else if section == .newFolder { + case .newFolder: viewModel.createNewFolder() + default: + break } + + tableView.deselectRow(at: indexPath, animated: false) } } diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewModel.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewModel.swift index df4eaea5749c..32547d11ad26 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewModel.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Edit Bookmark/EditBookmarkViewModel.swift @@ -64,14 +64,22 @@ class EditBookmarkViewModel: ParentFolderSelector { selectedFolder = folder } - func shouldShowDisclosureIndicator(isFolderSelected: Bool) -> Bool { - return isFolderSelected && !isFolderCollapsed + func shouldShowDisclosureIndicatorForFolder(_ folder: Folder) -> Bool { + let shouldShowDisclosureIndicator = folder.guid == selectedFolder?.guid + return shouldShowDisclosureIndicator && !isFolderCollapsed + } + + func indentationForFolder(_ folder: Folder) -> Int { + if isFolderCollapsed { + return 0 + } + return folder.indentation } func selectFolder(_ folder: Folder) { isFolderCollapsed.toggle() + selectedFolder = folder if isFolderCollapsed { - selectedFolder = folder folderStructures = [folder] onFolderStatusUpdate?() } else { @@ -91,7 +99,6 @@ class EditBookmarkViewModel: ParentFolderSelector { let folders = await self?.folderFetcher.fetchFolders() guard let folders else { return } self?.folderStructures = folders - self?.selectedFolder = selectedFolder self?.onFolderStatusUpdate?() } } diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/FolderHierarchyFetcher.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/FolderHierarchyFetcher.swift index b83d52c83b36..a2a945c58000 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/FolderHierarchyFetcher.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Utitlity/FolderHierarchyFetcher.swift @@ -9,7 +9,7 @@ protocol FolderHierarchyFetcher { func fetchFolders() async -> [Folder] } -struct Folder: Equatable { +struct Folder: Equatable, Hashable { init(title: String, guid: String, indentation: Int) { self.title = Self.localizedTitle(guid) ?? title self.guid = guid @@ -22,10 +22,6 @@ struct Folder: Equatable { static let DesktopFolderHeaderPlaceholderGuid = "DUMMY" - static func == (lhs: Self, rhs: Self) -> Bool { - return lhs.guid == rhs.guid - } - static func localizedTitle(_ guid: String) -> String? { return LocalizedRootBookmarkFolderStrings[guid] } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/EditBookmarkDataSourceTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/EditBookmarkDataSourceTests.swift new file mode 100644 index 000000000000..e025a0b2a5be --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/EditBookmarkDataSourceTests.swift @@ -0,0 +1,78 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client + +final class EditBookmarkDataSourceTests: XCTestCase { + private let folders = [ + Folder(title: "Parent", guid: "ParentFolder", indentation: 0), + Folder(title: "Child", guid: "ChildFolder", indentation: 1) + ] + private var tableView: UITableView! + + override func setUp() { + super.setUp() + tableView = UITableView() + let window = UIWindow(frame: UIScreen.main.bounds) + window.rootViewController = UIViewController() + window.rootViewController?.view.addSubview(tableView) + window.makeKeyAndVisible() + } + + override func tearDown() { + tableView = nil + super.tearDown() + } + + func testOnSnapshotUpdate() { + let expectation = XCTestExpectation(description: "onShapshotUpate should be called") + let subject = createSubject(tableView: tableView) + + subject.onSnapshotUpdate = { + expectation.fulfill() + } + + subject.updateSnapshot(isFolderCollapsed: true, folders: folders) + + wait(for: [expectation], timeout: 10.0) + } + + func testDataSourceSnapshot_whenFolderIsCollapsed() { + let subject = createSubject(tableView: tableView) + let sections: [EditBookmarkTableSection] = [.main, .selectFolder] + var tableCells: [EditBookmarkTableCell] = [.bookmark] + folders.forEach { + tableCells.append(EditBookmarkTableCell.folder($0, true)) + } + + subject.updateSnapshot(isFolderCollapsed: true, folders: folders) + + XCTAssertEqual(subject.snapshot().sectionIdentifiers, sections) + XCTAssertEqual(subject.snapshot().itemIdentifiers, tableCells) + } + + func testDataSourceSnapshot_whenFolderIsNotCollapsed() { + let subject = createSubject(tableView: tableView) + let sections: [EditBookmarkTableSection] = [.main, .selectFolder] + var tableCells: [EditBookmarkTableCell] = [.bookmark] + folders.forEach { + tableCells.append(EditBookmarkTableCell.folder($0, false)) + } + tableCells.append(.newFolder) + + subject.updateSnapshot(isFolderCollapsed: false, folders: folders) + + XCTAssertEqual(subject.snapshot().sectionIdentifiers, sections) + XCTAssertEqual(subject.snapshot().itemIdentifiers, tableCells) + } + + private func createSubject(tableView: UITableView) -> EditBookmarkDiffableDataSource { + let dataSource = EditBookmarkDiffableDataSource(tableView: tableView) { _, _, _ in + return UITableViewCell() + } + trackForMemoryLeaks(dataSource) + return dataSource + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/EditBookmarkViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/EditBookmarkViewModelTests.swift index 591607233dc6..4f7a9a08643a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/EditBookmarkViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/EditBookmarkViewModelTests.swift @@ -55,21 +55,30 @@ class EditBookmarkViewModelTests: XCTestCase { func testShouldShowDisclosureIndicator_whenIsFolderSelected() { let subject = createSubject(folder: folder, parentFolder: parentFolder) + let folder = Folder(title: folder.title, guid: folder.guid, indentation: 0) - XCTAssertFalse(subject.shouldShowDisclosureIndicator(isFolderSelected: true)) + subject.selectFolder(folder) + + XCTAssertTrue(subject.shouldShowDisclosureIndicatorForFolder(folder)) } func testShouldShowDisclosureIndicator_whenIsNotFolderSelected() { let subject = createSubject(folder: folder, parentFolder: parentFolder) + let folder = Folder(title: folder.title, guid: folder.guid, indentation: 0) - XCTAssertFalse(subject.shouldShowDisclosureIndicator(isFolderSelected: false)) + XCTAssertFalse(subject.shouldShowDisclosureIndicatorForFolder(folder)) } - func testShouldShowDisclosureIndicator_whenIsNotFolderSelectedAfterSelectFolder() { + func testShouldShowDisclosureIndicator_whenIsFolderCollapsed() { let subject = createSubject(folder: folder, parentFolder: parentFolder) - subject.selectFolder(Folder(title: "Test", guid: "", indentation: 0)) + let folder = Folder(title: folder.title, guid: folder.guid, indentation: 0) - XCTAssertTrue(subject.shouldShowDisclosureIndicator(isFolderSelected: true)) + subject.selectFolder(folder) + // Double selecting a folder turns isFolderCollapsed to true + subject.selectFolder(folder) + + XCTAssertFalse(subject.shouldShowDisclosureIndicatorForFolder(folder)) + XCTAssertTrue(subject.isFolderCollapsed) } func testBackNavigationButtonTitle_whenIsMobileFolderGuid() { From ca92100efe3f46f0962c7c25e0aa8b1d88da2229 Mon Sep 17 00:00:00 2001 From: Litianu Razvan Date: Thu, 16 Jan 2025 18:05:32 +0200 Subject: [PATCH 33/45] Add FXIOS-10469 Firefox iOS: DAU Ping Setting (#24125) * FXIOS-10469 Firefox iOS: DAU Ping Setting * Swiftlint fix * Add and fix tests * Fix registration * Fix tests 2 * Fix tests 3 * Fix tests 3 --- firefox-ios/Client.xcodeproj/project.pbxproj | 4 + .../Client/Application/AppDelegate.swift | 1 + .../Client/Application/AppLaunchUtil.swift | 11 +- .../Client/Application/DependencyHelper.swift | 3 + .../Browser/BrowserCoordinator.swift | 5 +- .../Coordinators/SettingsCoordinator.swift | 26 ++- .../Main/AppSettingsTableViewController.swift | 27 ++- .../Telemetry/GleanUsageReporting.swift | 38 ++-- .../SettingsCoordinatorTests.swift | 8 +- .../DependencyHelperMock.swift | 3 + .../GleanLifecycleObserverTests.swift | 162 ++++++++++++++++++ ...nUsageReportingLifecycleObserverTest.swift | 28 +-- .../AppSettingsTableViewControllerTests.swift | 3 + 13 files changed, 259 insertions(+), 60 deletions(-) create mode 100644 firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanLifecycleObserverTests.swift diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 4499826c79af..4ad060bfcce4 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -1117,6 +1117,7 @@ 8C44A9D22A6A99FE009A1AA7 /* ShoppingProduct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C44A9D12A6A99FE009A1AA7 /* ShoppingProduct.swift */; }; 8C46E1B72B2209F000F56521 /* FakespotAdsEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C46E1B62B2209F000F56521 /* FakespotAdsEvent.swift */; }; 8C4B0F5D2C076B12008B3E74 /* UpdatableAddressFields+Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C4B0F5C2C076B12008B3E74 /* UpdatableAddressFields+Decodable.swift */; }; + 8C6AF26A2D38FB1500254698 /* GleanLifecycleObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6AF2692D38FB1500254698 /* GleanLifecycleObserverTests.swift */; }; 8C6F94652A972EB300415FF6 /* FakespotAdjustRatingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6F94632A972EB300415FF6 /* FakespotAdjustRatingView.swift */; }; 8C6F94662A972EB300415FF6 /* FakespotStarRatingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C6F94642A972EB300415FF6 /* FakespotStarRatingView.swift */; }; 8C8D8C7A2AA067AD00490D32 /* FakespotCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C8D8C792AA067AD00490D32 /* FakespotCoordinatorTests.swift */; }; @@ -7949,6 +7950,7 @@ 8C46E1B62B2209F000F56521 /* FakespotAdsEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotAdsEvent.swift; sourceTree = ""; }; 8C4B0F5C2C076B12008B3E74 /* UpdatableAddressFields+Decodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UpdatableAddressFields+Decodable.swift"; sourceTree = ""; }; 8C514AFEABEF8DE46CE7B8D4 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/AuthenticationManager.strings; sourceTree = ""; }; + 8C6AF2692D38FB1500254698 /* GleanLifecycleObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GleanLifecycleObserverTests.swift; sourceTree = ""; }; 8C6DA7D02A6FE78F00DE264F /* ShoppingProductTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShoppingProductTests.swift; sourceTree = ""; }; 8C6F94632A972EB300415FF6 /* FakespotAdjustRatingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FakespotAdjustRatingView.swift; sourceTree = ""; }; 8C6F94642A972EB300415FF6 /* FakespotStarRatingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FakespotStarRatingView.swift; sourceTree = ""; }; @@ -14372,6 +14374,7 @@ 8A86DAD7277298DE00D7BFFF /* ClosedTabsStoreTests.swift */, C2200A692B7D148C00DC062A /* ContentBlockerTests.swift */, 8CA4E80E2D22D2C7007207C1 /* GleanUsageReportingLifecycleObserverTest.swift */, + 8C6AF2692D38FB1500254698 /* GleanLifecycleObserverTests.swift */, C889D7CD2858C4B500121E1D /* ContextMenuHelperTests.swift */, 8A93F86329D37314004159D9 /* Coordinators */, 43B658D729CE249D00C9EF08 /* CreditCard */, @@ -17500,6 +17503,7 @@ 8A93F86529D37331004159D9 /* DefaultRouterTests.swift in Sources */, 5AD3B6802CF6674F00AFA1FE /* MockUIApplication.swift in Sources */, 8AF10D8A29D713F50086351D /* LaunchScreenViewModelTests.swift in Sources */, + 8C6AF26A2D38FB1500254698 /* GleanLifecycleObserverTests.swift in Sources */, 8A87B4322CC1A3C0003A9239 /* PocketManagerTests.swift in Sources */, 2F13E79B1AC0C02700D75081 /* StringExtensionsTests.swift in Sources */, CA24B52224ABD7D40093848C /* PasswordManagerViewModelTests.swift in Sources */, diff --git a/firefox-ios/Client/Application/AppDelegate.swift b/firefox-ios/Client/Application/AppDelegate.swift index a5a4912565e1..45fca12b2b11 100644 --- a/firefox-ios/Client/Application/AppDelegate.swift +++ b/firefox-ios/Client/Application/AppDelegate.swift @@ -37,6 +37,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, FeatureFlaggable { lazy var backgroundTabLoader: BackgroundTabLoader = { return DefaultBackgroundTabLoader(tabQueue: (AppContainer.shared.resolve() as Profile).queue) }() + lazy var gleanLifecycleObserver = GleanLifecycleObserver() private var isLoadingBackgroundTabs = false private var shutdownWebServer: DispatchSourceTimer? diff --git a/firefox-ios/Client/Application/AppLaunchUtil.swift b/firefox-ios/Client/Application/AppLaunchUtil.swift index 9ed82d4ab9c5..25daae61c3b5 100644 --- a/firefox-ios/Client/Application/AppLaunchUtil.swift +++ b/firefox-ios/Client/Application/AppLaunchUtil.swift @@ -15,14 +15,19 @@ class AppLaunchUtil { private var profile: Profile private let introScreenManager: IntroScreenManager private let termsOfServiceManager: TermsOfServiceManager + private let gleanLifecycleObserver: GleanLifecycleObserver - init(logger: Logger = DefaultLogger.shared, - profile: Profile) { + init( + logger: Logger = DefaultLogger.shared, + profile: Profile, + gleanLifecycleObserver: GleanLifecycleObserver = AppContainer.shared.resolve() + ) { self.logger = logger self.profile = profile // self.adjustHelper = AdjustHelper(profile: profile) self.introScreenManager = IntroScreenManager(prefs: profile.prefs) self.termsOfServiceManager = TermsOfServiceManager(prefs: profile.prefs) + self.gleanLifecycleObserver = gleanLifecycleObserver } func setUpPreLaunchDependencies() { @@ -52,6 +57,8 @@ class AppLaunchUtil { TelemetryWrapper.shared.setup(profile: profile) TelemetryWrapper.shared.recordStartUpTelemetry() } + + gleanLifecycleObserver.startObserving() } else { logger.setup(sendCrashReports: sendCrashReports) TelemetryWrapper.shared.setup(profile: profile) diff --git a/firefox-ios/Client/Application/DependencyHelper.swift b/firefox-ios/Client/Application/DependencyHelper.swift index 0ec85f312efb..01c400073a61 100644 --- a/firefox-ios/Client/Application/DependencyHelper.swift +++ b/firefox-ios/Client/Application/DependencyHelper.swift @@ -46,6 +46,9 @@ class DependencyHelper { ) AppContainer.shared.register(service: pocketManager) + let gleanLifecycleObserver: GleanLifecycleObserver = appDelegate.gleanLifecycleObserver + AppContainer.shared.register(service: gleanLifecycleObserver) + // Tell the container we are done registering AppContainer.shared.bootstrap() } diff --git a/firefox-ios/Client/Coordinators/Browser/BrowserCoordinator.swift b/firefox-ios/Client/Coordinators/Browser/BrowserCoordinator.swift index 88a05681d929..b527f862e129 100644 --- a/firefox-ios/Client/Coordinators/Browser/BrowserCoordinator.swift +++ b/firefox-ios/Client/Coordinators/Browser/BrowserCoordinator.swift @@ -442,7 +442,10 @@ class BrowserCoordinator: BaseCoordinator, navigationController.modalPresentationStyle = modalPresentationStyle let settingsRouter = DefaultRouter(navigationController: navigationController) - let settingsCoordinator = SettingsCoordinator(router: settingsRouter, tabManager: tabManager) + let settingsCoordinator = SettingsCoordinator( + router: settingsRouter, + tabManager: tabManager + ) settingsCoordinator.parentCoordinator = self add(child: settingsCoordinator) settingsCoordinator.start(with: section) diff --git a/firefox-ios/Client/Coordinators/SettingsCoordinator.swift b/firefox-ios/Client/Coordinators/SettingsCoordinator.swift index 148e02633aa5..ec3b9ef513e4 100644 --- a/firefox-ios/Client/Coordinators/SettingsCoordinator.swift +++ b/firefox-ios/Client/Coordinators/SettingsCoordinator.swift @@ -28,25 +28,33 @@ class SettingsCoordinator: BaseCoordinator, private let profile: Profile private let tabManager: TabManager private let themeManager: ThemeManager + private let gleanLifecycleObserver: GleanLifecycleObserver weak var parentCoordinator: SettingsCoordinatorDelegate? private var windowUUID: WindowUUID { return tabManager.windowUUID } - init(router: Router, - wallpaperManager: WallpaperManagerInterface = WallpaperManager(), - profile: Profile = AppContainer.shared.resolve(), - tabManager: TabManager, - themeManager: ThemeManager = AppContainer.shared.resolve()) { + init( + router: Router, + wallpaperManager: WallpaperManagerInterface = WallpaperManager(), + profile: Profile = AppContainer.shared.resolve(), + tabManager: TabManager, + themeManager: ThemeManager = AppContainer.shared.resolve(), + gleanLifecycleObserver: GleanLifecycleObserver = AppContainer.shared.resolve() + ) { self.wallpaperManager = wallpaperManager self.profile = profile self.tabManager = tabManager self.themeManager = themeManager + self.gleanLifecycleObserver = gleanLifecycleObserver super.init(router: router) // It's important we initialize AppSettingsTableViewController with a settingsDelegate and parentCoordinator - let settingsViewController = AppSettingsTableViewController(with: profile, - and: tabManager, - settingsDelegate: self, - parentCoordinator: self) + let settingsViewController = AppSettingsTableViewController( + with: profile, + and: tabManager, + settingsDelegate: self, + parentCoordinator: self, + gleanLifecycleObserver: gleanLifecycleObserver + ) self.settingsViewController = settingsViewController router.setRootViewController(settingsViewController) } diff --git a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift index 88ccf10d802a..9e56be9a5e51 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift @@ -49,6 +49,7 @@ class AppSettingsTableViewController: SettingsTableViewController, private var appAuthenticator: AppAuthenticationProtocol private var applicationHelper: ApplicationHelper private let logger: Logger + private let gleanLifecycleObserver: GleanLifecycleObserver weak var parentCoordinator: SettingsFlowDelegate? @@ -60,16 +61,20 @@ class AppSettingsTableViewController: SettingsTableViewController, private var studiesToggleSetting: BoolSetting? // MARK: - Initializers - init(with profile: Profile, - and tabManager: TabManager, - settingsDelegate: SettingsDelegate, - parentCoordinator: SettingsFlowDelegate, - appAuthenticator: AppAuthenticationProtocol = AppAuthenticator(), - applicationHelper: ApplicationHelper = DefaultApplicationHelper(), - logger: Logger = DefaultLogger.shared) { + init( + with profile: Profile, + and tabManager: TabManager, + settingsDelegate: SettingsDelegate, + parentCoordinator: SettingsFlowDelegate, + gleanLifecycleObserver: GleanLifecycleObserver, + appAuthenticator: AppAuthenticationProtocol = AppAuthenticator(), + applicationHelper: ApplicationHelper = DefaultApplicationHelper(), + logger: Logger = DefaultLogger.shared + ) { self.appAuthenticator = appAuthenticator self.applicationHelper = applicationHelper self.logger = logger + self.gleanLifecycleObserver = gleanLifecycleObserver super.init(windowUUID: tabManager.windowUUID) self.profile = profile @@ -227,8 +232,12 @@ class AppSettingsTableViewController: SettingsTableViewController, learnMoreURL: SupportUtils.URLForTopic("usage-ping-settings-mobile"), isToSEnabled: isTermsOfServiceFeatureEnabled ) - sendDailyUsagePingSettings.shouldSendData = { value in - // TODO: FXIOS-10469 Firefox iOS: DAU Ping Setting + sendDailyUsagePingSettings.shouldSendData = { [weak self] value in + if value { + self?.gleanLifecycleObserver.startObserving() + } else { + self?.gleanLifecycleObserver.stopObserving() + } } sendDailyUsagePingSetting = sendDailyUsagePingSettings } else { diff --git a/firefox-ios/Client/Telemetry/GleanUsageReporting.swift b/firefox-ios/Client/Telemetry/GleanUsageReporting.swift index 82c5999e450d..1dab00844edc 100644 --- a/firefox-ios/Client/Telemetry/GleanUsageReporting.swift +++ b/firefox-ios/Client/Telemetry/GleanUsageReporting.swift @@ -7,7 +7,7 @@ import UIKit import Glean import Shared -enum UsageReason: String { +enum UsageReason: String, Equatable { case active case inactive } @@ -15,9 +15,23 @@ enum UsageReason: String { protocol GleanUsageReportingApi { func setUsageReason(_ usageReason: UsageReason) func submitPing() + func startTrackingDuration() + func stopTrackingDuration() } class GleanUsageReporting: GleanUsageReportingApi { + private var id: TimerId? + + func startTrackingDuration() { + id = GleanMetrics.Usage.duration.start() + } + + func stopTrackingDuration() { + if let timerId = id { + GleanMetrics.Usage.duration.stopAndAccumulate(timerId) + } + } + func setUsageReason(_ usageReason: UsageReason) { GleanMetrics.Usage.reason.set(usageReason.rawValue) } @@ -41,25 +55,29 @@ class GleanUsageReporting: GleanUsageReportingApi { class GleanLifecycleObserver { private let gleanUsageReportingApi: GleanUsageReportingApi - private var id: TimerId? - private var isObserving = false + private var isObserving: Bool = false + private let notificationCenter: NotificationCenter - init(gleanUsageReportingApi: GleanUsageReportingApi = GleanUsageReporting()) { + init( + gleanUsageReportingApi: GleanUsageReportingApi = GleanUsageReporting(), + notificationCenter: NotificationCenter = .default + ) { self.gleanUsageReportingApi = gleanUsageReportingApi + self.notificationCenter = notificationCenter } func startObserving() { guard !isObserving else { return } isObserving = true - NotificationCenter.default.addObserver( + notificationCenter.addObserver( self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil ) - NotificationCenter.default.addObserver( + notificationCenter.addObserver( self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, @@ -71,13 +89,13 @@ class GleanLifecycleObserver { guard isObserving else { return } isObserving = false - NotificationCenter.default.removeObserver( + notificationCenter.removeObserver( self, name: UIApplication.willEnterForegroundNotification, object: nil ) - NotificationCenter.default.removeObserver( + notificationCenter.removeObserver( self, name: UIApplication.didEnterBackgroundNotification, object: nil @@ -95,13 +113,13 @@ class GleanLifecycleObserver { } func handleForegroundEvent() { - id = GleanMetrics.Usage.duration.start() + gleanUsageReportingApi.startTrackingDuration() gleanUsageReportingApi.setUsageReason(.active) gleanUsageReportingApi.submitPing() } func handleBackgroundEvent() { - id.map(GleanMetrics.Usage.duration.stopAndAccumulate) + gleanUsageReportingApi.stopTrackingDuration() gleanUsageReportingApi.setUsageReason(.inactive) gleanUsageReportingApi.submitPing() } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SettingsCoordinatorTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SettingsCoordinatorTests.swift index eb1379482407..a6606108fbc9 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SettingsCoordinatorTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SettingsCoordinatorTests.swift @@ -533,9 +533,11 @@ final class SettingsCoordinatorTests: XCTestCase { // MARK: - Helper func createSubject() -> SettingsCoordinator { - let subject = SettingsCoordinator(router: mockRouter, - wallpaperManager: wallpaperManager, - tabManager: MockTabManager() + let subject = SettingsCoordinator( + router: mockRouter, + wallpaperManager: wallpaperManager, + tabManager: MockTabManager(), + gleanLifecycleObserver: MockGleanLifecycleObserver() ) trackForMemoryLeaks(subject) return subject diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DependencyInjection/DependencyHelperMock.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DependencyInjection/DependencyHelperMock.swift index 8f61103ada3b..1ea67cd1782a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DependencyInjection/DependencyHelperMock.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DependencyInjection/DependencyHelperMock.swift @@ -55,6 +55,9 @@ class DependencyHelperMock { let pocketManager: PocketManagerProvider = injectedPocketManager ?? MockPocketManager() AppContainer.shared.register(service: pocketManager) + let gleanLifecycleObserver: GleanLifecycleObserver = MockGleanLifecycleObserver() + AppContainer.shared.register(service: gleanLifecycleObserver) + // Tell the container we are done registering AppContainer.shared.bootstrap() } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanLifecycleObserverTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanLifecycleObserverTests.swift new file mode 100644 index 000000000000..84130d9ba5dc --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanLifecycleObserverTests.swift @@ -0,0 +1,162 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client + +final class MockGleanUsageReportingApi: GleanUsageReportingApi { + var setUsageReasonCalled = false + var submitPingCalled = false + var startTrackingDurationCalled = false + var stopTrackingDurationCalled = false + var pingSubmitCount = 0 + var lastUsageReason: String? + + func setUsageReason(_ usageReason: UsageReason) { + setUsageReasonCalled = true + lastUsageReason = usageReason.rawValue + } + + func submitPing() { + submitPingCalled = true + pingSubmitCount += 1 + } + + func startTrackingDuration() { + startTrackingDurationCalled = true + } + + func stopTrackingDuration() { + stopTrackingDurationCalled = true + } +} + +final class MockGleanLifecycleObserver: GleanLifecycleObserver { + var startObservingCalled = false + var stopObservingCalled = false + var handleForegroundEventCalled = false + var handleBackgroundEventCalled = false + + override func startObserving() { + startObservingCalled = true + } + + override func stopObserving() { + stopObservingCalled = true + } + + override func handleForegroundEvent() { + handleForegroundEventCalled = true + } + + override func handleBackgroundEvent() { + handleBackgroundEventCalled = true + } +} + +final class GleanLifecycleObserverTests: XCTestCase { + var mockGleanUsageReportingApi: MockGleanUsageReportingApi! + var gleanLifecycleObserver: GleanLifecycleObserver! + let notificationCenter = NotificationCenter() + + override func setUp() { + super.setUp() + mockGleanUsageReportingApi = MockGleanUsageReportingApi() + gleanLifecycleObserver = GleanLifecycleObserver( + gleanUsageReportingApi: mockGleanUsageReportingApi, + notificationCenter: notificationCenter + ) + } + + override func tearDown() { + mockGleanUsageReportingApi = nil + gleanLifecycleObserver = nil + super.tearDown() + } + + func testStartObserving() { + gleanLifecycleObserver.startObserving() + + notificationCenter.post(name: UIApplication.willEnterForegroundNotification, object: nil) + XCTAssertTrue( + mockGleanUsageReportingApi.startTrackingDurationCalled, + "Foreground notification should trigger startTrackingDuration." + ) + XCTAssertTrue( + mockGleanUsageReportingApi.setUsageReasonCalled, + "Foreground notification should trigger setUsageReason." + ) + } + + func testStopObserving() { + gleanLifecycleObserver.startObserving() + + notificationCenter.post(name: UIApplication.willEnterForegroundNotification, object: nil) + XCTAssertTrue( + mockGleanUsageReportingApi.startTrackingDurationCalled, + "Observer should respond to notifications when observing." + ) + + mockGleanUsageReportingApi.startTrackingDurationCalled = false + + gleanLifecycleObserver.stopObserving() + + notificationCenter.post(name: UIApplication.willEnterForegroundNotification, object: nil) + + XCTAssertFalse( + mockGleanUsageReportingApi.startTrackingDurationCalled, + "Observer should not respond to notifications after stopObserving is called." + ) + } + + func testHandleForegroundEvent() { + gleanLifecycleObserver.handleForegroundEvent() + XCTAssertTrue( + mockGleanUsageReportingApi.startTrackingDurationCalled, + "startTrackingDuration should be called." + ) + XCTAssertTrue( + mockGleanUsageReportingApi.setUsageReasonCalled, + "setUsageReason should be called." + ) + XCTAssertTrue( + mockGleanUsageReportingApi.submitPingCalled, + "submitPing should be called." + ) + } + + func testHandleBackgroundEvent() { + gleanLifecycleObserver.handleBackgroundEvent() + XCTAssertTrue( + mockGleanUsageReportingApi.stopTrackingDurationCalled, + "stopTrackingDuration should be called." + ) + XCTAssertTrue( + mockGleanUsageReportingApi.setUsageReasonCalled, + "setUsageReason should be called." + ) + XCTAssertTrue( + mockGleanUsageReportingApi.submitPingCalled, + "submitPing should be called." + ) + } + + func testNotificationTriggersForegroundEvent() { + gleanLifecycleObserver.startObserving() + notificationCenter.post(name: UIApplication.willEnterForegroundNotification, object: nil) + XCTAssertTrue( + mockGleanUsageReportingApi.startTrackingDurationCalled, + "Foreground notification should trigger startTrackingDuration." + ) + } + + func testNotificationTriggersBackgroundEvent() { + gleanLifecycleObserver.startObserving() + notificationCenter.post(name: UIApplication.didEnterBackgroundNotification, object: nil) + XCTAssertTrue( + mockGleanUsageReportingApi.stopTrackingDurationCalled, + "Background notification should trigger stopTrackingDuration." + ) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanUsageReportingLifecycleObserverTest.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanUsageReportingLifecycleObserverTest.swift index 1c9055e75c16..d428a9284250 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanUsageReportingLifecycleObserverTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/GleanUsageReportingLifecycleObserverTest.swift @@ -5,30 +5,12 @@ import XCTest @testable import Client -class GleanUsageReportingApiMock: GleanUsageReportingApi { - var pingSubmitCount = 0 - var lastUsageReason: String? - var lastDurationMillis: Int64? - - func setUsageReason(_ usageReason: UsageReason) { - lastUsageReason = usageReason.rawValue - } - - func submitPing() { - pingSubmitCount += 1 - } - - func setDuration(_ durationMillis: Int64) { - lastDurationMillis = durationMillis - } -} - class GleanUsageReportingLifecycleObserverTest: XCTestCase { - private var fakeGleanUsageReportingApi: GleanUsageReportingApiMock! + private var fakeGleanUsageReportingApi: MockGleanUsageReportingApi! override func setUp() { super.setUp() - fakeGleanUsageReportingApi = GleanUsageReportingApiMock() + fakeGleanUsageReportingApi = MockGleanUsageReportingApi() } func testNoPingsSubmittedBeforeLifecycleChanges() { @@ -66,12 +48,6 @@ class GleanUsageReportingLifecycleObserverTest: XCTestCase { XCTAssertEqual(fakeGleanUsageReportingApi.pingSubmitCount, 2) } - func testDoNotSubmitDurationIfNotSet() { - let observer = createObserver() - observer.handleBackgroundEvent() - XCTAssertNil(fakeGleanUsageReportingApi.lastDurationMillis) - } - private func createObserver() -> GleanLifecycleObserver { return GleanLifecycleObserver( gleanUsageReportingApi: fakeGleanUsageReportingApi diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/AppSettingsTableViewControllerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/AppSettingsTableViewControllerTests.swift index 3f15d63a21c1..9866a57ab85d 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/AppSettingsTableViewControllerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/AppSettingsTableViewControllerTests.swift @@ -14,6 +14,7 @@ class AppSettingsTableViewControllerTests: XCTestCase { private var applicationHelper: MockApplicationHelper! private var mockSettingsDelegate: MockSettingsDelegate! private var mockParentCoordinator: MockSettingsFlowDelegate! + private var mockGleanLifecycleObserver: MockGleanLifecycleObserver! override func setUp() { super.setUp() @@ -27,6 +28,7 @@ class AppSettingsTableViewControllerTests: XCTestCase { self.applicationHelper = MockApplicationHelper() self.mockSettingsDelegate = MockSettingsDelegate() self.mockParentCoordinator = MockSettingsFlowDelegate() + self.mockGleanLifecycleObserver = MockGleanLifecycleObserver() } override func tearDown() { @@ -136,6 +138,7 @@ class AppSettingsTableViewControllerTests: XCTestCase { and: tabManager, settingsDelegate: mockSettingsDelegate, parentCoordinator: mockParentCoordinator, + gleanLifecycleObserver: mockGleanLifecycleObserver, appAuthenticator: appAuthenticator, applicationHelper: applicationHelper) trackForMemoryLeaks(subject) From fa7e28ae05a4efdeed7657f7a377fa82f76cb15b Mon Sep 17 00:00:00 2001 From: dicarobinho <61138287+dicarobinho@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:07:48 +0200 Subject: [PATCH 34/45] =?UTF-8?q?Bugfix=20FXIOS-10663=20=E2=81=83=20[Felt?= =?UTF-8?q?=20privacy-Unified=20panel]=20-=20Back=20button=20from=20the=20?= =?UTF-8?q?submenus=20and=20from=20the=20certificate=20overlaps=20the=20si?= =?UTF-8?q?te=20name=20(#24172)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FXIOS-10663 #23324 ⁃ [Felt privacy-Unified panel] [Accessibility] - Back button from the submenus and from the certificate overlaps the site name --- .../Headers/NavigationHeaderView.swift | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/BrowserKit/Sources/ComponentLibrary/Headers/NavigationHeaderView.swift b/BrowserKit/Sources/ComponentLibrary/Headers/NavigationHeaderView.swift index a74969ff3d6d..555a2c3fe45a 100644 --- a/BrowserKit/Sources/ComponentLibrary/Headers/NavigationHeaderView.swift +++ b/BrowserKit/Sources/ComponentLibrary/Headers/NavigationHeaderView.swift @@ -23,8 +23,11 @@ public final class NavigationHeaderView: UIView { label.textAlignment = .center label.font = FXFontStyles.Regular.headline.scaledFont() label.numberOfLines = 2 + label.lineBreakMode = .byTruncatingTail label.accessibilityTraits.insert(.header) label.isAccessibilityElement = false + label.setContentHuggingPriority(.defaultLow, for: .horizontal) + label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) } private lazy var closeButton: CloseButton = .build { button in @@ -37,6 +40,8 @@ public final class NavigationHeaderView: UIView { for: .normal) button.titleLabel?.adjustsFontSizeToFitWidth = true button.addTarget(self, action: #selector(self.backButtonTapped), for: .touchUpInside) + button.setContentHuggingPriority(.defaultHigh, for: .horizontal) + button.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) } private let horizontalLine: UIView = .build() @@ -62,26 +67,20 @@ public final class NavigationHeaderView: UIView { closeButton.removeConstraints(closeButton.constraints) viewConstraints.removeAll() viewConstraints.append(contentsOf: [ - backButton.leadingAnchor.constraint( - equalTo: leadingAnchor, - constant: UX.imageMargins - ), + backButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: UX.imageMargins), backButton.centerYAnchor.constraint(equalTo: centerYAnchor), + backButton.trailingAnchor.constraint(lessThanOrEqualTo: titleLabel.leadingAnchor, + constant: -UX.horizontalMargin), - titleLabel.topAnchor.constraint( - equalTo: topAnchor, - constant: UX.baseDistance - ), - titleLabel.bottomAnchor.constraint( - equalTo: bottomAnchor, - constant: -UX.baseDistance - ), + titleLabel.leadingAnchor.constraint(greaterThanOrEqualTo: backButton.trailingAnchor, + constant: UX.horizontalMargin), + titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: closeButton.leadingAnchor, + constant: -UX.horizontalMargin), titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor), + titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: UX.baseDistance), + titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -UX.baseDistance), - closeButton.trailingAnchor.constraint( - equalTo: trailingAnchor, - constant: -UX.horizontalMargin - ), + closeButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -UX.horizontalMargin), closeButton.centerYAnchor.constraint(equalTo: centerYAnchor), horizontalLine.leadingAnchor.constraint(equalTo: leadingAnchor), From 49752c8232bda6a51298070780258b142032a8bf Mon Sep 17 00:00:00 2001 From: Clare So <1740517+clarmso@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:09:01 -0500 Subject: [PATCH 35/45] Refactor MTE-4104 Bump Github Actions slackapi/slack-github-action (#24164) * Try v2 of slack-github-actions * Add webhook and webhook-type * Only testing one file * Is it called templated? * Let's run all tests! * tabs... * Bump slack-github-action for focus previous iOS tests --- .../workflows/focus-ios-ui-tests-previous-os.yml | 14 ++++++++------ .github/workflows/focus-ios-ui-tests.yml | 7 ++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/focus-ios-ui-tests-previous-os.yml b/.github/workflows/focus-ios-ui-tests-previous-os.yml index e9ec50afec53..f6a4f75223eb 100644 --- a/.github/workflows/focus-ios-ui-tests-previous-os.yml +++ b/.github/workflows/focus-ios-ui-tests-previous-os.yml @@ -148,12 +148,13 @@ jobs: retention-days: 90 - name: Report to Slack (SmokeTests) id: slack-smoketests - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v2.0.0 with: payload-file-path: ${{ env.browser }}/slack-smoketests.json + payload-templated: true + webhook: ${{ secrets.WEBHOOK_SLACK_TOKEN }} + webhook-type: incoming-webhook env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK ios_simulator: ${{ matrix.ios_simulator }} ios_version: ${{ matrix.ios_version }} pass_fail: ${{ steps.run-smoketests.outcome == 'success' && ':white_check_mark:' || ':x:' }} @@ -165,12 +166,13 @@ jobs: sha: ${{ github.sha }} - name: Report to Slack (FullFunctionalTests) id: slack-fullfunctionaltests - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v2.0.0 with: payload-file-path: ${{ env.browser }}/slack-fullfunctionaltests.json + payload-templated: true + webhook: ${{ secrets.WEBHOOK_SLACK_TOKEN }} + webhook-type: incoming-webhook env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK ios_simulator: ${{ matrix.ios_simulator }} ios_version: ${{ matrix.ios_version }} pass_fail: ${{ steps.run-fullfunctionaltests.outcome == 'success' && ':white_check_mark:' || ':x:' }} diff --git a/.github/workflows/focus-ios-ui-tests.yml b/.github/workflows/focus-ios-ui-tests.yml index 6f53c74e17cd..37ce8e77c695 100644 --- a/.github/workflows/focus-ios-ui-tests.yml +++ b/.github/workflows/focus-ios-ui-tests.yml @@ -116,12 +116,13 @@ jobs: retention-days: 90 - name: Report to Slack id: slack - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v2.0.0 with: payload-file-path: ${{ env.browser }}/slack.json + payload-templated: true + webhook: ${{ secrets.WEBHOOK_SLACK_TOKEN }} + webhook-type: incoming-webhook env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK ios_simulator: ${{ matrix.ios_simulator }} pass_fail: ${{ steps.run-tests.outcome == 'success' && ':white_check_mark:' || ':x:' }} xcodebuild_test_plan: ${{ matrix.xcodebuild_test_plan }} From 0bdb91270aabb7e49ec0dc74f0c1f37b621b865c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 21:04:49 +0100 Subject: [PATCH 36/45] Refactor [v136] Auto update SPM with latest rust-component 136.0.20250116050343 (#24177) Auto update SPM with latest rust-component release 136.0.20250116050343 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- firefox-ios/Client.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- focus-ios/Blockzilla.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 4ad060bfcce4..974f1216c67e 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -26481,7 +26481,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift.git"; requirement = { kind = exactVersion; - version = 136.0.20250115050329; + version = 136.0.20250116050343; }; }; 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */ = { diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 42a91bedaa9e..46638e494de1 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/rust-components-swift.git", "state" : { - "revision" : "ce80ed1d87747988cecb49695537eeda645deb92", - "version" : "136.0.20250113194724" + "revision" : "34d3010b1aab2e2790a6d4c561a64b9a0563022b", + "version" : "136.0.20250116050343" } }, { diff --git a/focus-ios/Blockzilla.xcodeproj/project.pbxproj b/focus-ios/Blockzilla.xcodeproj/project.pbxproj index 7bc8d08a1e2d..00b1a90c0e9b 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.pbxproj +++ b/focus-ios/Blockzilla.xcodeproj/project.pbxproj @@ -7198,7 +7198,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift"; requirement = { kind = exactVersion; - version = 136.0.20250115050329; + version = 136.0.20250116050343; }; }; 8A0E7F2C2BA0F0E0006BC6B6 /* XCRemoteSwiftPackageReference "Fuzi" */ = { diff --git a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1c75cd78c414..1821f419c19c 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/mozilla/rust-components-swift", "state": { "branch": null, - "revision": "ce80ed1d87747988cecb49695537eeda645deb92", - "version": "136.0.20250113194724" + "revision": "34d3010b1aab2e2790a6d4c561a64b9a0563022b", + "version": "136.0.20250116050343" } }, { From 5e0ea4215dffaea0996ff460d7f638122e389273 Mon Sep 17 00:00:00 2001 From: Daniel Dervishi <58835213+DanielDervishi@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:41:25 -0500 Subject: [PATCH 37/45] Bugfix FXIOS-10559 [Password Generator] Swipe to close the password generator prompt gesture is hard to use (#24163) * Completed Ticket * improved fix * remove print statement --- .../BottomSheet/BottomSheetViewController.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift index 5de1cb59c0f2..7221fc914296 100644 --- a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift +++ b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift @@ -259,7 +259,10 @@ public class BottomSheetViewController: UIViewController, self.sheetView.transform = CGAffineTransform(translationX: 0, y: self.viewTranslation.y) }) case .ended: - if viewTranslation.y < 200 { + let velocity = recognizer.velocity(in: view) + if viewTranslation.y >= 200 || viewTranslation.y >= contentView.bounds.height / 2 || velocity.y >= 700 { + dismissSheetViewController() + } else { UIView.animate(withDuration: UX.animationDuration, delay: 0, usingSpringWithDamping: UX.springWithDamping, @@ -268,8 +271,6 @@ public class BottomSheetViewController: UIViewController, animations: { self.sheetView.transform = .identity }) - } else { - dismissSheetViewController() } default: break From 35dc42d1ab0775fd6675751d44994465a5604470 Mon Sep 17 00:00:00 2001 From: Clare So <1740517+clarmso@users.noreply.github.com> Date: Fri, 17 Jan 2025 04:01:36 -0500 Subject: [PATCH 38/45] Refactor MTE-4104 Bump slack-github-actions (#24186) * Bump slack-github-action for firefox-ios-update_* workflows * Bump slack-github-action for firefox-ios-autofill and firefox-ios-ui workflows * Use WEBHOOK_SLACK_TOKEN for Firefox-ios-ui workflow --- .../firefox-ios-autofill-playwrite-tests.yml | 7 ++++--- .github/workflows/firefox-ios-ui-tests.yml | 18 ++++++++++-------- ...x-ios-update_credential_provider_script.yml | 7 ++++--- ...-ios-update_remote_settings_data_script.yml | 9 +++++---- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/workflows/firefox-ios-autofill-playwrite-tests.yml b/.github/workflows/firefox-ios-autofill-playwrite-tests.yml index 32449c05e969..53f7da9deb60 100644 --- a/.github/workflows/firefox-ios-autofill-playwrite-tests.yml +++ b/.github/workflows/firefox-ios-autofill-playwrite-tests.yml @@ -54,11 +54,12 @@ jobs: - name: Send Slack notification if tests fail if: '${{ !cancelled() && !github.event.pull_request.head.repo.fork }}' id: slack - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v2.0.0 with: payload-file-path: "./test-fixtures/ci/slack-notification-payload-autofill-test.json" + payload-templated: true + webhook: ${{ secrets.WEBHOOK_SLACK_TOKEN }} + webhook-type: incoming-webhook env: JOB_STATUS: ${{ job.status == 'success' && ':white_check_mark:' || job.status == 'failure' && ':x:' }} JOB_STATUS_COLOR: ${{ job.status == 'success' && '#36a64f' || job.status == 'failure' && '#FF0000' }} - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK diff --git a/.github/workflows/firefox-ios-ui-tests.yml b/.github/workflows/firefox-ios-ui-tests.yml index 03de9d8d1b21..7d56c66a6cd8 100644 --- a/.github/workflows/firefox-ios-ui-tests.yml +++ b/.github/workflows/firefox-ios-ui-tests.yml @@ -4,8 +4,8 @@ on: workflow_dispatch: {} env: browser: firefox-ios - xcode_version: 16.1 - ios_version: 18.1 + xcode_version: 16.2 + ios_version: 18.2 ios_simulator_default: iPhone 16 xcodebuild_scheme: Fennec xcodebuild_target: Client @@ -184,12 +184,13 @@ jobs: retention-days: 90 - name: Report to Slack id: slack - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v2.0.0 with: payload-file-path: ${{ env.browser }}/slack.json + payload-templated: true + webhook: ${{ secrets.WEBHOOK_SLACK_TOKEN }} + webhook-type: incoming-webhook env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK ios_simulator: ${{ matrix.ios_simulator }} pass_fail: ${{ steps.passfail.outcome == 'success' && ':white_check_mark:' || ':x:' }} xcodebuild_test_plan: Smoketests @@ -267,12 +268,13 @@ jobs: retention-days: 90 - name: Report to Slack id: slack - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v2.0.0 with: payload-file-path: ${{ env.browser }}/slack.json + payload-templated: true + webhook: ${{ secrets.WEBHOOK_SLACK_TOKEN }} + webhook-type: incoming-webhook env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK ios_simulator: ${{ matrix.ios_simulator }} pass_fail: ${{ steps.run-tests.outcome == 'success' && ':white_check_mark:' || ':x:' }} xcodebuild_test_plan: FullFunctionalTestPlan diff --git a/.github/workflows/firefox-ios-update_credential_provider_script.yml b/.github/workflows/firefox-ios-update_credential_provider_script.yml index 51416ea56732..9972e436c529 100644 --- a/.github/workflows/firefox-ios-update_credential_provider_script.yml +++ b/.github/workflows/firefox-ios-update_credential_provider_script.yml @@ -53,14 +53,15 @@ jobs: - name: Send Slack to notifiy if github action fails if: '!cancelled()' id: slack - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v2.0.0 env: JOB_STATUS: ${{ job.status == 'success' && ':white_check_mark:' || job.status == 'failure' && ':x:' }} JOB_STATUS_COLOR: ${{ job.status == 'success' && '#36a64f' || job.status == 'failure' && '#FF0000' }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK with: payload-file-path: "./test-fixtures/ci/slack-notification-payload-autofill.json" + payload-templated: true + webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook call-firefox-ios-autofill-playwrite-tests: uses: ./.github/workflows/firefox-ios-autofill-playwrite-tests.yml secrets: diff --git a/.github/workflows/firefox-ios-update_remote_settings_data_script.yml b/.github/workflows/firefox-ios-update_remote_settings_data_script.yml index c25973b78834..ae04015074b6 100644 --- a/.github/workflows/firefox-ios-update_remote_settings_data_script.yml +++ b/.github/workflows/firefox-ios-update_remote_settings_data_script.yml @@ -65,11 +65,12 @@ jobs: - name: Send Slack to notifiy if github action fails if: '!cancelled()' id: slack - uses: slackapi/slack-github-action@v1.26.0 + uses: slackapi/slack-github-action@v2.0.0 env: JOB_STATUS: ${{ job.status == 'success' && ':white_check_mark:' || job.status == 'failure' && ':x:' }} JOB_STATUS_COLOR: ${{ job.status == 'success' && '#36a64f' || job.status == 'failure' && '#FF0000' }} - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK with: - payload-file-path: "./test-fixtures/ci/slack-notification-payload-remote-settings-fetch.json" \ No newline at end of file + payload-file-path: "./test-fixtures/ci/slack-notification-payload-remote-settings-fetch.json" + payload-templated: true + webhook: ${{ secrets.WEBHOOK_SLACK_TOKEN }} + webhook-type: incoming-webhook \ No newline at end of file From d233954905cd3e50de3dd03c27c8413828784f41 Mon Sep 17 00:00:00 2001 From: Clare So <1740517+clarmso@users.noreply.github.com> Date: Fri, 17 Jan 2025 04:02:35 -0500 Subject: [PATCH 39/45] Bugfix MTE-4100 Fix "Create a PR if there are changes in effective_tld_names file" Github Actions (#24142) * Ensure pr_title is assigned * Format commit-message * remove updates in Package.resolved --- .../workflows/firefox-ios-update-effective-tld-names-file.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/firefox-ios-update-effective-tld-names-file.yml b/.github/workflows/firefox-ios-update-effective-tld-names-file.yml index 73214d32b82c..d7a12a0debe6 100644 --- a/.github/workflows/firefox-ios-update-effective-tld-names-file.yml +++ b/.github/workflows/firefox-ios-update-effective-tld-names-file.yml @@ -72,7 +72,7 @@ jobs: author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> committer: GitHub token: ${{ secrets.GITHUB_TOKEN }} - commit-message: ${{ env.pr_title }} + commit-message: "Refactor [v${{ env.next_version }}] Update effective_tld_names file ${{ env.current_date }}" title: "Refactor [v${{ env.next_version }}] Update effective_tld_names file ${{ env.current_date }}" branch: ${{ env.branch_name }} body: ${{ env.pr_body }} From 1bdb2a8e89357713698a1b14a93a721932278610 Mon Sep 17 00:00:00 2001 From: dicarobinho <61138287+dicarobinho@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:08:40 +0200 Subject: [PATCH 40/45] Add FXIOS-10733 [ToS] Tracking for ToS Onboarding (#24123) * FXIOS-10733 Tracking for ToS Onboarding * Updated data reviews link * Fixed SwiftLint warning --- firefox-ios/Client.xcodeproj/project.pbxproj | 8 ++ .../Launch/LaunchCoordinator.swift | 2 + .../Models/TermsOfServiceTelemetry.swift | 38 ++++++++ .../PrivacyPreferencesViewController.swift | 2 + .../Views/TermsOfServiceViewController.swift | 3 + firefox-ios/Client/metrics.yaml | 87 +++++++++++++++++++ .../TermsOfServiceTelemetryTests.swift | 63 ++++++++++++++ 7 files changed, 203 insertions(+) create mode 100644 firefox-ios/Client/Frontend/Onboarding/Models/TermsOfServiceTelemetry.swift create mode 100644 firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/TermsOfServiceTelemetryTests.swift diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 974f1216c67e..893da3ad97e0 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -25,6 +25,8 @@ 0A76936B2C82018700103A6D /* TrackingProtectionBlockedTrackersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A76936A2C82018700103A6D /* TrackingProtectionBlockedTrackersView.swift */; }; 0A93C8AA2C87070300BEA143 /* TrackingProtectionConnectionStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A93C8A92C87070300BEA143 /* TrackingProtectionConnectionStatusView.swift */; }; 0A93C8AC2C870E7100BEA143 /* TrackingProtectionToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A93C8AB2C870E7100BEA143 /* TrackingProtectionToggleView.swift */; }; + 0ABCD45B2D355260005D704A /* TermsOfServiceTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABCD45A2D355255005D704A /* TermsOfServiceTelemetry.swift */; }; + 0ABCD45D2D356015005D704A /* TermsOfServiceTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABCD45C2D356006005D704A /* TermsOfServiceTelemetryTests.swift */; }; 0AC659272BF35854005C614A /* FxAWebViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC659262BF35854005C614A /* FxAWebViewModelTests.swift */; }; 0AC659292BF493CE005C614A /* MockFxAWebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC659282BF493CE005C614A /* MockFxAWebViewModel.swift */; }; 0AD3EEAC2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD3EEAB2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift */; }; @@ -2332,6 +2334,8 @@ 0A7D41DB98DDB127A2B8C544 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/Menu.strings; sourceTree = ""; }; 0A93C8A92C87070300BEA143 /* TrackingProtectionConnectionStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingProtectionConnectionStatusView.swift; sourceTree = ""; }; 0A93C8AB2C870E7100BEA143 /* TrackingProtectionToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingProtectionToggleView.swift; sourceTree = ""; }; + 0ABCD45A2D355255005D704A /* TermsOfServiceTelemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsOfServiceTelemetry.swift; sourceTree = ""; }; + 0ABCD45C2D356006005D704A /* TermsOfServiceTelemetryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsOfServiceTelemetryTests.swift; sourceTree = ""; }; 0AC659262BF35854005C614A /* FxAWebViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FxAWebViewModelTests.swift; sourceTree = ""; }; 0AC659282BF493CE005C614A /* MockFxAWebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFxAWebViewModel.swift; sourceTree = ""; }; 0AD3EEAB2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedCenteredTableViewCell.swift; sourceTree = ""; }; @@ -12116,6 +12120,7 @@ 8AC225632B6D3F9600CDA7FD /* Telemetry */ = { isa = PBXGroup; children = ( + 0ABCD45C2D356006005D704A /* TermsOfServiceTelemetryTests.swift */, 8AC225642B6D3FA400CDA7FD /* HomepageTelemetryTests.swift */, E1B9A2C32CADA78300F6A0E9 /* ToolbarTelemetryTests.swift */, 0A686B3B2CDB70DC0090E146 /* MainMenuTelemetryTests.swift */, @@ -13108,6 +13113,7 @@ 435D660423D794B90046EFA2 /* UpdateViewModel.swift */, 74B420C82A1D0D7A00370E53 /* OnboardingInstructionsPopupInfoModel.swift */, C8CD80DB2A1E8C970097C3AE /* OnboardingTelemetryUtility.swift */, + 0ABCD45A2D355255005D704A /* TermsOfServiceTelemetry.swift */, 81020C932BB5B026007B8481 /* OnboardingMultipleChoiceButtonViewModel.swift */, ); path = Models; @@ -16803,6 +16809,7 @@ E174963A2992B42C0096900A /* CreditCardSectionHeader.swift in Sources */, 8A3EF7F42A2FCF5700796E3A /* ExportBrowserDataSetting.swift in Sources */, 8CC6170B2C358BAC001C7688 /* ActionToast.swift in Sources */, + 0ABCD45B2D355260005D704A /* TermsOfServiceTelemetry.swift in Sources */, 2128E27B292E624400FB91BE /* SendToDeviceActivity.swift in Sources */, E63ED7D81BFCD9990097D08E /* LoginDetailTableViewCell.swift in Sources */, C855728229AE7F1700AF32B0 /* SurveySurfaceManager.swift in Sources */, @@ -17253,6 +17260,7 @@ 213B67A827CE721E000542F5 /* StartAtHomeHelperTests.swift in Sources */, 8A13FA892AD82BC8007527AB /* AppSendTabDelegateTests.swift in Sources */, 8A7653C228A2E57D00924ABF /* PocketDataAdaptorTests.swift in Sources */, + 0ABCD45D2D356015005D704A /* TermsOfServiceTelemetryTests.swift in Sources */, C8CD80D42A1E268C0097C3AE /* MockGleanPlumbEvaluationUtility.swift in Sources */, E1AEC176286E0CF500062E29 /* JumpBackInViewModelTests.swift in Sources */, 8A7AE4472BAC78230072DAEC /* MockLibraryNavigationHandler.swift in Sources */, diff --git a/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift b/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift index b95f2f252610..7d94948be94e 100644 --- a/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift +++ b/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift @@ -50,10 +50,12 @@ class LaunchCoordinator: BaseCoordinator, // MARK: - Terms of Service private func presentTermsOfService(with manager: TermsOfServiceManager, isFullScreen: Bool) { + TermsOfServiceTelemetry().termsOfServiceScreenDisplayed() let viewController = TermsOfServiceViewController(profile: profile, windowUUID: windowUUID) viewController.didFinishFlow = { [weak self] in guard let self = self else { return } manager.setAccepted() + TermsOfServiceTelemetry().termsOfServiceAcceptButtonTapped() let sendTechnicalData = profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true manager.shouldSendTechnicalData(value: sendTechnicalData) diff --git a/firefox-ios/Client/Frontend/Onboarding/Models/TermsOfServiceTelemetry.swift b/firefox-ios/Client/Frontend/Onboarding/Models/TermsOfServiceTelemetry.swift new file mode 100644 index 000000000000..3b445b300c59 --- /dev/null +++ b/firefox-ios/Client/Frontend/Onboarding/Models/TermsOfServiceTelemetry.swift @@ -0,0 +1,38 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Glean + +struct TermsOfServiceTelemetry { + func termsOfServiceScreenDisplayed() { + GleanMetrics.Onboarding.termsOfServiceCard.record() + } + + func technicalInteractionDataSwitched(to value: Bool) { + let extra = GleanMetrics.Onboarding.ToggleTechnicalInteractionDataExtra(changedTo: value) + GleanMetrics.Onboarding.toggleTechnicalInteractionData.record(extra) + } + + func automaticCrashReportsSwitched(to value: Bool) { + let extra = GleanMetrics.Onboarding.ToggleAutomaticCrashReportsExtra(changedTo: value) + GleanMetrics.Onboarding.toggleAutomaticCrashReports.record(extra) + } + + func termsOfServiceLinkTapped() { + GleanMetrics.Onboarding.termsOfServiceLinkClicked.record() + } + + func termsOfServicePrivacyNoticeLinkTapped() { + GleanMetrics.Onboarding.termsOfServicePrivacyNoticeLinkClicked.record() + } + + func termsOfServiceManageLinkTapped() { + GleanMetrics.Onboarding.termsOfServiceManageLinkClicked.record() + } + + func termsOfServiceAcceptButtonTapped() { + GleanMetrics.Onboarding.termsOfServiceAccepted.record() + } +} diff --git a/firefox-ios/Client/Frontend/Onboarding/Views/PrivacyPreferencesViewController.swift b/firefox-ios/Client/Frontend/Onboarding/Views/PrivacyPreferencesViewController.swift index 83a6b9742e93..535a9fc43bd8 100644 --- a/firefox-ios/Client/Frontend/Onboarding/Views/PrivacyPreferencesViewController.swift +++ b/firefox-ios/Client/Frontend/Onboarding/Views/PrivacyPreferencesViewController.swift @@ -163,10 +163,12 @@ final class PrivacyPreferencesViewController: UIViewController, private func setupCallbacks() { crashReportsSwitch.switchCallback = { [weak self] value in self?.profile.prefs.setBool(value, forKey: AppConstants.prefSendCrashReports) + TermsOfServiceTelemetry().automaticCrashReportsSwitched(to: value) } technicalDataSwitch.switchCallback = { [weak self] value in self?.profile.prefs.setBool(value, forKey: AppConstants.prefSendUsageData) + TermsOfServiceTelemetry().technicalInteractionDataSwitched(to: value) } crashReportsSwitch.learnMoreCallBack = { [weak self] in diff --git a/firefox-ios/Client/Frontend/Onboarding/Views/TermsOfServiceViewController.swift b/firefox-ios/Client/Frontend/Onboarding/Views/TermsOfServiceViewController.swift index a9dc6a914182..5afb92d789b2 100644 --- a/firefox-ios/Client/Frontend/Onboarding/Views/TermsOfServiceViewController.swift +++ b/firefox-ios/Client/Frontend/Onboarding/Views/TermsOfServiceViewController.swift @@ -252,16 +252,19 @@ class TermsOfServiceViewController: UIViewController, Themeable { // MARK: - Button actions @objc private func presentTermsOfService(_ gesture: UIGestureRecognizer) { + TermsOfServiceTelemetry().termsOfServiceLinkTapped() presentLink(with: URL(string: Links.termsOfService)) } @objc private func presentPrivacyNotice(_ gesture: UIGestureRecognizer) { + TermsOfServiceTelemetry().termsOfServicePrivacyNoticeLinkTapped() presentLink(with: URL(string: Links.privacyNotice)) } @objc private func presentManagePreferences(_ gesture: UIGestureRecognizer) { + TermsOfServiceTelemetry().termsOfServiceManageLinkTapped() let managePreferencesVC = PrivacyPreferencesViewController(profile: profile, windowUUID: windowUUID) if UIDevice.current.userInterfaceIdiom != .phone { managePreferencesVC.modalPresentationStyle = .formSheet diff --git a/firefox-ios/Client/metrics.yaml b/firefox-ios/Client/metrics.yaml index 5144825a23e9..87faaeb29387 100755 --- a/firefox-ios/Client/metrics.yaml +++ b/firefox-ios/Client/metrics.yaml @@ -1538,6 +1538,93 @@ key_commands: # Onboarding metrics onboarding: + terms_of_service_card: + type: event + description: | + User viewed the terms of service onboarding card. + bugs: + - https://mozilla-hub.atlassian.net/browse/FXIOS-10733 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/24123 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" + terms_of_service_accepted: + type: event + description: | + User clicked accept button on the terms of service onboarding card. + bugs: + - https://mozilla-hub.atlassian.net/browse/FXIOS-10733 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/24123 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" + terms_of_service_link_clicked: + type: event + description: | + User clicked the terms of service link on the onboarding card. + bugs: + - https://mozilla-hub.atlassian.net/browse/FXIOS-10733 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/24123 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" + terms_of_service_manage_link_clicked: + type: event + description: | + User clicked the manage link on the terms of service onboarding card. + bugs: + - https://mozilla-hub.atlassian.net/browse/FXIOS-10733 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/24123 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" + terms_of_service_privacy_notice_link_clicked: + type: event + description: | + User clicked the privacy policy link on the terms of service onboarding card. + bugs: + - https://mozilla-hub.atlassian.net/browse/FXIOS-10733 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/24123 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" + toggle_technical_interaction_data: + type: event + description: | + User toggled the preference to share technical and interaction data from the manage modal on the terms of service onboarding card. + extra_keys: + changed_to: + type: boolean + description: | + The value of the technical interaction data switch. + bugs: + - https://mozilla-hub.atlassian.net/browse/FXIOS-10733 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/24123 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" + toggle_automatic_crash_reports: + type: event + description: | + User toggled the preference to automatically send crash reports from the manage modal on the terms of service onboarding card. + extra_keys: + changed_to: + type: boolean + description: | + The value of the automatic crash reports switch. + bugs: + - https://mozilla-hub.atlassian.net/browse/FXIOS-10733 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/24123 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" card_view: type: event description: | diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/TermsOfServiceTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/TermsOfServiceTelemetryTests.swift new file mode 100644 index 000000000000..59338b708acb --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/TermsOfServiceTelemetryTests.swift @@ -0,0 +1,63 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Glean +import XCTest + +@testable import Client + +final class TermsOfServiceTelemetryTests: XCTestCase { + var subject: TermsOfServiceTelemetry? + + override func setUp() { + super.setUp() + // Due to changes allow certain custom pings to implement their own opt-out + // independent of Glean, custom pings may need to be registered manually in + // tests in order to puth them in a state in which they can collect data. + Glean.shared.registerPings(GleanMetrics.Pings.shared) + Glean.shared.resetGlean(clearStores: true) + subject = TermsOfServiceTelemetry() + } + + func testRecordTermsOfServiceScreenDisplayedThenGleanIsCalled() throws { + subject?.termsOfServiceScreenDisplayed() + testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.termsOfServiceCard) + } + + func testRecordTermsOfServiceTechnicalInteractionDataSwitchedThenGleanIsCalled() throws { + subject?.technicalInteractionDataSwitched(to: true) + testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.toggleTechnicalInteractionData) + + let resultValue = try XCTUnwrap(GleanMetrics.Onboarding.toggleTechnicalInteractionData.testGetValue()) + XCTAssertEqual(resultValue[0].extra?["changed_to"], "true") + } + + func testRecordTermsOfServiceAutomaticCrashReportsSwitchedThenGleanIsCalled() throws { + subject?.automaticCrashReportsSwitched(to: true) + testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.toggleAutomaticCrashReports) + + let resultValue = try XCTUnwrap(GleanMetrics.Onboarding.toggleAutomaticCrashReports.testGetValue()) + XCTAssertEqual(resultValue[0].extra?["changed_to"], "true") + } + + func testRecordTermsOfServiceLinkTappedThenGleanIsCalled() throws { + subject?.termsOfServiceLinkTapped() + testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.termsOfServiceLinkClicked) + } + + func testRecordTermsOfServicePrivacyNoticeLinkTappedThenGleanIsCalled() throws { + subject?.termsOfServicePrivacyNoticeLinkTapped() + testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.termsOfServicePrivacyNoticeLinkClicked) + } + + func testRecordTermsOfServiceManageLinkTappedThenGleanIsCalled() throws { + subject?.termsOfServiceManageLinkTapped() + testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.termsOfServiceManageLinkClicked) + } + + func testRecordTermsOfServiceAcceptButtonTappedThenGleanIsCalled() throws { + subject?.termsOfServiceAcceptButtonTapped() + testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.termsOfServiceAccepted) + } +} From 250df85ed0b2f20f5ad4502be9e8c6e12f303935 Mon Sep 17 00:00:00 2001 From: dragosb01 <134391433+dragosb01@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:20:07 +0200 Subject: [PATCH 41/45] Fixes MTE-4140 - for multiple ipad failures (#24200) --- .../Tests/FullFunctionalTestPlan.xctestplan | 1 - .../firefox-ios-tests/Tests/Smoketest2.xctestplan | 1 + .../Tests/XCUITests/BookmarksTests.swift | 2 +- .../Tests/XCUITests/BrowsingPDFTests.swift | 14 ++------------ .../Tests/XCUITests/SearchTest.swift | 1 + .../Tests/XCUITests/TopTabsTest.swift | 2 +- 6 files changed, 6 insertions(+), 15 deletions(-) diff --git a/firefox-ios/firefox-ios-tests/Tests/FullFunctionalTestPlan.xctestplan b/firefox-ios/firefox-ios-tests/Tests/FullFunctionalTestPlan.xctestplan index 93be1f8c4fdf..3ee833b923f1 100644 --- a/firefox-ios/firefox-ios-tests/Tests/FullFunctionalTestPlan.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/FullFunctionalTestPlan.xctestplan @@ -44,7 +44,6 @@ "BookmarksTests\/testDesktopFoldersArePresent()", "BookmarksTests\/testUndoDeleteBookmark()", "BrowsingPDFTests\/testBookmarkPDF()", - "BrowsingPDFTests\/testOpenLinkFromPDF()", "BrowsingPDFTests\/testPinPDFtoTopSites()", "ClipBoardTests\/testClipboardPasteAndGo()", "CreditCardsTests\/testAccessingTheCreditCardsSection()", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan index bd391222251a..b9b896ccffe5 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest2.xctestplan @@ -31,6 +31,7 @@ "BookmarksTests", "BrowsingPDFTests\/testLongPressOnPDFLink()", "BrowsingPDFTests\/testLongPressOnPDFLinkToAddToReadingList()", + "BrowsingPDFTests\/testOpenLinkFromPDF()", "BrowsingPDFTests\/testOpenPDFViewer()", "ClipBoardTests", "CreditCardsTests", diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift index e9265297254f..8468fbe947fe 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift @@ -435,7 +435,7 @@ class BookmarksTests: BaseTestCase { private func longPressBookmarkCell() { let bookMarkCell = app.cells["BookmarksCell"] - scrollToElement(bookMarkCell) + app.swipeUp() bookMarkCell.press(forDuration: 1.5) } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift index 8f72c7236402..e8d7c765057a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift @@ -58,12 +58,7 @@ class BrowsingPDFTests: BaseTestCase { navigator.openURL(PDF_website["url"]!) waitUntilPageLoad() // Long press on a link on the pdf and check the options shown - if !iPad() { - // Workaround for https://github.com/mozilla-mobile/firefox-ios/issues/23473 - longPressOnPdfLink() - } else { - app.webViews.links.element(boundBy: 0).pressAtPoint(CGPoint(x: 10, y: 0), forDuration: 3) - } + longPressOnPdfLink() waitForElementsToExist( [ @@ -85,12 +80,7 @@ class BrowsingPDFTests: BaseTestCase { navigator.openURL(PDF_website["url"]!) waitUntilPageLoad() // Long press on a link on the pdf and check the options shown - if !iPad() { - // Workaround for https://github.com/mozilla-mobile/firefox-ios/issues/23473 - longPressOnPdfLink() - } else { - app.webViews.links.element(boundBy: 0).pressAtPoint(CGPoint(x: 10, y: 0), forDuration: 3) - } + longPressOnPdfLink() mozWaitForElementToExist(app.staticTexts[PDF_website["longUrlValue"]!]) app.buttons["Add to Reading List"].waitAndTap() diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift index 83220337d147..a49acd00258f 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift @@ -469,6 +469,7 @@ class SearchTests: BaseTestCase { } else { mozWaitForElementToNotExist(app.tables.buttons[StandardImageIdentifiers.Large.appendUpLeft]) mozWaitForElementToExist(app.tables["SiteTable"].staticTexts["Firefox Suggest"]) + mozWaitForElementToExist(app.tables.cells.firstMatch) XCTAssertTrue(app.tables.cells.count <= 3) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift index 6755a9b4ef4a..047dcb04b90e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift @@ -350,7 +350,7 @@ class TopTabsTest: BaseTestCase { if !iPad() { tabsTrayCell.staticTexts.element(boundBy: 3).waitAndTap() } else { - XCTAssertEqual(tabsTrayCell.count, Int(numTab!)) + XCTAssertTrue(Int(numTab!) == 11) tabsTrayCell.staticTexts.element(boundBy: 6).waitAndTap() } // The current tab’s thumbnail is focused in the “Open Tabs” view From b71a363f1d1a97786f892901d084269807f8a312 Mon Sep 17 00:00:00 2001 From: dicarobinho <61138287+dicarobinho@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:26:14 +0200 Subject: [PATCH 42/45] =?UTF-8?q?Bugfix=20FXIOS-9829=20=E2=81=83=20Enhance?= =?UTF-8?q?d=20Tracking=20Protection=20certificates=20details=20screen=20a?= =?UTF-8?q?ccessibility=20identifiers=20(#24203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FXIOS-9829 #21585 ⁃ Enhanced Tracking Protection certificates details screen accessibility identifiers --- .../Client/Application/AccessibilityIdentifiers.swift | 6 ++++++ .../CertificatesHelpers/CertificatesCell.swift | 8 ++++++++ .../CertificatesHelpers/CertificatesHeaderView.swift | 6 ++++++ .../CertificatesViewController.swift | 11 ++++++++--- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift index 5f5a546ed4dc..d45687c37913 100644 --- a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift +++ b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift @@ -142,6 +142,12 @@ struct AccessibilityIdentifiers { static let closeButton = "TrackingProtectionDetails.CloseButton" static let backButton = "TrackingProtectionDetails.BackButton" static let titleLabel = "TrackingProtectionDetails.TitleLabel" + static let certificatesTitleLabel = "TrackingProtectionDetails.CertificatesTitleLabel" + static let tableView = "TrackingProtectionDetails.TableView" + static let tableViewHeader = "TrackingProtectionDetails.TableViewHeader" + static let sectionLabel = "TrackingProtectionDetails.SectionLabel" + static let allSectionItems = "TrackingProtectionDetails.AllSectionItems" + static let itemLabel = "TrackingProtectionDetails.ItemLabel" } struct BlockedTrackers { diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesCell.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesCell.swift index 1e4544a4a49f..f3c7f3ccf7c5 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesCell.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesCell.swift @@ -75,6 +75,13 @@ final class CertificatesCell: UITableViewCell, ReusableCell, ThemeApplicable { } } + // MARK: Accessibility + func setupAccessibilityIdentifiers() { + typealias A11y = AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen + sectionLabel.accessibilityIdentifier = A11y.sectionLabel + allSectionItemsStackView.accessibilityIdentifier = A11y.allSectionItems + } + func applyTheme(theme: Theme) { backgroundColor = theme.colors.layer5 sectionLabel.textColor = theme.colors.textPrimary @@ -88,6 +95,7 @@ final class CertificatesCell: UITableViewCell, ReusableCell, ThemeApplicable { label.textAlignment = isTitle ? .right : .left label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping + label.accessibilityIdentifier = AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.itemLabel } return itemLabel } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesHeaderView.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesHeaderView.swift index 9a63ad87ea4b..3e032290711a 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesHeaderView.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesHeaderView.swift @@ -48,4 +48,10 @@ class CertificatesHeaderView: UITableViewHeaderFooterView, ReusableCell { headerStackView.backgroundColor = theme.colors.layer5 } + + // MARK: Accessibility + func setupAccessibilityIdentifiers() { + typealias A11y = AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen + headerStackView.accessibilityIdentifier = A11y.tableViewHeader + } } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift index 7ae766083153..78be5960073d 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift @@ -208,6 +208,7 @@ class CertificatesViewController: UIViewController, } } headerView.configure(withItems: items, theme: currentTheme()) + headerView.setupAccessibilityIdentifiers() return headerView } @@ -222,6 +223,7 @@ class CertificatesViewController: UIViewController, } let certificate = model.certificates[model.selectedCertificateIndex] + cell.setupAccessibilityIdentifiers() switch CertificatesItemType(rawValue: indexPath.row) { case .subjectName: @@ -264,13 +266,16 @@ class CertificatesViewController: UIViewController, // MARK: Accessibility private func setupAccessibilityIdentifiers() { + typealias A11y = AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen headerView.setupAccessibility( closeButtonA11yLabel: .Menu.EnhancedTrackingProtection.AccessibilityLabels.CloseButton, - closeButtonA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.closeButton, - titleA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.titleLabel, + closeButtonA11yId: A11y.closeButton, + titleA11yId: A11y.titleLabel, backButtonA11yLabel: .Menu.EnhancedTrackingProtection.AccessibilityLabels.BackButton, - backButtonA11yId: AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.backButton + backButtonA11yId: A11y.backButton ) + titleLabel.accessibilityIdentifier = A11y.certificatesTitleLabel + certificatesTableView.accessibilityIdentifier = A11y.tableView } // MARK: View Transitions From a87d7b16dc2e60e9f3e1ba9f7793e3f5903cc3cf Mon Sep 17 00:00:00 2001 From: dicarobinho <61138287+dicarobinho@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:27:21 +0200 Subject: [PATCH 43/45] =?UTF-8?q?Bugfix=20FXIOS-10652=20=E2=81=83=20Felt?= =?UTF-8?q?=20privacy-Unified=20panel]=20-=20"Privacy=20settings"=20=20and?= =?UTF-8?q?=20"View=20certificate"=20are=20not=20underlined=20(#24201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FXIOS-10652 #23309 ⁃ Felt privacy-Unified panel] - "Privacy settings" and "View certificate" are not underlined --- .../Sources/ComponentLibrary/Buttons/LinkButton.swift | 10 ++++++++++ .../TrackingProtectionDetailsViewController.swift | 1 + .../TrackingProtectionViewController.swift | 1 + 3 files changed, 12 insertions(+) diff --git a/BrowserKit/Sources/ComponentLibrary/Buttons/LinkButton.swift b/BrowserKit/Sources/ComponentLibrary/Buttons/LinkButton.swift index f992457ab6f6..ccaafeb60de1 100644 --- a/BrowserKit/Sources/ComponentLibrary/Buttons/LinkButton.swift +++ b/BrowserKit/Sources/ComponentLibrary/Buttons/LinkButton.swift @@ -59,6 +59,16 @@ open class LinkButton: UIButton, ThemeApplicable { configuration = updatedConfiguration } + public func applyUnderline(underlinedText: String) { + let attributedString = NSAttributedString( + string: underlinedText, + attributes: [ + .underlineStyle: NSUnderlineStyle.single.rawValue + ] + ) + setAttributedTitle(attributedString, for: .normal) + } + // MARK: ThemeApplicable public func applyTheme(theme: Theme) { diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionDetailsViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionDetailsViewController.swift index 55a351d87e5d..7bf4ce3646df 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionDetailsViewController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionDetailsViewController.swift @@ -175,6 +175,7 @@ class TrackingProtectionDetailsViewController: UIViewController, Themeable { font: FXFontStyles.Regular.footnote.scaledFont() ) viewCertificatesButton.configure(viewModel: certificatesButtonViewModel) + viewCertificatesButton.applyUnderline(underlinedText: model.viewCertificatesButtonTitle) baseView.addArrangedSubview(viewCertificatesButton) } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift index 6226f831843e..a7a1f31c2712 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift @@ -402,6 +402,7 @@ class TrackingProtectionViewController: UIViewController, font: FXFontStyles.Regular.footnote.scaledFont() ) settingsLinkButton.configure(viewModel: settingsButtonViewModel) + settingsLinkButton.applyUnderline(underlinedText: model.settingsButtonTitle) } private func setupProtectionSettingsView() { From 9a2cd131591ee145deaf9df5fa6f3772984541c1 Mon Sep 17 00:00:00 2001 From: dicarobinho <61138287+dicarobinho@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:29:53 +0200 Subject: [PATCH 44/45] =?UTF-8?q?Bugfix=20FXIOS-10655=20=E2=81=83=20[Felt?= =?UTF-8?q?=20privacy-Unified=20panel]=20-=20The=20name=20in=20the=20Commo?= =?UTF-8?q?n=20Name=20should=20be=20blue=20and=20underlined=20on=20the=20c?= =?UTF-8?q?ertificate=20sheet=20(#24199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FXIOS-10655 #23315 ⁃ [Felt privacy-Unified panel] - The name in the Common Name should be blue and underlined on the certificate sheet --- .../CertificatesCell.swift | 40 +++++++++++++------ .../CertificatesViewController.swift | 3 +- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesCell.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesCell.swift index f3c7f3ccf7c5..bfaf1accfa0a 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesCell.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHelpers/CertificatesCell.swift @@ -62,15 +62,21 @@ final class CertificatesCell: UITableViewCell, ReusableCell, ThemeApplicable { fatalError("init(coder:) has not been implemented") } - func configure(theme: Theme, sectionTitle: String, items: CertificateItems) { + func configure(theme: Theme, sectionTitle: String, items: CertificateItems, isIssuerName: Bool = false) { applyTheme(theme: theme) sectionLabel.text = sectionTitle for (key, value) in items { + let isUnderlined = isIssuerName && key == .Menu.EnhancedTrackingProtection.certificateCommonName let stackView = getSectionItemStackView() - let titleLabel = getItemLabel(theme: theme, with: key, isTitle: true) + let titleLabel = getItemLabel(theme: theme, with: key, isTitle: true, isUnderlined: isUnderlined) titleLabel.widthAnchor.constraint(equalToConstant: UX.sectionLabelWidth).isActive = true stackView.addArrangedSubview(titleLabel) - stackView.addArrangedSubview(getItemLabel(theme: theme, with: value, isTitle: false)) + stackView.addArrangedSubview(getItemLabel( + theme: theme, + with: value, + isTitle: false, + isUnderlined: isUnderlined + )) allSectionItemsStackView.addArrangedSubview(stackView) } } @@ -87,15 +93,25 @@ final class CertificatesCell: UITableViewCell, ReusableCell, ThemeApplicable { sectionLabel.textColor = theme.colors.textPrimary } - private func getItemLabel(theme: Theme, with title: String, isTitle: Bool) -> UILabel { - let itemLabel: UILabel = .build { label in - label.font = FXFontStyles.Bold.headline.scaledFont() - label.textColor = isTitle ? theme.colors.textSecondary : theme.colors.textPrimary - label.text = title - label.textAlignment = isTitle ? .right : .left - label.numberOfLines = 0 - label.lineBreakMode = .byWordWrapping - label.accessibilityIdentifier = AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.itemLabel + private func getItemLabel(theme: Theme, with title: String, isTitle: Bool, isUnderlined: Bool) -> UILabel { + let itemLabel: UILabel = .build() + itemLabel.font = FXFontStyles.Bold.headline.scaledFont() + itemLabel.textAlignment = isTitle ? .right : .left + itemLabel.numberOfLines = 0 + itemLabel.lineBreakMode = .byWordWrapping + itemLabel.accessibilityIdentifier = AccessibilityIdentifiers.EnhancedTrackingProtection.DetailsScreen.itemLabel + if isUnderlined, !isTitle { + let attributedString = NSAttributedString( + string: title, + attributes: [ + .underlineStyle: NSUnderlineStyle.single.rawValue + ] + ) + itemLabel.textColor = theme.colors.textAccent + itemLabel.attributedText = attributedString + } else { + itemLabel.textColor = isTitle ? theme.colors.textSecondary : theme.colors.textPrimary + itemLabel.text = title } return itemLabel } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift index 78be5960073d..ea309dedfa42 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift @@ -242,7 +242,8 @@ class CertificatesViewController: UIViewController, sectionTitle: .Menu.EnhancedTrackingProtection.certificateIssuerName, items: [(.Menu.EnhancedTrackingProtection.certificateIssuerCountry, country), (.Menu.EnhancedTrackingProtection.certificateIssuerOrganization, organization), - (.Menu.EnhancedTrackingProtection.certificateCommonName, commonName)]) + (.Menu.EnhancedTrackingProtection.certificateCommonName, commonName)], + isIssuerName: true) } case .validity: From 6d8a691b48f1eb5d3e0aecc96f70c6480ff9d232 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:03:01 -0500 Subject: [PATCH 45/45] Refactor [v136] Auto update SPM with latest rust-component 136.0.20250117050350 (#24211) Auto update SPM with latest rust-component release 136.0.20250117050350 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- firefox-ios/Client.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- focus-ios/Blockzilla.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 893da3ad97e0..2f9c88271e9a 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -26489,7 +26489,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift.git"; requirement = { kind = exactVersion; - version = 136.0.20250116050343; + version = 136.0.20250117050350; }; }; 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */ = { diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 46638e494de1..fd67ee0b4111 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/rust-components-swift.git", "state" : { - "revision" : "34d3010b1aab2e2790a6d4c561a64b9a0563022b", - "version" : "136.0.20250116050343" + "revision" : "a17ff110a1b6550afd905038bd956b9c9b44eb45", + "version" : "136.0.20250117050350" } }, { diff --git a/focus-ios/Blockzilla.xcodeproj/project.pbxproj b/focus-ios/Blockzilla.xcodeproj/project.pbxproj index 00b1a90c0e9b..dfa7a73de3c0 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.pbxproj +++ b/focus-ios/Blockzilla.xcodeproj/project.pbxproj @@ -7198,7 +7198,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift"; requirement = { kind = exactVersion; - version = 136.0.20250116050343; + version = 136.0.20250117050350; }; }; 8A0E7F2C2BA0F0E0006BC6B6 /* XCRemoteSwiftPackageReference "Fuzi" */ = { diff --git a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1821f419c19c..b3be88f0d6cf 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/mozilla/rust-components-swift", "state": { "branch": null, - "revision": "34d3010b1aab2e2790a6d4c561a64b9a0563022b", - "version": "136.0.20250116050343" + "revision": "a17ff110a1b6550afd905038bd956b9c9b44eb45", + "version": "136.0.20250117050350" } }, {