Skip to content

Commit 24eb45c

Browse files
authored
Use remote feature flag to use application passwords for Jetpack Sites (#16126)
2 parents 0d38dd7 + 5eb23ae commit 24eb45c

File tree

6 files changed

+87
-29
lines changed

6 files changed

+87
-29
lines changed

Modules/Sources/Experiments/DefaultFeatureFlagService.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,6 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
100100
return true
101101
case .pointOfSaleHistoricalOrdersi1:
102102
return buildConfig == .localDeveloper || buildConfig == .alpha
103-
case .applicationPasswordExperiment:
104-
return buildConfig == .localDeveloper || buildConfig == .alpha
105103
case .pointOfSaleLocalCatalogi1:
106104
return buildConfig == .localDeveloper || buildConfig == .alpha
107105
case .ciabBookings:

Modules/Sources/Experiments/FeatureFlag.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,6 @@ public enum FeatureFlag: Int {
207207
///
208208
case pointOfSaleHistoricalOrdersi1
209209

210-
/// Enables switching Jetpack requests to use application password
211-
///
212-
case applicationPasswordExperiment
213-
214210
/// Enables Local Catalog i1 in Point of Sale.
215211
/// It syncs products and variations to local storage and display them in POS for quick access.
216212
///

Modules/Sources/Networking/Remote/FeatureFlagRemote.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public enum RemoteFeatureFlag: Decodable {
3030
case storeCreationCompleteNotification
3131
case hardcodedPlanUpgradeDetailsMilestone1AreAccurate
3232
case pointOfSale
33+
case appPasswordsForJetpackSites
3334

3435
init?(rawValue: String) {
3536
switch rawValue {
@@ -39,6 +40,8 @@ public enum RemoteFeatureFlag: Decodable {
3940
self = .hardcodedPlanUpgradeDetailsMilestone1AreAccurate
4041
case "woo_pos":
4142
self = .pointOfSale
43+
case "woo_app_passwords_for_jetpack_sites":
44+
self = .appPasswordsForJetpackSites
4245
default:
4346
return nil
4447
}

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
-----
66
- [*] Order details: Display only physical items in the Shipping Labels section. [https://github.com/woocommerce/woocommerce-ios/pull/16127]
77
- [internal] Address deprecated view modifiers usage following iOS17 API updates [https://github.com/woocommerce/woocommerce-ios/pull/16080]
8+
- [***] Performance improvements for users logging in with WordPress.com accounts [https://github.com/woocommerce/woocommerce-ios/pull/16126]
89
- [internal] Fix back swipe gesture detection compatibility for iOS26 [https://github.com/woocommerce/woocommerce-ios/pull/16133]
910
- [internal] POS Modularization: Removed direct ServiceLocator usage within POS by requiring complex Woo app target dependencies to be injected via POS dependency protocols, and moved reusable dependencies to WooFoundation and Yosemite [https://github.com/woocommerce/woocommerce-ios/pull/16132]
1011
- [*] Workaround to make custom field decoding more reliable with sites which return incorrectly formatted meta_data for products, variations, and orders. [https://github.com/woocommerce/woocommerce-ios/pull/16141]

WooCommerce/Classes/ViewRelated/Dashboard/Settings/Beta features/ApplicationPasswordsExperimentState.swift

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,17 @@ final class ApplicationPasswordsExperimentAvailabilityChecker: ApplicationPasswo
8484
}
8585
}
8686

87+
@MainActor
8788
func fetchAvailability() async -> Bool {
88-
await withCheckedContinuation { continuation in
89-
//TODO: - put the remote FF checking here
90-
//For now rely on local FF for mocked value to avoid unwanted exposure
91-
let mockResultValue = ServiceLocator.featureFlagService.isFeatureFlagEnabled(
92-
.applicationPasswordExperiment
93-
)
94-
95-
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
96-
continuation.resume(returning: mockResultValue)
97-
}
98-
99-
cachedRemoteFFValue = mockResultValue
89+
let isEnabled = await withCheckedContinuation { continuation in
90+
stores.dispatch(FeatureFlagAction.isRemoteFeatureFlagEnabled(
91+
.appPasswordsForJetpackSites,
92+
defaultValue: false
93+
) { isEnabled in
94+
continuation.resume(returning: isEnabled)
95+
})
10096
}
97+
cachedRemoteFFValue = isEnabled
98+
return isEnabled
10199
}
102100
}

WooCommerce/WooCommerceTests/ViewRelated/Settings/ApplicationPasswordsExperimentAvailabilityCheckerTests.swift

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Yosemite
44

55
final class ApplicationPasswordsExperimentAvailabilityCheckerTests: XCTestCase {
66
private var availabilityChecker: ApplicationPasswordsExperimentAvailabilityCheckerProtocol!
7-
private var stores: StoresManager!
7+
private var stores: MockStoresManager!
88
private var userDefaults: UserDefaults!
99

1010
override func tearDown() {
@@ -16,7 +16,7 @@ final class ApplicationPasswordsExperimentAvailabilityCheckerTests: XCTestCase {
1616

1717
private func setupEnvironment(
1818
isWPComAuthenticated: Bool,
19-
isRemoteFFEnabled: Bool
19+
cachedRemoteFFEnabled: Bool
2020
) throws {
2121
stores = MockStoresManager(
2222
sessionManager: .makeForTesting(
@@ -26,31 +26,93 @@ final class ApplicationPasswordsExperimentAvailabilityCheckerTests: XCTestCase {
2626
)
2727

2828
userDefaults = try XCTUnwrap(UserDefaults(suiteName: "TestingSuite"))
29-
userDefaults[.applicationPasswordsExperimentRemoteFFValue] = isRemoteFFEnabled
29+
userDefaults[.applicationPasswordsExperimentRemoteFFValue] = cachedRemoteFFEnabled
3030

3131
availabilityChecker = ApplicationPasswordsExperimentAvailabilityChecker(
3232
userDefaults: userDefaults,
3333
stores: stores
3434
)
3535
}
3636

37-
func test_when_wpcom_authenticated_and_remote_ff_enabled_then_isAvailable_returns_true() throws {
38-
try setupEnvironment(isWPComAuthenticated: true, isRemoteFFEnabled: true)
37+
func test_when_wpcom_authenticated_and_cached_remote_ff_enabled_then_isAvailable_returns_true() throws {
38+
try setupEnvironment(isWPComAuthenticated: true, cachedRemoteFFEnabled: true)
3939
XCTAssertTrue(availabilityChecker.isAvailable)
4040
}
4141

42-
func test_when_wpcom_authenticated_and_remote_ff_disabled_then_isAvailable_returns_false() throws {
43-
try setupEnvironment(isWPComAuthenticated: true, isRemoteFFEnabled: false)
42+
func test_when_wpcom_authenticated_and_cached_remote_ff_disabled_then_isAvailable_returns_false() throws {
43+
try setupEnvironment(isWPComAuthenticated: true, cachedRemoteFFEnabled: false)
4444
XCTAssertFalse(availabilityChecker.isAvailable)
4545
}
4646

47-
func test_when_not_wpcom_authenticated_and_remote_ff_enabled_then_isAvailable_returns_false() throws {
48-
try setupEnvironment(isWPComAuthenticated: false, isRemoteFFEnabled: true)
47+
func test_when_not_wpcom_authenticated_and_cached_remote_ff_enabled_then_isAvailable_returns_false() throws {
48+
try setupEnvironment(isWPComAuthenticated: false, cachedRemoteFFEnabled: true)
4949
XCTAssertFalse(availabilityChecker.isAvailable)
5050
}
5151

52-
func test_when_not_wpcom_authenticated_and_remote_ff_disabled_then_isAvailable_returns_false() throws {
53-
try setupEnvironment(isWPComAuthenticated: false, isRemoteFFEnabled: false)
52+
func test_when_not_wpcom_authenticated_and_cached_remote_ff_disabled_then_isAvailable_returns_false() throws {
53+
try setupEnvironment(isWPComAuthenticated: false, cachedRemoteFFEnabled: false)
5454
XCTAssertFalse(availabilityChecker.isAvailable)
5555
}
56+
57+
@MainActor
58+
func test_when_cached_flag_is_disabled_and_remote_flag_is_enabled_then_fetchAvailability_returns_true() async throws {
59+
// Given
60+
stores = MockStoresManager(
61+
sessionManager: .makeForTesting(
62+
authenticated: true,
63+
isWPCom: true
64+
)
65+
)
66+
stores.whenReceivingAction(ofType: FeatureFlagAction.self) { action in
67+
switch action {
68+
case .isRemoteFeatureFlagEnabled(_, _, let completion):
69+
completion(true)
70+
}
71+
}
72+
73+
userDefaults = try XCTUnwrap(UserDefaults(suiteName: UUID().uuidString))
74+
userDefaults[.applicationPasswordsExperimentRemoteFFValue] = false
75+
76+
availabilityChecker = ApplicationPasswordsExperimentAvailabilityChecker(
77+
userDefaults: userDefaults,
78+
stores: stores
79+
)
80+
81+
// When
82+
let availability = await availabilityChecker.fetchAvailability()
83+
84+
// Then
85+
XCTAssertTrue(availability)
86+
}
87+
88+
@MainActor
89+
func test_when_cached_flag_is_enabled_and_remote_flag_is_disabled_then_fetchAvailability_returns_false() async throws {
90+
// Given
91+
stores = MockStoresManager(
92+
sessionManager: .makeForTesting(
93+
authenticated: true,
94+
isWPCom: true
95+
)
96+
)
97+
stores.whenReceivingAction(ofType: FeatureFlagAction.self) { action in
98+
switch action {
99+
case .isRemoteFeatureFlagEnabled(_, _, let completion):
100+
completion(false)
101+
}
102+
}
103+
104+
userDefaults = try XCTUnwrap(UserDefaults(suiteName: UUID().uuidString))
105+
userDefaults[.applicationPasswordsExperimentRemoteFFValue] = true
106+
107+
availabilityChecker = ApplicationPasswordsExperimentAvailabilityChecker(
108+
userDefaults: userDefaults,
109+
stores: stores
110+
)
111+
112+
// When
113+
let availability = await availabilityChecker.fetchAvailability()
114+
115+
// Then
116+
XCTAssertFalse(availability)
117+
}
56118
}

0 commit comments

Comments
 (0)