From 5a9608ec4dc8d45d9531657bdb4563248f357df2 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 12:13:38 +0530 Subject: [PATCH 01/24] Introduce `ABTestVariationProvider` for reading `ABTest` from Woo layer. --- .../Experiments.xcodeproj/project.pbxproj | 4 ++++ .../Experiments/ABTestVariationProvider.swift | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 Experiments/Experiments/ABTestVariationProvider.swift diff --git a/Experiments/Experiments.xcodeproj/project.pbxproj b/Experiments/Experiments.xcodeproj/project.pbxproj index cfbaeaf7bb2..071ba6bbdc3 100644 --- a/Experiments/Experiments.xcodeproj/project.pbxproj +++ b/Experiments/Experiments.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ BC10218D75FEA979BDA1E68C /* Pods_Experiments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33CEC0C5283FD4C9EF8C6A3C /* Pods_Experiments.framework */; }; C8E16F0EE6954B58A1C402F0 /* Pods_ExperimentsTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAC7C082DD376957B4676401 /* Pods_ExperimentsTests.framework */; }; CC53FB48275E426900C4CA4F /* ABTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC53FB47275E426900C4CA4F /* ABTest.swift */; }; + EE2EDFDF29879331004E702B /* ABTestVariationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2EDFDE29879331004E702B /* ABTestVariationProvider.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -47,6 +48,7 @@ AAC7C082DD376957B4676401 /* Pods_ExperimentsTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ExperimentsTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AF72D9DB7771E7A5105C88B0 /* Pods-Experiments.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Experiments.release.xcconfig"; path = "Target Support Files/Pods-Experiments/Pods-Experiments.release.xcconfig"; sourceTree = ""; }; CC53FB47275E426900C4CA4F /* ABTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABTest.swift; sourceTree = ""; }; + EE2EDFDE29879331004E702B /* ABTestVariationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABTestVariationProvider.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -100,6 +102,7 @@ 0270C0A427069B8900FC799F /* DefaultFeatureFlagService.swift */, 0270C0A627069BA500FC799F /* BuildConfiguration.swift */, CC53FB47275E426900C4CA4F /* ABTest.swift */, + EE2EDFDE29879331004E702B /* ABTestVariationProvider.swift */, ); path = Experiments; sourceTree = ""; @@ -314,6 +317,7 @@ 0270C0A327069B7800FC799F /* FeatureFlagService.swift in Sources */, 0270C0A527069B8900FC799F /* DefaultFeatureFlagService.swift in Sources */, 0270C09C27069AE700FC799F /* FeatureFlag.swift in Sources */, + EE2EDFDF29879331004E702B /* ABTestVariationProvider.swift in Sources */, 0270C0A727069BA500FC799F /* BuildConfiguration.swift in Sources */, CC53FB48275E426900C4CA4F /* ABTest.swift in Sources */, ); diff --git a/Experiments/Experiments/ABTestVariationProvider.swift b/Experiments/Experiments/ABTestVariationProvider.swift new file mode 100644 index 00000000000..4db9af7e7e0 --- /dev/null +++ b/Experiments/Experiments/ABTestVariationProvider.swift @@ -0,0 +1,16 @@ +import AutomatticTracks + +/// For getting the variation of a `ABTest` +public protocol ABTestVariationProvider { + /// Returns the `Variation` for the provided `ABTest` + func variation(for abTest: ABTest) -> Variation +} + +/// Default implementation of `ABTestVariationProvider` +public struct DefaultABTestVariationProvider: ABTestVariationProvider { + public init() { } + + public func variation(for abTest: ABTest) -> Variation { + abTest.variation + } +} From b8868f113b2cc628ecd6e93247067087af5af844 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 12:15:31 +0530 Subject: [PATCH 02/24] `AuthenticationManager` - Inject `ABTestVariationProvider` and use it for reading ABTest. --- .../Authentication/AuthenticationManager.swift | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/WooCommerce/Classes/Authentication/AuthenticationManager.swift b/WooCommerce/Classes/Authentication/AuthenticationManager.swift index 9bf14754e3c..1ad0c6bf438 100644 --- a/WooCommerce/Classes/Authentication/AuthenticationManager.swift +++ b/WooCommerce/Classes/Authentication/AuthenticationManager.swift @@ -9,6 +9,8 @@ import struct Networking.Settings import protocol Experiments.FeatureFlagService import protocol Storage.StorageManagerType import class Networking.DefaultApplicationPasswordUseCase +import protocol Experiments.ABTestVariationProvider +import struct Experiments.DefaultABTestVariationProvider /// Encapsulates all of the interactions with the WordPress Authenticator /// @@ -45,15 +47,19 @@ class AuthenticationManager: Authentication { private let analytics: Analytics + private let abTestVariationProvider: ABTestVariationProvider + /// Keeps a reference to the checker private var postSiteCredentialLoginChecker: PostSiteCredentialLoginChecker? init(storageManager: StorageManagerType = ServiceLocator.storageManager, featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService, - analytics: Analytics = ServiceLocator.analytics) { + analytics: Analytics = ServiceLocator.analytics, + abTestVariationProvider: ABTestVariationProvider = DefaultABTestVariationProvider()) { self.storageManager = storageManager self.featureFlagService = featureFlagService self.analytics = analytics + self.abTestVariationProvider = abTestVariationProvider } /// Initializes the WordPress Authenticator. @@ -62,7 +68,7 @@ class AuthenticationManager: Authentication { let isWPComMagicLinkPreferredToPassword = featureFlagService.isFeatureFlagEnabled(.loginMagicLinkEmphasis) let isWPComMagicLinkShownAsSecondaryActionOnPasswordScreen = featureFlagService.isFeatureFlagEnabled(.loginMagicLinkEmphasisM2) let isStoreCreationMVPEnabled = featureFlagService.isFeatureFlagEnabled(.storeCreationMVP) - let enableSiteAddressLoginOnly = ABTest.applicationPasswordAuthentication.variation == .treatment + let enableSiteAddressLoginOnly = abTestVariationProvider.variation(for: .applicationPasswordAuthentication) == .treatment let configuration = WordPressAuthenticatorConfiguration(wpcomClientId: ApiCredentials.dotcomAppId, wpcomSecret: ApiCredentials.dotcomSecret, wpcomScheme: ApiCredentials.dotcomAuthScheme, @@ -341,7 +347,7 @@ extension AuthenticationManager: WordPressAuthenticatorDelegate { /// save the site to memory to check for jetpack requirement in epilogue currentSelfHostedSite = site - let enableWPComOnlyForWPComSites = ABTest.applicationPasswordAuthentication.variation == .treatment + let enableWPComOnlyForWPComSites = abTestVariationProvider.variation(for: .applicationPasswordAuthentication) == .treatment switch (enableWPComOnlyForWPComSites, site.isWPCom) { case (true, true), (false, _): @@ -387,7 +393,7 @@ extension AuthenticationManager: WordPressAuthenticatorDelegate { /// If the user logged in with site credentials and application password feature flag is enabled, /// check if they can use the app and navigates to the home screen. if let siteCredentials = credentials.wporg, - ABTest.applicationPasswordAuthentication.variation == .treatment { + abTestVariationProvider.variation(for: .applicationPasswordAuthentication) == .treatment { return didAuthenticateUser(to: siteURL, with: siteCredentials, in: navigationController) @@ -499,7 +505,7 @@ extension AuthenticationManager: WordPressAuthenticatorDelegate { /// func sync(credentials: AuthenticatorCredentials, onCompletion: @escaping () -> Void) { if let wporg = credentials.wporg, - ABTest.applicationPasswordAuthentication.variation == .treatment { + abTestVariationProvider.variation(for: .applicationPasswordAuthentication) == .treatment { ServiceLocator.stores.authenticate(credentials: .wporg(username: wporg.username, password: wporg.password, siteAddress: wporg.siteURL)) From 3813dc426f581b1ced4b36aae32015152e4913b7 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 12:16:13 +0530 Subject: [PATCH 03/24] `AppCoordinator` - Inject `ABTestVariationProvider` and use it for reading ABTest. --- WooCommerce/Classes/ViewRelated/AppCoordinator.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/AppCoordinator.swift b/WooCommerce/Classes/ViewRelated/AppCoordinator.swift index fe6cf72f201..0f50477e36c 100644 --- a/WooCommerce/Classes/ViewRelated/AppCoordinator.swift +++ b/WooCommerce/Classes/ViewRelated/AppCoordinator.swift @@ -26,6 +26,7 @@ final class AppCoordinator { private var authStatesSubscription: AnyCancellable? private var localNotificationResponsesSubscription: AnyCancellable? private var isLoggedIn: Bool = false + private let abTestVariationProvider: ABTestVariationProvider /// Checks on whether the Apple ID credential is valid when the app is logged in and becomes active. /// @@ -143,7 +144,8 @@ private extension AppCoordinator { configureAndDisplayAuthenticator() } - analytics.track(event: .ApplicationPassword.restAPILoginExperiment(variation: ABTest.applicationPasswordAuthentication.variation.analyticsValue)) + let applicationPasswordABTestVariation = abTestVariationProvider.variation(for: .applicationPasswordAuthentication) + analytics.track(event: .ApplicationPassword.restAPILoginExperiment(variation: applicationPasswordABTestVariation.analyticsValue)) } /// Configures the WPAuthenticator and sets the authenticator UI as the window's root view. From 1c5fdc880dd46a56faeec088e388aa5b005fa8ed Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 12:16:34 +0530 Subject: [PATCH 04/24] `AppCoordinator` - Inject `ABTestVariationProvider` and use it for reading ABTest. --- WooCommerce/Classes/ViewRelated/AppCoordinator.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WooCommerce/Classes/ViewRelated/AppCoordinator.swift b/WooCommerce/Classes/ViewRelated/AppCoordinator.swift index 0f50477e36c..6ed970e49e4 100644 --- a/WooCommerce/Classes/ViewRelated/AppCoordinator.swift +++ b/WooCommerce/Classes/ViewRelated/AppCoordinator.swift @@ -5,6 +5,8 @@ import WordPressAuthenticator import Yosemite import class AutomatticTracks.CrashLogging import protocol Storage.StorageManagerType +import protocol Experiments.ABTestVariationProvider +import struct Experiments.DefaultABTestVariationProvider /// Coordinates app navigation based on authentication state: tab bar UI is shown when the app is logged in, and authentication UI is shown /// when the app is logged out. @@ -40,7 +42,8 @@ final class AppCoordinator { analytics: Analytics = ServiceLocator.analytics, loggedOutAppSettings: LoggedOutAppSettingsProtocol = LoggedOutAppSettings(userDefaults: .standard), pushNotesManager: PushNotesManager = ServiceLocator.pushNotesManager, - featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService) { + featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService, + abTestVariationProvider: ABTestVariationProvider = DefaultABTestVariationProvider()) { self.window = window self.tabBarController = { let storyboard = UIStoryboard(name: "Main", bundle: nil) // Main is the name of storyboard @@ -57,6 +60,7 @@ final class AppCoordinator { self.loggedOutAppSettings = loggedOutAppSettings self.pushNotesManager = pushNotesManager self.featureFlagService = featureFlagService + self.abTestVariationProvider = abTestVariationProvider authenticationManager.setLoggedOutAppSettings(loggedOutAppSettings) From c3414ad90c199c0995d7d2005d850471dca591a4 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 12:17:23 +0530 Subject: [PATCH 05/24] Inject ABTest variation from unit tests. --- .../AuthenticationManagerTests.swift | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift b/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift index c21a8a4a754..083e040123d 100644 --- a/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift +++ b/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift @@ -3,6 +3,8 @@ import WordPressKit import WordPressAuthenticator import Yosemite @testable import WooCommerce +@testable import Experiments +@testable import AutomatticTracks /// Test cases for `AuthenticationManager`. final class AuthenticationManagerTests: XCTestCase { @@ -137,9 +139,12 @@ final class AuthenticationManagerTests: XCTestCase { XCTAssertTrue(viewModel is NoSecureConnectionErrorViewModel) } - func test_it_presents_username_and_password_controller_for_non_jetpack_site() { + func test_it_presents_username_and_password_controller_for_non_jetpack_site_when_not_using_application_password_authentication() { // Given - let manager = AuthenticationManager() + let mockABTestVariationProvider = MockABTestVariationProvider() + mockABTestVariationProvider.mockVariationValue = .control + + let manager = AuthenticationManager(abTestVariationProvider: mockABTestVariationProvider) let siteInfo = WordPressComSiteInfo(remote: ["isWordPress": true, "hasJetpack": false]) var result: WordPressAuthenticatorResult? let completionHandler: (WordPressAuthenticatorResult) -> Void = { completionResult in @@ -206,9 +211,12 @@ final class AuthenticationManagerTests: XCTestCase { XCTAssertTrue(rootController is ULAccountMismatchViewController) } - func test_it_can_display_jetpack_error_for_org_site_credentials_sign_in() { + func test_it_can_display_jetpack_error_for_org_site_credentials_sign_in_when_not_using_application_password_authentication() { // Given - let manager = AuthenticationManager() + let mockABTestVariationProvider = MockABTestVariationProvider() + mockABTestVariationProvider.mockVariationValue = .control + + let manager = AuthenticationManager(abTestVariationProvider: mockABTestVariationProvider) let testSite = "http://test.com" let siteInfo = WordPressComSiteInfo(remote: ["isWordPress": true, "hasJetpack": false, "urlAfterRedirects": testSite]) let wporgCredentials = WordPressOrgCredentials(username: "cba", password: "password", xmlrpc: "http://test.com/xmlrpc.php", options: [:]) @@ -459,7 +467,6 @@ final class AuthenticationManagerTests: XCTestCase { func test_shouldPresentUsernamePasswordController_tracks_fetched_site_info() throws { // Given - let navigationController = UINavigationController() let analyticsProvider = MockAnalyticsProvider() let analytics = WooAnalytics(analyticsProvider: analyticsProvider) @@ -498,3 +505,11 @@ private extension AuthenticationManagerTests { "isWordPressDotCom": isWordPressCom]) } } + +private class MockABTestVariationProvider: ABTestVariationProvider { + var mockVariationValue: Variation! + + func variation(for abTest: ABTest) -> Variation { + mockVariationValue + } +} From 66a03216afeba3f59f5ed6cce566316f93ce0868 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 12:18:06 +0530 Subject: [PATCH 06/24] Stop having `variation` as public to force usage of `ABTestVariationProvider`. --- Experiments/Experiments/ABTest.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Experiments/Experiments/ABTest.swift b/Experiments/Experiments/ABTest.swift index 9dc3d0f0c55..bf3e48e87ae 100644 --- a/Experiments/Experiments/ABTest.swift +++ b/Experiments/Experiments/ABTest.swift @@ -20,7 +20,8 @@ public enum ABTest: String, CaseIterable { case applicationPasswordAuthentication = "woocommerceios_login_rest_api_project_202301_v2" /// Returns a variation for the given experiment - public var variation: Variation { + /// + var variation: Variation { ExPlat.shared?.experiment(rawValue) ?? .control } From 0033332ba3a8b067dbbc3b826bbcdccd3213d892 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 12:25:57 +0530 Subject: [PATCH 07/24] Unit test the REST API AB test tracks event value. --- .../AppCoordinatorTests.swift | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift b/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift index b60d300540c..f7e2f4e1d61 100644 --- a/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift +++ b/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift @@ -5,6 +5,7 @@ import XCTest @testable import WooCommerce import Yosemite import protocol Storage.StorageManagerType +@testable import AutomatticTracks final class AppCoordinatorTests: XCTestCase { private var sessionManager: SessionManager! @@ -325,10 +326,15 @@ final class AppCoordinatorTests: XCTestCase { // Given stores.deauthenticate() let analytics = MockAnalyticsProvider() + + let mockABTestVariationProvider = MockABTestVariationProvider() + mockABTestVariationProvider.mockVariationValue = .control + let appCoordinator = makeCoordinator(window: window, stores: stores, authenticationManager: authenticationManager, - analytics: WooAnalytics(analyticsProvider: analytics)) + analytics: WooAnalytics(analyticsProvider: analytics), + abTestVariationProvider: mockABTestVariationProvider) // When appCoordinator.start() @@ -336,7 +342,9 @@ final class AppCoordinatorTests: XCTestCase { // Then let indexOfEvent = try XCTUnwrap(analytics.receivedEvents.firstIndex(where: { $0 == "rest_api_login_experiment" })) let eventProperties = try XCTUnwrap(analytics.receivedProperties[indexOfEvent]) - XCTAssertNotNil(eventProperties["experiment_variant"] as? String) + + let variant = try XCTUnwrap(eventProperties["experiment_variant"] as? String) + XCTAssertEqual(variant, "control") } // MARK: - Login reminder analytics @@ -453,7 +461,8 @@ private extension AppCoordinatorTests { analytics: Analytics = ServiceLocator.analytics, loggedOutAppSettings: LoggedOutAppSettingsProtocol = MockLoggedOutAppSettings(), pushNotesManager: PushNotesManager = ServiceLocator.pushNotesManager, - featureFlagService: FeatureFlagService = MockFeatureFlagService()) -> AppCoordinator { + featureFlagService: FeatureFlagService = MockFeatureFlagService(), + abTestVariationProvider: ABTestVariationProvider = DefaultABTestVariationProvider()) -> AppCoordinator { return AppCoordinator(window: window ?? self.window, stores: stores ?? self.stores, storageManager: storageManager ?? self.storageManager, @@ -462,6 +471,15 @@ private extension AppCoordinatorTests { analytics: analytics, loggedOutAppSettings: loggedOutAppSettings, pushNotesManager: pushNotesManager, - featureFlagService: featureFlagService) + featureFlagService: featureFlagService, + abTestVariationProvider: abTestVariationProvider) + } +} + +private class MockABTestVariationProvider: ABTestVariationProvider { + var mockVariationValue: Variation! + + func variation(for abTest: ABTest) -> Variation { + mockVariationValue } } From 4e7535e150a10bd783bb5cbee7234bda7cd2ec8f Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 12:44:59 +0530 Subject: [PATCH 08/24] Move `MockABTestVariationProvider` to a separate file for reusability. --- WooCommerce/WooCommerce.xcodeproj/project.pbxproj | 4 ++++ .../WooCommerceTests/AppCoordinatorTests.swift | 9 --------- .../Authentication/AuthenticationManagerTests.swift | 10 ---------- .../Mocks/MockABTestVariationProvider.swift | 11 +++++++++++ 4 files changed, 15 insertions(+), 19 deletions(-) create mode 100644 WooCommerce/WooCommerceTests/Mocks/MockABTestVariationProvider.swift diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index 6a3b2eab2e3..c4cb4b0e4a4 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -1968,6 +1968,7 @@ E1F52DC62668E03B00349D75 /* CardPresentModalBluetoothRequired.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1F52DC52668E03B00349D75 /* CardPresentModalBluetoothRequired.swift */; }; EE0EE7A628B7415200F6061E /* CustomHelpCenterContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0EE7A528B7415200F6061E /* CustomHelpCenterContent.swift */; }; EE0EE7A828B74EF300F6061E /* CustomHelpCenterContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0EE7A728B74EF300F6061E /* CustomHelpCenterContentTests.swift */; }; + EE2EDFE12987A189004E702B /* MockABTestVariationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2EDFE02987A189004E702B /* MockABTestVariationProvider.swift */; }; EE57C11D297AC27300BC31E7 /* TrackEventRequestNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE57C11C297AC27300BC31E7 /* TrackEventRequestNotificationHandler.swift */; }; EE57C11F297E742200BC31E7 /* WooAnalyticsEvent+ApplicationPassword.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE57C11E297E742200BC31E7 /* WooAnalyticsEvent+ApplicationPassword.swift */; }; EE57C121297E76E000BC31E7 /* TrackEventRequestNotificationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE57C120297E76E000BC31E7 /* TrackEventRequestNotificationHandlerTests.swift */; }; @@ -4066,6 +4067,7 @@ E1F52DC52668E03B00349D75 /* CardPresentModalBluetoothRequired.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalBluetoothRequired.swift; sourceTree = ""; }; EE0EE7A528B7415200F6061E /* CustomHelpCenterContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomHelpCenterContent.swift; sourceTree = ""; }; EE0EE7A728B74EF300F6061E /* CustomHelpCenterContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomHelpCenterContentTests.swift; sourceTree = ""; }; + EE2EDFE02987A189004E702B /* MockABTestVariationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockABTestVariationProvider.swift; sourceTree = ""; }; EE57C11C297AC27300BC31E7 /* TrackEventRequestNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackEventRequestNotificationHandler.swift; sourceTree = ""; }; EE57C11E297E742200BC31E7 /* WooAnalyticsEvent+ApplicationPassword.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WooAnalyticsEvent+ApplicationPassword.swift"; sourceTree = ""; }; EE57C120297E76E000BC31E7 /* TrackEventRequestNotificationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackEventRequestNotificationHandlerTests.swift; sourceTree = ""; }; @@ -6798,6 +6800,7 @@ EE8DCA7F28BF964700F23B23 /* MockAuthentication.swift */, AEB4DB98290AE8F300AE4340 /* MockCookieJar.swift */, 02660503293D8D24004084EA /* PaymentCaptureCelebration.swift */, + EE2EDFE02987A189004E702B /* MockABTestVariationProvider.swift */, ); path = Mocks; sourceTree = ""; @@ -11412,6 +11415,7 @@ DE4B3B2C2692DC2200EEF2D8 /* ReviewOrderViewModelTests.swift in Sources */, D89C009425B4E9E2000E4683 /* ULAccountMismatchViewControllerTests.swift in Sources */, 573A960324F433DD0091F3A5 /* ProductsTopBannerFactoryTests.swift in Sources */, + EE2EDFE12987A189004E702B /* MockABTestVariationProvider.swift in Sources */, 0273707E24C0047800167204 /* SequenceHelpersTests.swift in Sources */, D802547326551D0F001B2CC1 /* CardPresentModalTapCardTests.swift in Sources */, F997174723DC070D00592D8E /* XLPagerStrip+AccessibilityIdentifierTests.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift b/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift index f7e2f4e1d61..571e070c5e5 100644 --- a/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift +++ b/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift @@ -5,7 +5,6 @@ import XCTest @testable import WooCommerce import Yosemite import protocol Storage.StorageManagerType -@testable import AutomatticTracks final class AppCoordinatorTests: XCTestCase { private var sessionManager: SessionManager! @@ -475,11 +474,3 @@ private extension AppCoordinatorTests { abTestVariationProvider: abTestVariationProvider) } } - -private class MockABTestVariationProvider: ABTestVariationProvider { - var mockVariationValue: Variation! - - func variation(for abTest: ABTest) -> Variation { - mockVariationValue - } -} diff --git a/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift b/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift index 083e040123d..85151a218ae 100644 --- a/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift +++ b/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift @@ -3,8 +3,6 @@ import WordPressKit import WordPressAuthenticator import Yosemite @testable import WooCommerce -@testable import Experiments -@testable import AutomatticTracks /// Test cases for `AuthenticationManager`. final class AuthenticationManagerTests: XCTestCase { @@ -505,11 +503,3 @@ private extension AuthenticationManagerTests { "isWordPressDotCom": isWordPressCom]) } } - -private class MockABTestVariationProvider: ABTestVariationProvider { - var mockVariationValue: Variation! - - func variation(for abTest: ABTest) -> Variation { - mockVariationValue - } -} diff --git a/WooCommerce/WooCommerceTests/Mocks/MockABTestVariationProvider.swift b/WooCommerce/WooCommerceTests/Mocks/MockABTestVariationProvider.swift new file mode 100644 index 00000000000..dd45cec5fbe --- /dev/null +++ b/WooCommerce/WooCommerceTests/Mocks/MockABTestVariationProvider.swift @@ -0,0 +1,11 @@ +import protocol Experiments.ABTestVariationProvider +import enum Experiments.ABTest +import enum AutomatticTracks.Variation + +final class MockABTestVariationProvider: ABTestVariationProvider { + var mockVariationValue: Variation! + + func variation(for abTest: ABTest) -> Variation { + mockVariationValue + } +} From 5ae2cda770469b7b5d362eae9001e3bc0093e10c Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 14:19:53 +0530 Subject: [PATCH 09/24] Update unit test name to reflect the actual test. --- .../Authentication/AuthenticationManagerTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift b/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift index 85151a218ae..2d337c91c70 100644 --- a/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift +++ b/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift @@ -137,7 +137,7 @@ final class AuthenticationManagerTests: XCTestCase { XCTAssertTrue(viewModel is NoSecureConnectionErrorViewModel) } - func test_it_presents_username_and_password_controller_for_non_jetpack_site_when_not_using_application_password_authentication() { + func test_it_presents_email_controller_for_non_jetpack_site_when_not_using_application_password_authentication() { // Given let mockABTestVariationProvider = MockABTestVariationProvider() mockABTestVariationProvider.mockVariationValue = .control From b4b1582c33f0ae04aaeb777d30360c80db7e54ed Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Mon, 30 Jan 2023 14:20:37 +0530 Subject: [PATCH 10/24] Add unit tests to check the scenarios when application password authentication is turned on. --- .../AuthenticationManagerTests.swift | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift b/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift index 2d337c91c70..3fce915efab 100644 --- a/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift +++ b/WooCommerce/WooCommerceTests/Authentication/AuthenticationManagerTests.swift @@ -158,6 +158,27 @@ final class AuthenticationManagerTests: XCTestCase { } } + func test_it_presents_username_and_password_controller_for_non_jetpack_site_when_using_application_password_authentication() { + // Given + let mockABTestVariationProvider = MockABTestVariationProvider() + mockABTestVariationProvider.mockVariationValue = .treatment + + let manager = AuthenticationManager(abTestVariationProvider: mockABTestVariationProvider) + let siteInfo = WordPressComSiteInfo(remote: ["isWordPress": true, "hasJetpack": false]) + var result: WordPressAuthenticatorResult? + let completionHandler: (WordPressAuthenticatorResult) -> Void = { completionResult in + result = completionResult + } + + // When + manager.shouldPresentUsernamePasswordController(for: siteInfo, onCompletion: completionHandler) + + // Then + guard case .presentPasswordController = result else { + return XCTFail("Unexpected result returned for non-Jetpack site") + } + } + func test_it_shows_error_upon_login_epilogue_if_the_self_hosted_site_does_not_have_jetpack() { // Given let manager = AuthenticationManager() @@ -230,6 +251,27 @@ final class AuthenticationManagerTests: XCTestCase { XCTAssertTrue(rootController is ULErrorViewController) } + func test_it_does_not_display_jetpack_error_for_org_site_credentials_sign_in_when_using_application_password_authentication() { + // Given + let mockABTestVariationProvider = MockABTestVariationProvider() + mockABTestVariationProvider.mockVariationValue = .treatment + + let manager = AuthenticationManager(abTestVariationProvider: mockABTestVariationProvider) + let testSite = "http://test.com" + let siteInfo = WordPressComSiteInfo(remote: ["isWordPress": true, "hasJetpack": false, "urlAfterRedirects": testSite]) + let wporgCredentials = WordPressOrgCredentials(username: "cba", password: "password", xmlrpc: "http://test.com/xmlrpc.php", options: [:]) + let credentials = AuthenticatorCredentials(wpcom: nil, wporg: wporgCredentials) + let navigationController = UINavigationController() + + // When + manager.shouldPresentUsernamePasswordController(for: siteInfo, onCompletion: { _ in }) + manager.presentLoginEpilogue(in: navigationController, for: credentials, source: nil, onDismiss: {}) + + // Then + let rootController = navigationController.viewControllers.first + XCTAssertFalse(rootController is ULErrorViewController) + } + func test_errorViewController_display_account_mismatch_screen_if_no_site_matches_the_given_self_hosted_site() { // Given let manager = AuthenticationManager() From 0c32dd19683eb022e0cf810f6366e10f3d3d2682 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 09:03:14 +0530 Subject: [PATCH 11/24] Make ABTest confirm to Codable to store it to disk --- Experiments/Experiments/ABTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Experiments/Experiments/ABTest.swift b/Experiments/Experiments/ABTest.swift index bf3e48e87ae..f4335c9354a 100644 --- a/Experiments/Experiments/ABTest.swift +++ b/Experiments/Experiments/ABTest.swift @@ -2,7 +2,7 @@ import AutomatticTracks /// ABTest adds A/B testing experiments and runs the tests based on their variations from the ExPlat service. /// -public enum ABTest: String, CaseIterable { +public enum ABTest: String, CaseIterable, Codable { /// Throwaway case, to prevent a compiler error: /// `An enum with no cases cannot declare a raw type` case null From 7c5a5cfc2ef65a556496c683421e97d9d2e31dac Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 09:06:21 +0530 Subject: [PATCH 12/24] Introduce `VariationCache` to store the ABTest variation information to disk. --- .../Experiments.xcodeproj/project.pbxproj | 21 +++++++++ Experiments/Experiments/VariationCache.swift | 47 +++++++++++++++++++ .../VariationCacheTests.swift | 36 ++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 Experiments/Experiments/VariationCache.swift create mode 100644 Experiments/ExperimentsTests/VariationCacheTests.swift diff --git a/Experiments/Experiments.xcodeproj/project.pbxproj b/Experiments/Experiments.xcodeproj/project.pbxproj index 071ba6bbdc3..30ef763472c 100644 --- a/Experiments/Experiments.xcodeproj/project.pbxproj +++ b/Experiments/Experiments.xcodeproj/project.pbxproj @@ -17,6 +17,10 @@ C8E16F0EE6954B58A1C402F0 /* Pods_ExperimentsTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AAC7C082DD376957B4676401 /* Pods_ExperimentsTests.framework */; }; CC53FB48275E426900C4CA4F /* ABTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC53FB47275E426900C4CA4F /* ABTest.swift */; }; EE2EDFDF29879331004E702B /* ABTestVariationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2EDFDE29879331004E702B /* ABTestVariationProvider.swift */; }; + EEC8C0ED298A92F10047B4CB /* CachedABTestVariationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC8C0EC298A92F10047B4CB /* CachedABTestVariationProvider.swift */; }; + EEC8C0EF298A939C0047B4CB /* VariationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC8C0EE298A939C0047B4CB /* VariationCache.swift */; }; + EEC8C0F1298B5AFB0047B4CB /* VariationCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC8C0F0298B5AFB0047B4CB /* VariationCacheTests.swift */; }; + EEC8C0F3298B5F950047B4CB /* CachedABTestVariationProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC8C0F2298B5F950047B4CB /* CachedABTestVariationProviderTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -49,6 +53,10 @@ AF72D9DB7771E7A5105C88B0 /* Pods-Experiments.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Experiments.release.xcconfig"; path = "Target Support Files/Pods-Experiments/Pods-Experiments.release.xcconfig"; sourceTree = ""; }; CC53FB47275E426900C4CA4F /* ABTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABTest.swift; sourceTree = ""; }; EE2EDFDE29879331004E702B /* ABTestVariationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ABTestVariationProvider.swift; sourceTree = ""; }; + EEC8C0EC298A92F10047B4CB /* CachedABTestVariationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedABTestVariationProvider.swift; sourceTree = ""; }; + EEC8C0EE298A939C0047B4CB /* VariationCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariationCache.swift; sourceTree = ""; }; + EEC8C0F0298B5AFB0047B4CB /* VariationCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariationCacheTests.swift; sourceTree = ""; }; + EEC8C0F2298B5F950047B4CB /* CachedABTestVariationProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachedABTestVariationProviderTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -103,6 +111,8 @@ 0270C0A627069BA500FC799F /* BuildConfiguration.swift */, CC53FB47275E426900C4CA4F /* ABTest.swift */, EE2EDFDE29879331004E702B /* ABTestVariationProvider.swift */, + EEC8C0EC298A92F10047B4CB /* CachedABTestVariationProvider.swift */, + EEC8C0EE298A939C0047B4CB /* VariationCache.swift */, ); path = Experiments; sourceTree = ""; @@ -111,6 +121,8 @@ isa = PBXGroup; children = ( 0270C09127069A8900FC799F /* Info.plist */, + EEC8C0F0298B5AFB0047B4CB /* VariationCacheTests.swift */, + EEC8C0F2298B5F950047B4CB /* CachedABTestVariationProviderTests.swift */, ); path = ExperimentsTests; sourceTree = ""; @@ -206,6 +218,7 @@ }; 0270C08927069A8900FC799F = { CreatedOnToolsVersion = 12.5.1; + LastSwiftMigration = 1420; }; }; }; @@ -314,11 +327,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EEC8C0EF298A939C0047B4CB /* VariationCache.swift in Sources */, 0270C0A327069B7800FC799F /* FeatureFlagService.swift in Sources */, 0270C0A527069B8900FC799F /* DefaultFeatureFlagService.swift in Sources */, 0270C09C27069AE700FC799F /* FeatureFlag.swift in Sources */, EE2EDFDF29879331004E702B /* ABTestVariationProvider.swift in Sources */, 0270C0A727069BA500FC799F /* BuildConfiguration.swift in Sources */, + EEC8C0ED298A92F10047B4CB /* CachedABTestVariationProvider.swift in Sources */, CC53FB48275E426900C4CA4F /* ABTest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -327,6 +342,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EEC8C0F1298B5AFB0047B4CB /* VariationCacheTests.swift in Sources */, + EEC8C0F3298B5F950047B4CB /* CachedABTestVariationProviderTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -521,6 +538,7 @@ baseConfigurationReference = 3022E2766134CE2735C73FC6 /* Pods-ExperimentsTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "${inherited}"; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = ExperimentsTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -530,6 +548,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.automattic.ExperimentsTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -540,6 +559,7 @@ baseConfigurationReference = 7C831644164B49828A485590 /* Pods-ExperimentsTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "${inherited}"; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = ExperimentsTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -644,6 +664,7 @@ baseConfigurationReference = 3F9DB5FBFF7A42EFBCB746F3 /* Pods-ExperimentsTests.release-alpha.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "${inherited}"; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = ExperimentsTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Experiments/Experiments/VariationCache.swift b/Experiments/Experiments/VariationCache.swift new file mode 100644 index 00000000000..d25db108877 --- /dev/null +++ b/Experiments/Experiments/VariationCache.swift @@ -0,0 +1,47 @@ +import AutomatticTracks + +enum VariationCacheError: Error { + case onlyLoggedOutExperimentsShouldBeCached +} + +public struct VariationCache { + private let variationKey = "VariationCacheKey" + + private let userDefaults: UserDefaults + + public init(userDefaults: UserDefaults) { + self.userDefaults = userDefaults + } + + func variation(for abTest: ABTest) -> Variation? { + guard abTest.context == .loggedOut else { + return nil + } + + guard let data = userDefaults.object(forKey: variationKey) as? Data, + let cachedVariations = try? JSONDecoder().decode([CachedVariation].self, from: data), + case let variation = cachedVariations.first(where: { $0.abTest == abTest })?.variation + else { + return nil + } + + return variation + } + + func assign(variation: Variation, for abTest: ABTest) throws { + guard abTest.context == .loggedOut else { + throw VariationCacheError.onlyLoggedOutExperimentsShouldBeCached + } + + var variations = userDefaults.object(forKey: variationKey) as? [CachedVariation] ?? [] + variations.append(CachedVariation(abTest: abTest, variation: variation)) + + let encodedVariation = try JSONEncoder().encode(variations) + userDefaults.set(encodedVariation, forKey: variationKey) + } +} + +public struct CachedVariation: Codable { + let abTest: ABTest + let variation: Variation +} diff --git a/Experiments/ExperimentsTests/VariationCacheTests.swift b/Experiments/ExperimentsTests/VariationCacheTests.swift new file mode 100644 index 00000000000..1ce29f88d43 --- /dev/null +++ b/Experiments/ExperimentsTests/VariationCacheTests.swift @@ -0,0 +1,36 @@ +import XCTest +@testable import Experiments + +final class VariationCacheTests: XCTestCase { + func test_variation_is_nil_when_the_value_does_not_exist() throws { + // Given + let userDefaults = try XCTUnwrap(UserDefaults(suiteName: UUID().uuidString)) + + // When + let cache = VariationCache(userDefaults: userDefaults) + + // Then + XCTAssertNil(cache.variation(for: .applicationPasswordAuthentication)) + } + + func test_correct_variation_is_returned_after_setting_it() throws { + // Given + let userDefaults = try XCTUnwrap(UserDefaults(suiteName: UUID().uuidString)) + let cache = VariationCache(userDefaults: userDefaults) + + // When + try cache.assign(variation: .treatment, for: .applicationPasswordAuthentication) + + // Then + XCTAssertEqual(cache.variation(for: .applicationPasswordAuthentication), .treatment) + } + + func test_it_throws_when_trying_to_cache_logged_in_experiment() throws { + // Given + let userDefaults = try XCTUnwrap(UserDefaults(suiteName: UUID().uuidString)) + let cache = VariationCache(userDefaults: userDefaults) + + // When + XCTAssertThrowsError(try cache.assign(variation: .treatment, for: .aaTestLoggedIn)) + } +} From ada4aa6d00747752dac4c9666d0f7c6a11862f2c Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 09:07:53 +0530 Subject: [PATCH 13/24] Add cached implementation of `ABTestVariationProvider` --- .../CachedABTestVariationProvider.swift | 27 +++++++++++++++++ .../CachedABTestVariationProviderTests.swift | 29 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 Experiments/Experiments/CachedABTestVariationProvider.swift create mode 100644 Experiments/ExperimentsTests/CachedABTestVariationProviderTests.swift diff --git a/Experiments/Experiments/CachedABTestVariationProvider.swift b/Experiments/Experiments/CachedABTestVariationProvider.swift new file mode 100644 index 00000000000..776b924ceb3 --- /dev/null +++ b/Experiments/Experiments/CachedABTestVariationProvider.swift @@ -0,0 +1,27 @@ +import AutomatticTracks + +/// Cache based implementation of `ABTestVariationProvider` +/// +public struct CachedABTestVariationProvider: ABTestVariationProvider { + + private let cache: VariationCache + + public init(cache: VariationCache = VariationCache(userDefaults: .standard)) { + self.cache = cache + } + + public func variation(for abTest: ABTest) -> Variation { + guard abTest.context == .loggedOut else { + return abTest.variation ?? .control + } + + if let cachedVariation = cache.variation(for: abTest) { + return cachedVariation + } else if let variation = abTest.variation { + try? cache.assign(variation: variation, for: abTest) + return variation + } else { + return .control + } + } +} diff --git a/Experiments/ExperimentsTests/CachedABTestVariationProviderTests.swift b/Experiments/ExperimentsTests/CachedABTestVariationProviderTests.swift new file mode 100644 index 00000000000..5e68d3815d1 --- /dev/null +++ b/Experiments/ExperimentsTests/CachedABTestVariationProviderTests.swift @@ -0,0 +1,29 @@ +import XCTest +@testable import Experiments + +final class CachedABTestVariationProviderTests: XCTestCase { + func test_variation_is_control_when_the_value_does_not_exist() throws { + // Given + let userDefaults = try XCTUnwrap(UserDefaults(suiteName: UUID().uuidString)) + + // When + let cache = VariationCache(userDefaults: userDefaults) + let provider = CachedABTestVariationProvider(cache: cache) + + // Then + XCTAssertEqual(provider.variation(for: .applicationPasswordAuthentication), .control) + } + + func test_correct_variation_is_returned_after_caching_it() throws { + // Given + let userDefaults = try XCTUnwrap(UserDefaults(suiteName: UUID().uuidString)) + let cache = VariationCache(userDefaults: userDefaults) + let provider = CachedABTestVariationProvider(cache: cache) + + // When + try cache.assign(variation: .treatment, for: .applicationPasswordAuthentication) + + // Then + XCTAssertEqual(provider.variation(for: .applicationPasswordAuthentication), .treatment) + } +} From f9107f3b383198ac27a7db47f0959230d8b906d8 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 09:09:15 +0530 Subject: [PATCH 14/24] Point Automattic tracks pod to beta version. --- Podfile | 4 ++-- Podfile.lock | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Podfile b/Podfile index c78588acfb7..2f13092ab9e 100644 --- a/Podfile +++ b/Podfile @@ -27,9 +27,9 @@ def aztec end def tracks - pod 'Automattic-Tracks-iOS', '~> 1.0.0' + # pod 'Automattic-Tracks-iOS', '~> 1.0.0' # pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :branch => 'trunk' - # pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :commit => '' + pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :commit => '3c15eb0450aa5f70aff29bc2e465e6005ce41a47' # pod 'Automattic-Tracks-iOS', :path => '../Automattic-Tracks-iOS' end diff --git a/Podfile.lock b/Podfile.lock index cff0d5731fb..24c03667a07 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -6,7 +6,7 @@ PODS: - AppAuth/Core (1.6.0) - AppAuth/ExternalUserAgent (1.6.0): - AppAuth/Core - - Automattic-Tracks-iOS (1.0.0): + - Automattic-Tracks-iOS (1.1.0-beta): - Sentry (~> 7.25) - Sodium (>= 0.9.1) - UIDeviceIdentifier (~> 2.0) @@ -75,7 +75,7 @@ PODS: DEPENDENCIES: - Alamofire (~> 4.8) - - Automattic-Tracks-iOS (~> 1.0.0) + - Automattic-Tracks-iOS (from `https://github.com/Automattic/Automattic-Tracks-iOS.git`, commit `3c15eb0450aa5f70aff29bc2e465e6005ce41a47`) - CocoaLumberjack (~> 3.7.4) - CocoaLumberjack/Swift (~> 3.7.4) - Gridicons (~> 1.2.0) @@ -99,7 +99,6 @@ SPEC REPOS: trunk: - Alamofire - AppAuth - - Automattic-Tracks-iOS - CocoaLumberjack - GoogleSignIn - Gridicons @@ -132,10 +131,20 @@ SPEC REPOS: - ZendeskSupportProvidersSDK - ZendeskSupportSDK +EXTERNAL SOURCES: + Automattic-Tracks-iOS: + :commit: 3c15eb0450aa5f70aff29bc2e465e6005ce41a47 + :git: https://github.com/Automattic/Automattic-Tracks-iOS.git + +CHECKOUT OPTIONS: + Automattic-Tracks-iOS: + :commit: 3c15eb0450aa5f70aff29bc2e465e6005ce41a47 + :git: https://github.com/Automattic/Automattic-Tracks-iOS.git + SPEC CHECKSUMS: Alamofire: 3ec537f71edc9804815215393ae2b1a8ea33a844 AppAuth: 8fca6b5563a5baef2c04bee27538025e4ceb2add - Automattic-Tracks-iOS: 93df154824af31eba947718110023acce1ce7905 + Automattic-Tracks-iOS: 382b2275c332d370b3c74f90d979b58f562ab5c1 CocoaLumberjack: 543c79c114dadc3b1aba95641d8738b06b05b646 GoogleSignIn: fd381840dbe7c1137aa6dc30849a5c3e070c034a Gridicons: 4455b9f366960121430e45997e32112ae49ffe1d @@ -169,6 +178,6 @@ SPEC CHECKSUMS: ZendeskSupportProvidersSDK: 2bdf8544f7cd0fd4c002546f5704b813845beb2a ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba -PODFILE CHECKSUM: 5611275f2e251a88706eac841eb27ae63383a7ac +PODFILE CHECKSUM: dc236dd54fdfff69ac2a6ea7b31e8b35545227e2 COCOAPODS: 1.11.3 From 99b0953d5e6cd1926c65fd9dc765c5c2a8397838 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 09:10:17 +0530 Subject: [PATCH 15/24] ABTest - Return nil instead of assuming control as variation. --- Experiments/Experiments/ABTest.swift | 4 ++-- Experiments/Experiments/ABTestVariationProvider.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Experiments/Experiments/ABTest.swift b/Experiments/Experiments/ABTest.swift index f4335c9354a..fab95d45edf 100644 --- a/Experiments/Experiments/ABTest.swift +++ b/Experiments/Experiments/ABTest.swift @@ -21,8 +21,8 @@ public enum ABTest: String, CaseIterable, Codable { /// Returns a variation for the given experiment /// - var variation: Variation { - ExPlat.shared?.experiment(rawValue) ?? .control + public var variation: Variation? { + ExPlat.shared?.experiment(rawValue) } /// Returns the context for the given experiment. diff --git a/Experiments/Experiments/ABTestVariationProvider.swift b/Experiments/Experiments/ABTestVariationProvider.swift index 4db9af7e7e0..89eeccf9b7b 100644 --- a/Experiments/Experiments/ABTestVariationProvider.swift +++ b/Experiments/Experiments/ABTestVariationProvider.swift @@ -11,6 +11,6 @@ public struct DefaultABTestVariationProvider: ABTestVariationProvider { public init() { } public func variation(for abTest: ABTest) -> Variation { - abTest.variation + abTest.variation ?? .control } } From dcca9645557ad63c274dcbda288633db0b15e493 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 09:10:45 +0530 Subject: [PATCH 16/24] Add ExperimentsTests to unit test plan. --- WooCommerce/WooCommerceTests/UnitTests.xctestplan | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/WooCommerce/WooCommerceTests/UnitTests.xctestplan b/WooCommerce/WooCommerceTests/UnitTests.xctestplan index e353c33e4b9..6697ca371ad 100644 --- a/WooCommerce/WooCommerceTests/UnitTests.xctestplan +++ b/WooCommerce/WooCommerceTests/UnitTests.xctestplan @@ -78,6 +78,13 @@ "identifier" : "B9C9C63D283E703C001B879F", "name" : "WooFoundationTests" } + }, + { + "target" : { + "containerPath" : "container:..\/Experiments\/Experiments.xcodeproj", + "identifier" : "0270C08927069A8900FC799F", + "name" : "ExperimentsTests" + } } ], "version" : 1 From 9803a258d54392ae176c59f6ed61bd3d562ecc27 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 09:11:27 +0530 Subject: [PATCH 17/24] Start using `CachedABTestVariationProvider` instead of `DefaultABTestVariationProvider` --- .../Classes/Authentication/AuthenticationManager.swift | 4 ++-- WooCommerce/Classes/ViewRelated/AppCoordinator.swift | 4 ++-- WooCommerce/WooCommerceTests/AppCoordinatorTests.swift | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/WooCommerce/Classes/Authentication/AuthenticationManager.swift b/WooCommerce/Classes/Authentication/AuthenticationManager.swift index 1ad0c6bf438..2902562d0b8 100644 --- a/WooCommerce/Classes/Authentication/AuthenticationManager.swift +++ b/WooCommerce/Classes/Authentication/AuthenticationManager.swift @@ -10,7 +10,7 @@ import protocol Experiments.FeatureFlagService import protocol Storage.StorageManagerType import class Networking.DefaultApplicationPasswordUseCase import protocol Experiments.ABTestVariationProvider -import struct Experiments.DefaultABTestVariationProvider +import struct Experiments.CachedABTestVariationProvider /// Encapsulates all of the interactions with the WordPress Authenticator /// @@ -55,7 +55,7 @@ class AuthenticationManager: Authentication { init(storageManager: StorageManagerType = ServiceLocator.storageManager, featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService, analytics: Analytics = ServiceLocator.analytics, - abTestVariationProvider: ABTestVariationProvider = DefaultABTestVariationProvider()) { + abTestVariationProvider: ABTestVariationProvider = CachedABTestVariationProvider()) { self.storageManager = storageManager self.featureFlagService = featureFlagService self.analytics = analytics diff --git a/WooCommerce/Classes/ViewRelated/AppCoordinator.swift b/WooCommerce/Classes/ViewRelated/AppCoordinator.swift index 6ed970e49e4..c1715db6f5a 100644 --- a/WooCommerce/Classes/ViewRelated/AppCoordinator.swift +++ b/WooCommerce/Classes/ViewRelated/AppCoordinator.swift @@ -6,7 +6,7 @@ import Yosemite import class AutomatticTracks.CrashLogging import protocol Storage.StorageManagerType import protocol Experiments.ABTestVariationProvider -import struct Experiments.DefaultABTestVariationProvider +import struct Experiments.CachedABTestVariationProvider /// Coordinates app navigation based on authentication state: tab bar UI is shown when the app is logged in, and authentication UI is shown /// when the app is logged out. @@ -43,7 +43,7 @@ final class AppCoordinator { loggedOutAppSettings: LoggedOutAppSettingsProtocol = LoggedOutAppSettings(userDefaults: .standard), pushNotesManager: PushNotesManager = ServiceLocator.pushNotesManager, featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService, - abTestVariationProvider: ABTestVariationProvider = DefaultABTestVariationProvider()) { + abTestVariationProvider: ABTestVariationProvider = CachedABTestVariationProvider()) { self.window = window self.tabBarController = { let storyboard = UIStoryboard(name: "Main", bundle: nil) // Main is the name of storyboard diff --git a/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift b/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift index 571e070c5e5..41c8fcdce7c 100644 --- a/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift +++ b/WooCommerce/WooCommerceTests/AppCoordinatorTests.swift @@ -461,7 +461,7 @@ private extension AppCoordinatorTests { loggedOutAppSettings: LoggedOutAppSettingsProtocol = MockLoggedOutAppSettings(), pushNotesManager: PushNotesManager = ServiceLocator.pushNotesManager, featureFlagService: FeatureFlagService = MockFeatureFlagService(), - abTestVariationProvider: ABTestVariationProvider = DefaultABTestVariationProvider()) -> AppCoordinator { + abTestVariationProvider: ABTestVariationProvider = CachedABTestVariationProvider()) -> AppCoordinator { return AppCoordinator(window: window ?? self.window, stores: stores ?? self.stores, storageManager: storageManager ?? self.storageManager, From 8908527227c75f0b4bf688e6ff7e52df6af4e819 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 09:26:12 +0530 Subject: [PATCH 18/24] Point tracks pod to latest beta commit. --- Podfile | 2 +- Podfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index 2f13092ab9e..8a6d7881887 100644 --- a/Podfile +++ b/Podfile @@ -29,7 +29,7 @@ end def tracks # pod 'Automattic-Tracks-iOS', '~> 1.0.0' # pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :branch => 'trunk' - pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :commit => '3c15eb0450aa5f70aff29bc2e465e6005ce41a47' + pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :commit => 'f990056f1d7f5ccac2d8c8d6e0a41ef60ea9c004' # pod 'Automattic-Tracks-iOS', :path => '../Automattic-Tracks-iOS' end diff --git a/Podfile.lock b/Podfile.lock index 24c03667a07..d65b85c9211 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -75,7 +75,7 @@ PODS: DEPENDENCIES: - Alamofire (~> 4.8) - - Automattic-Tracks-iOS (from `https://github.com/Automattic/Automattic-Tracks-iOS.git`, commit `3c15eb0450aa5f70aff29bc2e465e6005ce41a47`) + - Automattic-Tracks-iOS (from `https://github.com/Automattic/Automattic-Tracks-iOS.git`, commit `f990056f1d7f5ccac2d8c8d6e0a41ef60ea9c004`) - CocoaLumberjack (~> 3.7.4) - CocoaLumberjack/Swift (~> 3.7.4) - Gridicons (~> 1.2.0) @@ -133,12 +133,12 @@ SPEC REPOS: EXTERNAL SOURCES: Automattic-Tracks-iOS: - :commit: 3c15eb0450aa5f70aff29bc2e465e6005ce41a47 + :commit: f990056f1d7f5ccac2d8c8d6e0a41ef60ea9c004 :git: https://github.com/Automattic/Automattic-Tracks-iOS.git CHECKOUT OPTIONS: Automattic-Tracks-iOS: - :commit: 3c15eb0450aa5f70aff29bc2e465e6005ce41a47 + :commit: f990056f1d7f5ccac2d8c8d6e0a41ef60ea9c004 :git: https://github.com/Automattic/Automattic-Tracks-iOS.git SPEC CHECKSUMS: @@ -178,6 +178,6 @@ SPEC CHECKSUMS: ZendeskSupportProvidersSDK: 2bdf8544f7cd0fd4c002546f5704b813845beb2a ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba -PODFILE CHECKSUM: dc236dd54fdfff69ac2a6ea7b31e8b35545227e2 +PODFILE CHECKSUM: 707be8e6bec5ee95efd80c5e966878e21e9e537f COCOAPODS: 1.11.3 From e0d9ee5a75b8da40669fe2e7d605238f50b1b73b Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 11:42:06 +0530 Subject: [PATCH 19/24] Use Mock `ABTest` values for unit testing. --- Experiments/Experiments/ABTest.swift | 7 +++++++ .../CachedABTestVariationProviderTests.swift | 6 +++--- Experiments/ExperimentsTests/VariationCacheTests.swift | 8 ++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Experiments/Experiments/ABTest.swift b/Experiments/Experiments/ABTest.swift index fab95d45edf..a04c47168f8 100644 --- a/Experiments/Experiments/ABTest.swift +++ b/Experiments/Experiments/ABTest.swift @@ -19,6 +19,9 @@ public enum ABTest: String, CaseIterable, Codable { /// Experiment ref: pbxNRc-2i4-p2 case applicationPasswordAuthentication = "woocommerceios_login_rest_api_project_202301_v2" + /// Mocks for unit testing + case mockLoggedIn, mockLoggedOut + /// Returns a variation for the given experiment /// public var variation: Variation? { @@ -36,6 +39,10 @@ public enum ABTest: String, CaseIterable, Codable { return .loggedOut case .null: return .none + case .mockLoggedIn: + return .loggedIn + case .mockLoggedOut: + return .loggedOut } } } diff --git a/Experiments/ExperimentsTests/CachedABTestVariationProviderTests.swift b/Experiments/ExperimentsTests/CachedABTestVariationProviderTests.swift index 5e68d3815d1..5b569c1cc8e 100644 --- a/Experiments/ExperimentsTests/CachedABTestVariationProviderTests.swift +++ b/Experiments/ExperimentsTests/CachedABTestVariationProviderTests.swift @@ -11,7 +11,7 @@ final class CachedABTestVariationProviderTests: XCTestCase { let provider = CachedABTestVariationProvider(cache: cache) // Then - XCTAssertEqual(provider.variation(for: .applicationPasswordAuthentication), .control) + XCTAssertEqual(provider.variation(for: .mockLoggedOut), .control) } func test_correct_variation_is_returned_after_caching_it() throws { @@ -21,9 +21,9 @@ final class CachedABTestVariationProviderTests: XCTestCase { let provider = CachedABTestVariationProvider(cache: cache) // When - try cache.assign(variation: .treatment, for: .applicationPasswordAuthentication) + try cache.assign(variation: .treatment, for: .mockLoggedOut) // Then - XCTAssertEqual(provider.variation(for: .applicationPasswordAuthentication), .treatment) + XCTAssertEqual(provider.variation(for: .mockLoggedOut), .treatment) } } diff --git a/Experiments/ExperimentsTests/VariationCacheTests.swift b/Experiments/ExperimentsTests/VariationCacheTests.swift index 1ce29f88d43..5f039b0a47e 100644 --- a/Experiments/ExperimentsTests/VariationCacheTests.swift +++ b/Experiments/ExperimentsTests/VariationCacheTests.swift @@ -10,7 +10,7 @@ final class VariationCacheTests: XCTestCase { let cache = VariationCache(userDefaults: userDefaults) // Then - XCTAssertNil(cache.variation(for: .applicationPasswordAuthentication)) + XCTAssertNil(cache.variation(for: .mockLoggedOut)) } func test_correct_variation_is_returned_after_setting_it() throws { @@ -19,10 +19,10 @@ final class VariationCacheTests: XCTestCase { let cache = VariationCache(userDefaults: userDefaults) // When - try cache.assign(variation: .treatment, for: .applicationPasswordAuthentication) + try cache.assign(variation: .treatment, for: .mockLoggedOut) // Then - XCTAssertEqual(cache.variation(for: .applicationPasswordAuthentication), .treatment) + XCTAssertEqual(cache.variation(for: .mockLoggedOut), .treatment) } func test_it_throws_when_trying_to_cache_logged_in_experiment() throws { @@ -31,6 +31,6 @@ final class VariationCacheTests: XCTestCase { let cache = VariationCache(userDefaults: userDefaults) // When - XCTAssertThrowsError(try cache.assign(variation: .treatment, for: .aaTestLoggedIn)) + XCTAssertThrowsError(try cache.assign(variation: .treatment, for: .mockLoggedIn)) } } From c4ce4cad76513d2f8423cbd0056d038d46a9358d Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 11:42:27 +0530 Subject: [PATCH 20/24] Explain why we cache only logged out experiments. --- Experiments/Experiments/CachedABTestVariationProvider.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Experiments/Experiments/CachedABTestVariationProvider.swift b/Experiments/Experiments/CachedABTestVariationProvider.swift index 776b924ceb3..1085f219666 100644 --- a/Experiments/Experiments/CachedABTestVariationProvider.swift +++ b/Experiments/Experiments/CachedABTestVariationProvider.swift @@ -11,6 +11,9 @@ public struct CachedABTestVariationProvider: ABTestVariationProvider { } public func variation(for abTest: ABTest) -> Variation { + // We cache only logged out ABTests as they are assigned based on `anonId` by `ExPlat`. + // There will be one value assigned to one device and it won't change. + // guard abTest.context == .loggedOut else { return abTest.variation ?? .control } From 737ee3275840014083d97603773ad264450abc51 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 12:17:49 +0530 Subject: [PATCH 21/24] Avoid sending mock ABTest cases to server. --- Experiments/Experiments/ABTest.swift | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Experiments/Experiments/ABTest.swift b/Experiments/Experiments/ABTest.swift index a04c47168f8..0cbefac8923 100644 --- a/Experiments/Experiments/ABTest.swift +++ b/Experiments/Experiments/ABTest.swift @@ -2,10 +2,9 @@ import AutomatticTracks /// ABTest adds A/B testing experiments and runs the tests based on their variations from the ExPlat service. /// -public enum ABTest: String, CaseIterable, Codable { - /// Throwaway case, to prevent a compiler error: - /// `An enum with no cases cannot declare a raw type` - case null +public enum ABTest: String, Codable, CaseIterable { + /// Mocks for unit testing + case mockLoggedIn, mockLoggedOut /// A/A test to make sure there is no bias in the logged out state. /// Experiment ref: pbxNRc-1QS-p2 @@ -19,9 +18,6 @@ public enum ABTest: String, CaseIterable, Codable { /// Experiment ref: pbxNRc-2i4-p2 case applicationPasswordAuthentication = "woocommerceios_login_rest_api_project_202301_v2" - /// Mocks for unit testing - case mockLoggedIn, mockLoggedOut - /// Returns a variation for the given experiment /// public var variation: Variation? { @@ -37,14 +33,19 @@ public enum ABTest: String, CaseIterable, Codable { return .loggedIn case .aaTestLoggedOut, .applicationPasswordAuthentication: return .loggedOut - case .null: - return .none + // Mocks case .mockLoggedIn: return .loggedIn case .mockLoggedOut: return .loggedOut } } + + // Returns only the genuine ABTest cases. (After removing unit test mocks) + // + static var genuineCases: [ABTest] { + ABTest.allCases.filter { [.mockLoggedIn, .mockLoggedOut].contains($0) == false } + } } public extension ABTest { @@ -52,7 +53,7 @@ public extension ABTest { /// @MainActor static func start(for context: ExperimentContext) async { - let experiments = ABTest.allCases.filter { $0.context == context } + let experiments = ABTest.genuineCases.filter { $0.context == context } await withCheckedContinuation { continuation in guard !experiments.isEmpty else { From c0708bfcf930776dac5b95cd6236829c69d95e22 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 12:20:23 +0530 Subject: [PATCH 22/24] Give preference to remote value over cached value. --- Experiments/Experiments/CachedABTestVariationProvider.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Experiments/Experiments/CachedABTestVariationProvider.swift b/Experiments/Experiments/CachedABTestVariationProvider.swift index 1085f219666..6ec636382c1 100644 --- a/Experiments/Experiments/CachedABTestVariationProvider.swift +++ b/Experiments/Experiments/CachedABTestVariationProvider.swift @@ -18,11 +18,11 @@ public struct CachedABTestVariationProvider: ABTestVariationProvider { return abTest.variation ?? .control } - if let cachedVariation = cache.variation(for: abTest) { - return cachedVariation - } else if let variation = abTest.variation { + if let variation = abTest.variation { try? cache.assign(variation: variation, for: abTest) return variation + } else if let cachedVariation = cache.variation(for: abTest) { + return cachedVariation } else { return .control } From 40c08c34256c5f0430a0d317806f284d094ad0b2 Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 13:16:03 +0530 Subject: [PATCH 23/24] Point `Automattic-Tracks-iOS` to latest beta version. --- Podfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Podfile b/Podfile index 8a6d7881887..b0821db3621 100644 --- a/Podfile +++ b/Podfile @@ -27,9 +27,9 @@ def aztec end def tracks - # pod 'Automattic-Tracks-iOS', '~> 1.0.0' + pod 'Automattic-Tracks-iOS', '~> 1.1.0-beta.1' # pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :branch => 'trunk' - pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :commit => 'f990056f1d7f5ccac2d8c8d6e0a41ef60ea9c004' + # pod 'Automattic-Tracks-iOS', :git => 'https://github.com/Automattic/Automattic-Tracks-iOS.git', :commit => '' # pod 'Automattic-Tracks-iOS', :path => '../Automattic-Tracks-iOS' end From 527373f590096820456ef240c59cc9730833403e Mon Sep 17 00:00:00 2001 From: Sharma Elanthiraiyan Date: Thu, 2 Feb 2023 13:34:15 +0530 Subject: [PATCH 24/24] Update podfile.lock. --- Podfile.lock | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Podfile.lock b/Podfile.lock index d65b85c9211..09ec2baae80 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -6,7 +6,7 @@ PODS: - AppAuth/Core (1.6.0) - AppAuth/ExternalUserAgent (1.6.0): - AppAuth/Core - - Automattic-Tracks-iOS (1.1.0-beta): + - Automattic-Tracks-iOS (1.1.0-beta.1): - Sentry (~> 7.25) - Sodium (>= 0.9.1) - UIDeviceIdentifier (~> 2.0) @@ -75,7 +75,7 @@ PODS: DEPENDENCIES: - Alamofire (~> 4.8) - - Automattic-Tracks-iOS (from `https://github.com/Automattic/Automattic-Tracks-iOS.git`, commit `f990056f1d7f5ccac2d8c8d6e0a41ef60ea9c004`) + - Automattic-Tracks-iOS (~> 1.1.0-beta.1) - CocoaLumberjack (~> 3.7.4) - CocoaLumberjack/Swift (~> 3.7.4) - Gridicons (~> 1.2.0) @@ -99,6 +99,7 @@ SPEC REPOS: trunk: - Alamofire - AppAuth + - Automattic-Tracks-iOS - CocoaLumberjack - GoogleSignIn - Gridicons @@ -131,20 +132,10 @@ SPEC REPOS: - ZendeskSupportProvidersSDK - ZendeskSupportSDK -EXTERNAL SOURCES: - Automattic-Tracks-iOS: - :commit: f990056f1d7f5ccac2d8c8d6e0a41ef60ea9c004 - :git: https://github.com/Automattic/Automattic-Tracks-iOS.git - -CHECKOUT OPTIONS: - Automattic-Tracks-iOS: - :commit: f990056f1d7f5ccac2d8c8d6e0a41ef60ea9c004 - :git: https://github.com/Automattic/Automattic-Tracks-iOS.git - SPEC CHECKSUMS: Alamofire: 3ec537f71edc9804815215393ae2b1a8ea33a844 AppAuth: 8fca6b5563a5baef2c04bee27538025e4ceb2add - Automattic-Tracks-iOS: 382b2275c332d370b3c74f90d979b58f562ab5c1 + Automattic-Tracks-iOS: a30ebcffcdc33e5dd41aaa8a60999b4ef0e86700 CocoaLumberjack: 543c79c114dadc3b1aba95641d8738b06b05b646 GoogleSignIn: fd381840dbe7c1137aa6dc30849a5c3e070c034a Gridicons: 4455b9f366960121430e45997e32112ae49ffe1d @@ -178,6 +169,6 @@ SPEC CHECKSUMS: ZendeskSupportProvidersSDK: 2bdf8544f7cd0fd4c002546f5704b813845beb2a ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba -PODFILE CHECKSUM: 707be8e6bec5ee95efd80c5e966878e21e9e537f +PODFILE CHECKSUM: 7b77ea6b3b7ca7a6a8b75a3c174d2ca7b1a4bbc2 COCOAPODS: 1.11.3