Skip to content

Commit 0d0862d

Browse files
authored
Merge pull request #7706 from woocommerce/issue/7597-add-analytics
Login: Add tracks for Jetpack connection flow
2 parents 7942619 + aa91c90 commit 0d0862d

File tree

6 files changed

+138
-4
lines changed

6 files changed

+138
-4
lines changed

WooCommerce/Classes/Analytics/WooAnalyticsEvent.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,3 +1591,17 @@ extension WooAnalyticsEvent {
15911591
WooAnalyticsEvent(statName: .universalLinkFailed, properties: [Key.url.rawValue: url.absoluteString])
15921592
}
15931593
}
1594+
1595+
// MARK: - Jetpack connection
1596+
//
1597+
extension WooAnalyticsEvent {
1598+
enum LoginJetpackConnection {
1599+
enum Key: String {
1600+
case selfHosted = "is_selfhosted_site"
1601+
}
1602+
1603+
static func jetpackConnectionErrorShown(selfHostedSite: Bool) -> WooAnalyticsEvent {
1604+
WooAnalyticsEvent(statName: .loginJetpackConnectionErrorShown, properties: [Key.selfHosted.rawValue: selfHostedSite])
1605+
}
1606+
}
1607+
}

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,14 @@ public enum WooAnalyticsStat: String {
680680
// MARK: Universal Links
681681
case universalLinkOpened = "universal_link_opened"
682682
case universalLinkFailed = "universal_link_failed"
683+
684+
// MARK: Login Jetpack Connection
685+
case loginJetpackConnectionErrorShown = "login_jetpack_connection_error_shown"
686+
case loginJetpackConnectButtonTapped = "login_jetpack_connect_button_tapped"
687+
case loginJetpackConnectCompleted = "login_jetpack_connect_completed"
688+
case loginJetpackConnectDismissed = "login_jetpack_connect_dismissed"
689+
case loginJetpackConnectionURLFetchFailed = "login_jetpack_connection_url_fetch_failed"
690+
case loginJetpackConnectionVerificationFailed = "login_jetpack_connection_verification_failed"
683691
}
684692

685693
public extension WooAnalyticsStat {

WooCommerce/Classes/Authentication/Navigation Exceptions/JetpackConnectionErrorViewModel.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ final class JetpackConnectionErrorViewModel: ULErrorViewModel {
88
private let siteURL: String
99
private var jetpackConnectionURL: URL?
1010
private let stores: StoresManager
11+
private let analytics: Analytics
1112
private let isPrimaryButtonLoadingSubject = CurrentValueSubject<Bool, Never>(false)
1213
private let jetpackSetupCompletionHandler: (String) -> Void
1314

1415
init(siteURL: String,
1516
credentials: WordPressOrgCredentials,
1617
stores: StoresManager = ServiceLocator.stores,
18+
analytics: Analytics = ServiceLocator.analytics,
1719
onJetpackSetupCompletion: @escaping (String) -> Void) {
1820
self.siteURL = siteURL
1921
self.stores = stores
22+
self.analytics = analytics
2023
self.jetpackSetupCompletionHandler = onJetpackSetupCompletion
2124
authenticate(with: credentials)
2225
fetchJetpackConnectionURL()
@@ -51,10 +54,11 @@ final class JetpackConnectionErrorViewModel: ULErrorViewModel {
5154
let secondaryButtonTitle = Localization.secondaryButtonTitle
5255

5356
func viewDidLoad(_ viewController: UIViewController?) {
54-
// TODO: Tracks?
57+
analytics.track(event: .LoginJetpackConnection.jetpackConnectionErrorShown(selfHostedSite: true))
5558
}
5659

5760
func didTapPrimaryButton(in viewController: UIViewController?) {
61+
analytics.track(.loginJetpackConnectButtonTapped)
5862
showJetpackConnectionWebView(from: viewController)
5963
}
6064

@@ -108,6 +112,7 @@ private extension JetpackConnectionErrorViewModel {
108112
case .success(let url):
109113
self.jetpackConnectionURL = url
110114
case .failure(let error):
115+
self.analytics.track(.loginJetpackConnectionURLFetchFailed, withError: error)
111116
DDLogWarn("⚠️ Error fetching Jetpack connection URL: \(error)")
112117
}
113118
}
@@ -127,11 +132,13 @@ private extension JetpackConnectionErrorViewModel {
127132
case .success(let user):
128133
guard let emailAddress = user.wpcomUser?.email else {
129134
DDLogWarn("⚠️ Cannot find connected WPcom user")
135+
self.analytics.track(.loginJetpackConnectionVerificationFailed)
130136
return self.showSetupErrorNotice(in: viewController)
131137
}
132138
self.jetpackSetupCompletionHandler(emailAddress)
133139
case .failure(let error):
134140
DDLogWarn("⚠️ Error fetching Jetpack user: \(error)")
141+
self.analytics.track(.loginJetpackConnectionVerificationFailed, withError: error)
135142
self.showSetupErrorNotice(in: viewController)
136143
}
137144
}

WooCommerce/Classes/Authentication/Navigation Exceptions/JetpackConnectionWebViewModel.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,20 @@ final class JetpackConnectionWebViewModel: AuthenticatedWebViewModel {
1010
let siteURL: String
1111
let completionHandler: () -> Void
1212

13-
init(initialURL: URL, siteURL: String, completion: @escaping () -> Void) {
13+
private let analytics: Analytics
14+
15+
init(initialURL: URL,
16+
siteURL: String,
17+
analytics: Analytics = ServiceLocator.analytics,
18+
completion: @escaping () -> Void) {
19+
self.analytics = analytics
1420
self.initialURL = initialURL
1521
self.siteURL = siteURL
1622
self.completionHandler = completion
1723
}
1824

1925
func handleDismissal() {
20-
// TODO: tracks?
26+
analytics.track(.loginJetpackConnectDismissed)
2127
}
2228

2329
func handleRedirect(for url: URL?) {
@@ -40,7 +46,7 @@ final class JetpackConnectionWebViewModel: AuthenticatedWebViewModel {
4046
}
4147

4248
private func handleSetupCompletion() {
43-
// TODO: tracks?
49+
analytics.track(.loginJetpackConnectCompleted)
4450
completionHandler()
4551
}
4652
}

WooCommerce/WooCommerceTests/Authentication/JetpackConnectionErrorViewModelTests.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,73 @@ final class JetpackConnectionErrorViewModelTests: XCTestCase {
100100
// Then
101101
XCTAssertTrue(navigationController.topViewController is AuthenticatedWebViewController)
102102
}
103+
104+
func test_error_view_is_tracked() {
105+
// Given
106+
let analyticsProvider = MockAnalyticsProvider()
107+
let analytics = WooAnalytics(analyticsProvider: analyticsProvider)
108+
let viewModel = JetpackConnectionErrorViewModel(siteURL: siteURL,
109+
credentials: credentials,
110+
analytics: analytics,
111+
onJetpackSetupCompletion: { _ in })
112+
113+
// When
114+
viewModel.viewDidLoad(nil)
115+
116+
// Then
117+
XCTAssertNotNil(analyticsProvider.receivedEvents.first(where: { $0 == "login_jetpack_connection_error_shown" }))
118+
}
119+
120+
func test_primary_button_tap_is_tracked() {
121+
// Given
122+
let analyticsProvider = MockAnalyticsProvider()
123+
let analytics = WooAnalytics(analyticsProvider: analyticsProvider)
124+
let viewModel = JetpackConnectionErrorViewModel(siteURL: siteURL,
125+
credentials: credentials,
126+
analytics: analytics,
127+
onJetpackSetupCompletion: { _ in })
128+
129+
// When
130+
viewModel.didTapPrimaryButton(in: nil)
131+
132+
// Then
133+
XCTAssertNotNil(analyticsProvider.receivedEvents.first(where: { $0 == "login_jetpack_connect_button_tapped" }))
134+
}
135+
136+
func test_failure_to_fetch_connection_url_is_tracked() throws {
137+
// Given
138+
let stores = MockStoresManager(sessionManager: .makeForTesting())
139+
var completed = false
140+
stores.whenReceivingAction(ofType: JetpackConnectionAction.self) { action in
141+
switch action {
142+
case .fetchJetpackConnectionURL(let completion):
143+
let error = NSError(domain: "Test", code: 123)
144+
completion(.failure(error))
145+
completed = true
146+
default:
147+
break
148+
}
149+
}
150+
151+
let analyticsProvider = MockAnalyticsProvider()
152+
let analytics = WooAnalytics(analyticsProvider: analyticsProvider)
153+
154+
// When
155+
_ = JetpackConnectionErrorViewModel(siteURL: siteURL,
156+
credentials: credentials,
157+
stores: stores,
158+
analytics: analytics,
159+
onJetpackSetupCompletion: { _ in })
160+
waitUntil {
161+
completed == true
162+
}
163+
164+
// Then
165+
let indexOfEvent = try XCTUnwrap(analyticsProvider.receivedEvents.firstIndex(where: { $0 == "login_jetpack_connection_url_fetch_failed" }))
166+
let properties = try XCTUnwrap(analyticsProvider.receivedProperties[indexOfEvent])
167+
XCTAssertEqual(properties["error_code"] as? String, "123")
168+
XCTAssertEqual(properties["error_domain"] as? String, "Test")
169+
}
103170
}
104171

105172
private extension JetpackConnectionErrorViewModelTests {

WooCommerce/WooCommerceTests/Authentication/JetpackConnectionWebViewModelTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,36 @@ final class JetpackConnectionWebViewModelTests: XCTestCase {
2525
XCTAssertTrue(completionTriggered)
2626
}
2727

28+
func test_dismissal_is_tracked() throws {
29+
// Given
30+
let analyticsProvider = MockAnalyticsProvider()
31+
let analytics = WooAnalytics(analyticsProvider: analyticsProvider)
32+
33+
let siteURL = "https://test.com"
34+
let initialURL = try XCTUnwrap(URL(string: "https://jetpack.wordpress.com/jetpack.authorize/1/"))
35+
let viewModel = JetpackConnectionWebViewModel(initialURL: initialURL, siteURL: siteURL, analytics: analytics, completion: {})
36+
37+
// When
38+
viewModel.handleDismissal()
39+
40+
// Then
41+
XCTAssertNotNil(analyticsProvider.receivedEvents.first(where: { $0 == "login_jetpack_connect_dismissed" }))
42+
}
43+
44+
func test_completion_is_tracked() async throws {
45+
// Given
46+
let analyticsProvider = MockAnalyticsProvider()
47+
let analytics = WooAnalytics(analyticsProvider: analyticsProvider)
48+
49+
let siteURL = "https://test.com"
50+
let initialURL = try XCTUnwrap(URL(string: "https://jetpack.wordpress.com/jetpack.authorize/1/"))
51+
let viewModel = JetpackConnectionWebViewModel(initialURL: initialURL, siteURL: siteURL, analytics: analytics, completion: {})
52+
53+
// When
54+
let finalUrl = try XCTUnwrap(URL(string: siteURL + "/wp-admin"))
55+
_ = await viewModel.decidePolicy(for: finalUrl)
56+
57+
// Then
58+
XCTAssertNotNil(analyticsProvider.receivedEvents.first(where: { $0 == "login_jetpack_connect_completed" }))
59+
}
2860
}

0 commit comments

Comments
 (0)