Skip to content

Commit

Permalink
Onboarding highlights pixels (#3365)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1206329551987282/1208084960727012

**Description**:
1. Add pixels to Onboarding Highlights experiment.
[Commit](e57c3fb)
2. Remove previous onboarding experiment temporary pixels.
[Commit](ccc4a01)
  • Loading branch information
alessandroboron authored Sep 19, 2024
1 parent 7f9b60d commit 7b3cca1
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 99 deletions.
12 changes: 12 additions & 0 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ extension Pixel {
case onboardingIntroShownUnique
case onboardingIntroComparisonChartShownUnique
case onboardingIntroChooseBrowserCTAPressed
case onboardingIntroChooseAppIconImpressionUnique
case onboardingIntroChooseCustomAppIconColorCTAPressed
case onboardingIntroChooseAddressBarImpressionUnique
case onboardingIntroBottomAddressBarSelected

case onboardingContextualSearchOptionTappedUnique
case onboardingContextualSearchCustomUnique
case onboardingContextualSiteOptionTappedUnique
Expand All @@ -164,6 +169,7 @@ extension Pixel {
case daxDialogsFireEducationCancelledUnique
case daxDialogsEndOfJourneyTabUnique
case daxDialogsEndOfJourneyNewTabUnique
case daxDialogsEndOfJourneyDismissed

case widgetsOnboardingCTAPressed
case widgetsOnboardingDeclineOptionPressed
Expand Down Expand Up @@ -953,6 +959,11 @@ extension Pixel.Event {
case .onboardingIntroShownUnique: return "m_preonboarding_intro_shown_unique"
case .onboardingIntroComparisonChartShownUnique: return "m_preonboarding_comparison_chart_shown_unique"
case .onboardingIntroChooseBrowserCTAPressed: return "m_preonboarding_choose_browser_pressed"
case .onboardingIntroChooseAppIconImpressionUnique: return "m_preonboarding_choose_icon_impressions_unique"
case .onboardingIntroChooseCustomAppIconColorCTAPressed: return "m_preonboarding_icon_color_chosen"
case .onboardingIntroChooseAddressBarImpressionUnique: return "m_preonboarding_choose_address_bar_impressions_unique"
case .onboardingIntroBottomAddressBarSelected: return "m_preonboarding_bottom_address_bar_selected"

case .onboardingContextualSearchOptionTappedUnique: return "m_onboarding_search_option_tapped_unique"
case .onboardingContextualSiteOptionTappedUnique: return "m_onboarding_visit_site_option_tapped_unique"
case .onboardingContextualSecondSiteVisitUnique: return "m_second_sitevisit_unique"
Expand All @@ -973,6 +984,7 @@ extension Pixel.Event {
case .daxDialogsFireEducationCancelledUnique: return "m_dx_fe_ca_unique"
case .daxDialogsEndOfJourneyTabUnique: return "m_dx_end_tab_unique"
case .daxDialogsEndOfJourneyNewTabUnique: return "m_dx_end_new_tab_unique"
case .daxDialogsEndOfJourneyDismissed: return "m_dx_end_dialog_dismissed"

case .widgetsOnboardingCTAPressed: return "m_o_w_a"
case .widgetsOnboardingDeclineOptionPressed: return "m_o_w_d"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ final class NewTabDaxDialogFactory: NewTabDaxDialogProvider {
let message = onboardingManager.isOnboardingHighlightsEnabled ? UserText.HighlightsOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage : UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage

return FadeInView {
OnboardingFinalDialog(message: message, highFiveAction: {
OnboardingFinalDialog(message: message, highFiveAction: { [weak self] in
self?.onboardingPixelReporter.trackEndOfJourneyDialogCTAAction()
onDismiss()
})
.onboardingDaxDialogStyle()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory {
private func endOfJourneyDialog(delegate: ContextualOnboardingDelegate, pixelName: Pixel.Event) -> some View {
let message = onboardingManager.isOnboardingHighlightsEnabled ? UserText.HighlightsOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage : UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage

return OnboardingFinalDialog(message: message, highFiveAction: { [weak delegate] in
return OnboardingFinalDialog(message: message, highFiveAction: { [weak delegate, weak self] in
delegate?.didTapDismissContextualOnboardingAction()
self?.contextualOnboardingPixelReporter.trackEndOfJourneyDialogCTAAction()
})
.onFirstAppear { [weak self] in
self?.contextualOnboardingLogic.setFinalOnboardingDialogSeen()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,24 @@ final class OnboardingIntroViewModel: ObservableObject {
private let onboardingManager: OnboardingHighlightsManaging
private let isIpad: Bool
private let urlOpener: URLOpener
private let appIconProvider: () -> AppIcon
private let addressBarPositionProvider: () -> AddressBarPosition

init(
pixelReporter: OnboardingIntroPixelReporting,
onboardingManager: OnboardingHighlightsManaging = OnboardingManager(),
isIpad: Bool = UIDevice.current.userInterfaceIdiom == .pad,
urlOpener: URLOpener = UIApplication.shared
urlOpener: URLOpener = UIApplication.shared,
appIconProvider: @escaping () -> AppIcon = { AppIconManager.shared.appIcon },
addressBarPositionProvider: @escaping () -> AddressBarPosition = { AppUserDefaults().currentAddressBarPosition }
) {
self.pixelReporter = pixelReporter
self.onboardingManager = onboardingManager
self.isIpad = isIpad
self.urlOpener = urlOpener
self.appIconProvider = appIconProvider
self.addressBarPositionProvider = addressBarPositionProvider

introSteps = if onboardingManager.isOnboardingHighlightsEnabled {
isIpad ? OnboardingIntroStep.highlightsIPadFlow : OnboardingIntroStep.highlightsIPhoneFlow
} else {
Expand Down Expand Up @@ -79,14 +86,22 @@ final class OnboardingIntroViewModel: ObservableObject {
}

func appIconPickerContinueAction() {
if appIconProvider() != .defaultAppIcon {
pixelReporter.trackChooseCustomAppIconColor()
}

if isIpad {
onCompletingOnboardingIntro?()
} else {
state = makeViewState(for: .addressBarPositionSelection)
pixelReporter.trackAddressBarPositionSelectionImpression()
}
}

func selectAddressBarPositionAction() {
if addressBarPositionProvider() == .bottom {
pixelReporter.trackChooseBottomAddressBarPosition()
}
onCompletingOnboardingIntro?()
}

Expand Down Expand Up @@ -127,6 +142,7 @@ private extension OnboardingIntroViewModel {
func handleSetDefaultBrowserAction() {
if onboardingManager.isOnboardingHighlightsEnabled {
state = makeViewState(for: .appIconSelection)
pixelReporter.trackChooseAppIconImpression()
} else {
onCompletingOnboardingIntro?()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ protocol OnboardingIntroImpressionReporting {
protocol OnboardingIntroPixelReporting: OnboardingIntroImpressionReporting {
func trackBrowserComparisonImpression()
func trackChooseBrowserCTAAction()
func trackChooseAppIconImpression()
func trackChooseCustomAppIconColor()
func trackAddressBarPositionSelectionImpression()
func trackChooseBottomAddressBarPosition()
}

protocol OnboardingCustomInteractionPixelReporting {
Expand All @@ -58,11 +62,12 @@ protocol OnboardingCustomInteractionPixelReporting {
func trackPrivacyDashboardOpenedForFirstTime()
}

protocol OnboardingScreenImpressionReporting {
protocol OnboardingDaxDialogsReporting {
func trackScreenImpression(event: Pixel.Event)
func trackEndOfJourneyDialogCTAAction()
}

typealias OnboardingPixelReporting = OnboardingIntroImpressionReporting & OnboardingIntroPixelReporting & OnboardingSearchSuggestionsPixelReporting & OnboardingSiteSuggestionsPixelReporting & OnboardingCustomInteractionPixelReporting & OnboardingScreenImpressionReporting
typealias OnboardingPixelReporting = OnboardingIntroImpressionReporting & OnboardingIntroPixelReporting & OnboardingSearchSuggestionsPixelReporting & OnboardingSiteSuggestionsPixelReporting & OnboardingCustomInteractionPixelReporting & OnboardingDaxDialogsReporting

// MARK: - Implementation

Expand Down Expand Up @@ -146,22 +151,38 @@ extension OnboardingPixelReporter: OnboardingIntroPixelReporting {
fire(event: .onboardingIntroChooseBrowserCTAPressed, unique: false)
}

func trackChooseAppIconImpression() {
fire(event: .onboardingIntroChooseAppIconImpressionUnique, unique: true, includedParameters: [.appVersion])
}

func trackChooseCustomAppIconColor() {
fire(event: .onboardingIntroChooseCustomAppIconColorCTAPressed, unique: false, includedParameters: [.appVersion])
}

func trackAddressBarPositionSelectionImpression() {
fire(event: .onboardingIntroChooseAddressBarImpressionUnique, unique: true, includedParameters: [.appVersion])
}

func trackChooseBottomAddressBarPosition() {
fire(event: .onboardingIntroBottomAddressBarSelected, unique: false, includedParameters: [.appVersion])
}

}

// MARK: - OnboardingPixelReporter + List

extension OnboardingPixelReporter: OnboardingSearchSuggestionsPixelReporting {

func trackSearchSuggetionOptionTapped() {
fire(event: .onboardingContextualSearchOptionTappedUnique, unique: true)
// Left empty on purpose. These were temporary pixels in iOS. macOS will still use them.
}

}

extension OnboardingPixelReporter: OnboardingSiteSuggestionsPixelReporting {

func trackSiteSuggetionOptionTapped() {
fire(event: .onboardingContextualSiteOptionTappedUnique, unique: true)
// Left empty on purpose. These were temporary pixels in iOS. macOS will still use them.
}

}
Expand Down Expand Up @@ -199,12 +220,16 @@ extension OnboardingPixelReporter: OnboardingCustomInteractionPixelReporting {

// MARK: - OnboardingPixelReporter + Screen Impression

extension OnboardingPixelReporter: OnboardingScreenImpressionReporting {
extension OnboardingPixelReporter: OnboardingDaxDialogsReporting {

func trackScreenImpression(event: Pixel.Event) {
fire(event: event, unique: true)
}

func trackEndOfJourneyDialogCTAAction() {
fire(event: .daxDialogsEndOfJourneyDismissed, unique: false)
}

}

struct EnqueuedPixel {
Expand Down
14 changes: 14 additions & 0 deletions DuckDuckGoTests/ContextualDaxDialogsFactoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,20 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase {
XCTAssertTrue(pixelReporterMock.didCallTrackScreenImpressionCalled)
XCTAssertEqual(pixelReporterMock.capturedScreenImpression, .onboardingContextualTryVisitSiteUnique)
}

func testWhenEndOfJourneyDialogCTAIsTappedThenExpectedPixelFires() throws {
// GIVEN
let spec = DaxDialogs.BrowsingSpec.final
let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {})
let view = try XCTUnwrap(find(OnboardingFinalDialog.self, in: result))
XCTAssertFalse(pixelReporterMock.didCallTrackEndOfJourneyDialogDismiss)

// WHEN
view.highFiveAction()

// THEN
XCTAssertTrue(pixelReporterMock.didCallTrackEndOfJourneyDialogDismiss)
}
}

extension ContextualDaxDialogsFactoryTests {
Expand Down
21 changes: 18 additions & 3 deletions DuckDuckGoTests/ContextualOnboardingNewTabDialogFactoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,30 +124,45 @@ class ContextualOnboardingNewTabDialogFactoryTests: XCTestCase {

// MARK: - Pixels

func testWhenOnboardingTrySearchDialogAppearForTheFirstTime_ThenSendFireExpectedPixel() {
func testWhenOnboardingTrySearchDialogAppearForTheFirstTime_ThenFireExpectedPixel() {
// GIVEN
let spec = DaxDialogs.HomeScreenSpec.initial
let pixelEvent = Pixel.Event.onboardingContextualTrySearchUnique
// TEST
testDialogDefinedBy(spec: spec, firesEvent: pixelEvent)
}

func testWhenOnboardingTryVisitSiteDialogAppearForTheFirstTime_ThenSendFireExpectedPixel() {
func testWhenOnboardingTryVisitSiteDialogAppearForTheFirstTime_ThenFireExpectedPixel() {
// GIVEN
let spec = DaxDialogs.HomeScreenSpec.subsequent
let pixelEvent = Pixel.Event.onboardingContextualTryVisitSiteUnique
// TEST
testDialogDefinedBy(spec: spec, firesEvent: pixelEvent)
}

func testWhenOnboardingFinalDialogAppearForTheFirstTime_ThenSendFireExpectedPixel() {
func testWhenOnboardingFinalDialogAppearForTheFirstTime_ThenFireExpectedPixel() {
// GIVEN
let spec = DaxDialogs.HomeScreenSpec.final
let pixelEvent = Pixel.Event.daxDialogsEndOfJourneyNewTabUnique
// TEST
testDialogDefinedBy(spec: spec, firesEvent: pixelEvent)
}

func testWhenOnboardingFinalDialogCTAIsTapped_ThenFireExpectedPixel() throws {
// GIVEN
let view = factory.createDaxDialog(for: DaxDialogs.HomeScreenSpec.final, onDismiss: {})
let host = UIHostingController(rootView: view)
window.rootViewController = host
let finalDialog = try XCTUnwrap(find(OnboardingFinalDialog.self, in: host))
XCTAssertFalse(pixelReporterMock.didCallTrackEndOfJourneyDialogDismiss)

// WHEN
finalDialog.highFiveAction()

// THEN
XCTAssertTrue(pixelReporterMock.didCallTrackEndOfJourneyDialogDismiss)
}

}

private extension ContextualOnboardingNewTabDialogFactoryTests {
Expand Down
Loading

0 comments on commit 7b3cca1

Please sign in to comment.