Skip to content

Commit

Permalink
[DuckPlayer] Experiment Fix - Update Test Variables and pixel names (#…
Browse files Browse the repository at this point in the history
…3363)

Task/Issue URL: https://app.asana.com/0/1204099484721401/1208336598845809/f

Description:

Moves DuckPlayer launch experiment to be behind the Launch Feature flag to avoid enrolling users before time. (As it happened)
Renames variables and pixels to avoid collisions with the old (bogus) experiment
  • Loading branch information
afterxleep authored Sep 17, 2024
1 parent 84f3ac5 commit 741daa5
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 61 deletions.
10 changes: 5 additions & 5 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1619,11 +1619,11 @@ extension Pixel.Event {
case .pproFeedbackSubmitScreenFAQClick: return "m_ppro_feedback_submit-screen-faq_click"

// MARK: Duckplayer experiment
case .duckplayerExperimentCohortAssign: return "duckplayer_experiment_cohort_assign"
case .duckplayerExperimentSearch: return "duckplayer_experiment_search"
case .duckplayerExperimentDailySearch: return "duckplayer_experiment_daily_search"
case .duckplayerExperimentWeeklySearch: return "duckplayer_experiment_weekly_search"
case .duckplayerExperimentYoutubePageView: return "duckplayer_experiment_youtube_page_view"
case .duckplayerExperimentCohortAssign: return "duckplayer_experiment_cohort_assign_v2"
case .duckplayerExperimentSearch: return "duckplayer_experiment_search_v2"
case .duckplayerExperimentDailySearch: return "duckplayer_experiment_daily_search_v2"
case .duckplayerExperimentWeeklySearch: return "duckplayer_experiment_weekly_search_v2"
case .duckplayerExperimentYoutubePageView: return "duckplayer_experiment_youtube_page_view_v2"

}
}
Expand Down
66 changes: 33 additions & 33 deletions DuckDuckGo/DuckPlayer/DuckPlayerLaunchExperiment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,19 @@ final class DuckPlayerLaunchExperiment: DuckPlayerLaunchExperimentHandling {
private let dateProvider: DuckPlayerExperimentDateProvider

@UserDefaultsWrapper(key: .duckPlayerPixelExperimentLastWeekPixelFired, defaultValue: nil)
private var lastWeekPixelFired: Int?
private var lastWeekPixelFiredV2: Int?

@UserDefaultsWrapper(key: .duckPlayerPixelExperimentLastDayPixelFired, defaultValue: nil)
private var lastDayPixelFired: Int?
private var lastDayPixelFiredV2: Int?

@UserDefaultsWrapper(key: .duckPlayerPixelExperimentLastVideoIDRendered, defaultValue: nil)
private var lastVideoIDReported: String?
private var lastVideoIDReportedV2: String?

@UserDefaultsWrapper(key: .duckPlayerPixelExperimentEnrollmentDate, defaultValue: nil)
var enrollmentDate: Date?
var enrollmentDateV2: Date?

@UserDefaultsWrapper(key: .duckPlayerPixelExperimentCohort, defaultValue: nil)
var experimentCohort: String?
var experimentCohortV2: String?

private var isInternalUser: Bool

Expand All @@ -113,7 +113,7 @@ final class DuckPlayerLaunchExperiment: DuckPlayerLaunchExperimentHandling {

private var dates: (day: Int, week: Int)? {
guard isEnrolled,
let enrollmentDate = enrollmentDate else { return nil }
let enrollmentDate = enrollmentDateV2 else { return nil }
let currentDate = dateProvider.currentDate
let calendar = Calendar.current
let dayDifference = calendar.dateComponents([.day], from: enrollmentDate, to: currentDate).day ?? 0
Expand All @@ -123,7 +123,7 @@ final class DuckPlayerLaunchExperiment: DuckPlayerLaunchExperimentHandling {

private var formattedEnrollmentDate: String? {
guard isEnrolled,
let enrollmentDate = enrollmentDate else { return nil }
let enrollmentDate = enrollmentDateV2 else { return nil }
return Self.formattedDate(enrollmentDate)
}

Expand All @@ -135,11 +135,11 @@ final class DuckPlayerLaunchExperiment: DuckPlayerLaunchExperimentHandling {
}

var isEnrolled: Bool {
return enrollmentDate != nil && experimentCohort != nil
return enrollmentDateV2 != nil && experimentCohortV2 != nil
}

var isExperimentCohort: Bool {
return experimentCohort == "experiment"
return experimentCohortV2 == "experiment"
}

func assignUserToCohort() {
Expand All @@ -149,32 +149,32 @@ final class DuckPlayerLaunchExperiment: DuckPlayerLaunchExperimentHandling {
if isInternalUser {
cohort = .experiment
}
experimentCohort = cohort.rawValue
enrollmentDate = dateProvider.currentDate
experimentCohortV2 = cohort.rawValue
enrollmentDateV2 = dateProvider.currentDate
fireEnrollmentPixel()
}
}

private func fireEnrollmentPixel() {
guard isEnrolled,
let experimentCohort = experimentCohort,
let experimentCohortV2 = experimentCohortV2,
let formattedEnrollmentDate else { return }

let params = [Constants.variantKey: experimentCohort, Constants.enrollmentKey: formattedEnrollmentDate]
let params = [Constants.variantKey: experimentCohortV2, Constants.enrollmentKey: formattedEnrollmentDate]
pixel.fireDuckPlayerExperimentPixel(pixel: .duckplayerExperimentCohortAssign, withAdditionalParameters: params)
}

func fireSearchPixels() {
if isEnrolled {
guard isEnrolled,
let experimentCohort = experimentCohort,
let experimentCohortV2 = experimentCohortV2,
let dates,
let formattedEnrollmentDate else {
return
}

var params = [
Constants.variantKey: experimentCohort,
Constants.variantKey: experimentCohortV2,
Constants.dayKey: "\(dates.day)",
Constants.enrollmentKey: formattedEnrollmentDate
]
Expand All @@ -183,56 +183,56 @@ final class DuckPlayerLaunchExperiment: DuckPlayerLaunchExperimentHandling {
pixel.fireDuckPlayerExperimentPixel(pixel: .duckplayerExperimentSearch, withAdditionalParameters: params)

// Fire a daily pixel
if dates.day != lastDayPixelFired {
if dates.day != lastDayPixelFiredV2 {
pixel.fireDuckPlayerExperimentPixel(pixel: .duckplayerExperimentDailySearch, withAdditionalParameters: params)
lastDayPixelFired = dates.day
lastDayPixelFiredV2 = dates.day
}

// Fire a weekly pixel
if dates.week != lastWeekPixelFired && dates.day > 0 {
if dates.week != lastWeekPixelFiredV2 && dates.day > 0 {
params.removeValue(forKey: Constants.dayKey)
params[Constants.weekKey] = "\(dates.week)"
pixel.fireDuckPlayerExperimentPixel(pixel: .duckplayerExperimentWeeklySearch, withAdditionalParameters: params)
lastWeekPixelFired = dates.week
lastWeekPixelFiredV2 = dates.week
}
}
}

func fireYoutubePixel(videoID: String) {
guard isEnrolled,
let experimentCohort = experimentCohort,
let experimentCohortV2 = experimentCohortV2,
let dates,
let formattedEnrollmentDate else {
return
}

let params = [
Constants.variantKey: experimentCohort,
Constants.variantKey: experimentCohortV2,
Constants.dayKey: "\(dates.day)",
Constants.stateKey: duckPlayerMode?.stringValue ?? "",
Constants.referrerKey: referrer?.stringValue ?? "",
Constants.enrollmentKey: formattedEnrollmentDate
]
if lastVideoIDReported != videoID {
if lastVideoIDReportedV2 != videoID {
pixel.fireDuckPlayerExperimentPixel(pixel: .duckplayerExperimentYoutubePageView, withAdditionalParameters: params)
lastVideoIDReported = videoID
lastVideoIDReportedV2 = videoID
}
}

func cleanup() {
enrollmentDate = nil
experimentCohort = nil
lastDayPixelFired = nil
lastWeekPixelFired = nil
lastVideoIDReported = nil
enrollmentDateV2 = nil
experimentCohortV2 = nil
lastDayPixelFiredV2 = nil
lastWeekPixelFiredV2 = nil
lastVideoIDReportedV2 = nil
}

func override() {
enrollmentDate = Date()
experimentCohort = "experiment"
lastDayPixelFired = nil
lastWeekPixelFired = nil
lastVideoIDReported = nil
enrollmentDateV2 = Date()
experimentCohortV2 = "experiment"
lastDayPixelFiredV2 = nil
lastWeekPixelFiredV2 = nil
lastVideoIDReportedV2 = nil

}

Expand Down
43 changes: 24 additions & 19 deletions DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ final class DuckPlayerNavigationHandler {
var featureFlagger: FeatureFlagger
var appSettings: AppSettings
var experiment: DuckPlayerLaunchExperimentHandling
private lazy var internalUserDecider = AppDependencyProvider.shared.internalUserDecider

private struct Constants {
static let SERPURL = "duckduckgo.com/"
Expand Down Expand Up @@ -216,26 +217,30 @@ final class DuckPlayerNavigationHandler {
if let navigationAction, isSERPLink(navigationAction: navigationAction) {
referrer = .serp
}


// DuckPlayer Experiment run
let experiment = DuckPlayerLaunchExperiment(duckPlayerMode: duckPlayerMode, referrer: referrer)

// Enroll user if not enrolled
if !experiment.isEnrolled {
experiment.assignUserToCohort()
}

// DuckPlayer is disabled before user enrolls,
// So trigger a settings change notification
// to let the FE know about the 'actual' setting
// and update Experiment value
if experiment.isExperimentCohort {
duckPlayer.settings.triggerNotification()
experiment.duckPlayerMode = duckPlayer.settings.mode

if featureFlagger.isFeatureOn(.duckPlayer) || internalUserDecider.isInternalUser {

// DuckPlayer Experiment run
let experiment = DuckPlayerLaunchExperiment(duckPlayerMode: duckPlayerMode,
referrer: referrer,
isInternalUser: internalUserDecider.isInternalUser)

// Enroll user if not enrolled
if !experiment.isEnrolled {
experiment.assignUserToCohort()
}

// DuckPlayer is disabled before user enrolls,
// So trigger a settings change notification
// to let the FE know about the 'actual' setting
// and update Experiment value
if experiment.isExperimentCohort {
duckPlayer.settings.triggerNotification()
experiment.duckPlayerMode = duckPlayer.settings.mode
}

experiment.fireYoutubePixel(videoID: videoID)
}

experiment.fireYoutubePixel(videoID: videoID)

}

Expand Down
8 changes: 4 additions & 4 deletions DuckDuckGoTests/DuckPlayerExperimentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ final class DuckPlayerLaunchExperimentTests: XCTestCase {
sut.assignUserToCohort()

XCTAssertTrue(sut.isEnrolled, "User should be enrolled after assigning to cohort.")
XCTAssertNotNil(sut.experimentCohort, "Experiment cohort should be assigned.")
XCTAssertNotNil(sut.enrollmentDate, "Enrollment date should be set.")
XCTAssertEqual(DuckPlayerLaunchExperiment.formattedDate(sut.enrollmentDate ?? Date()), "20240910", "The assigned date should match.")
XCTAssertNotNil(sut.experimentCohortV2, "Experiment cohort should be assigned.")
XCTAssertNotNil(sut.enrollmentDateV2, "Enrollment date should be set.")
XCTAssertEqual(DuckPlayerLaunchExperiment.formattedDate(sut.enrollmentDateV2 ?? Date()), "20240910", "The assigned date should match.")

// Check the pixel event history
let history = DuckPlayerExperimentPixelFireMock.capturedPixelEventHistory
Expand Down Expand Up @@ -142,7 +142,7 @@ final class DuckPlayerLaunchExperimentTests: XCTestCase {
sut.assignUserToCohort()
XCTAssertEqual(DuckPlayerExperimentPixelFireMock.capturedPixelEventHistory.count, 0, "Enrollment pixel should not have fired again")
XCTAssertEqual(sut.isEnrolled, true, "The assigned date should not change.")
XCTAssertEqual(DuckPlayerLaunchExperiment.formattedDate(sut.enrollmentDate ?? Date()), "20240910", "The assigned date should not change.")
XCTAssertEqual(DuckPlayerLaunchExperiment.formattedDate(sut.enrollmentDateV2 ?? Date()), "20240910", "The assigned date should not change.")
}

func testIfUserIsEnrolled_SearchDailyPixelsFire() {
Expand Down

0 comments on commit 741daa5

Please sign in to comment.