diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 2afe5960b8..c6e89c030f 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -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" } } diff --git a/DuckDuckGo/DuckPlayer/DuckPlayerLaunchExperiment.swift b/DuckDuckGo/DuckPlayer/DuckPlayerLaunchExperiment.swift index 19decaf548..8edd25ed56 100644 --- a/DuckDuckGo/DuckPlayer/DuckPlayerLaunchExperiment.swift +++ b/DuckDuckGo/DuckPlayer/DuckPlayerLaunchExperiment.swift @@ -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 @@ -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 @@ -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) } @@ -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() { @@ -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 ] @@ -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 } diff --git a/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift b/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift index 96eb8ec30f..b2316a331e 100644 --- a/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift +++ b/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift @@ -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/" @@ -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) } diff --git a/DuckDuckGoTests/DuckPlayerExperimentTests.swift b/DuckDuckGoTests/DuckPlayerExperimentTests.swift index 86b8f17817..56040b0dee 100644 --- a/DuckDuckGoTests/DuckPlayerExperimentTests.swift +++ b/DuckDuckGoTests/DuckPlayerExperimentTests.swift @@ -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 @@ -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() {