Skip to content

Commit

Permalink
Send daily pixel with open tabs count (#3587)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1206226850447395/1208014614013438/f
Tech Design URL:
CC:

**Description**:

Adds a daily pixel with number of total open tabs and number of new tabs
when Tab switcher is opened.

**Steps to test this PR**:
1. Modify `DailyPixel.hasBeenFiredToday` to always return `false`.
2. Open a bunch of tabs including a few new tabs.
3. Check Pixel is fired when opening tab switcher containing tabs count
buckets that are in line with
https://app.asana.com/0/1206226850447395/1208014614013438/f.

**Definition of Done (Internal Only)**:

* [ ] Does this PR satisfy our [Definition of
Done](https://app.asana.com/0/1202500774821704/1207634633537039/f)?

---
###### Internal references:
[Software Engineering
Expectations](https://app.asana.com/0/59792373528535/199064865822552)
[Technical Design
Template](https://app.asana.com/0/59792373528535/184709971311943)
  • Loading branch information
dus7 authored Nov 25, 2024
1 parent acc750b commit 8925890
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ extension Pixel {
case tabSwitcherClickCloseTab
case tabSwitcherSwipeCloseTab
case tabSwitchLongPressNewTab
case tabSwitcherOpenDaily

case settingsDoNotSellShown
case settingsDoNotSellOn
Expand Down Expand Up @@ -910,6 +911,7 @@ extension Pixel.Event {
case .tabSwitcherClickCloseTab: return "m_tab_manager_close_tab_click"
case .tabSwitcherSwipeCloseTab: return "m_tab_manager_close_tab_swipe"
case .tabSwitchLongPressNewTab: return "m_tab_manager_long_press_new_tab"
case .tabSwitcherOpenDaily: return "m_tab_manager_clicked_daily"

case .settingsDoNotSellShown: return "ms_dns"
case .settingsDoNotSellOn: return "ms_dns_on"
Expand Down
8 changes: 8 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@
6FEC0B882C999961006B4F6E /* FavoritesListInteractingAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FEC0B872C999961006B4F6E /* FavoritesListInteractingAdapter.swift */; };
6FF915822B88E07A0042AC87 /* AdAttributionFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF915802B88E0750042AC87 /* AdAttributionFetcherTests.swift */; };
6FF9AD452CE766F700C5A406 /* NewTabPageControllerPixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF9AD442CE766F700C5A406 /* NewTabPageControllerPixelTests.swift */; };
6FF9AD3F2CE63DD800C5A406 /* TabSwitcherOpenDailyPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF9AD3E2CE63DC200C5A406 /* TabSwitcherOpenDailyPixel.swift */; };
6FF9AD412CE6610F00C5A406 /* TabSwitcherDailyPixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF9AD402CE6610600C5A406 /* TabSwitcherDailyPixelTests.swift */; };
7B1604E82CB685B400A44EC6 /* Logger+TipKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1604E72CB685B400A44EC6 /* Logger+TipKit.swift */; };
7B1604EC2CB68BDA00A44EC6 /* TipKitController+ConvenienceInitializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1604EB2CB68BDA00A44EC6 /* TipKitController+ConvenienceInitializers.swift */; };
7B1604EE2CB68D2600A44EC6 /* TipKitDebugOptionsUIActionHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1604ED2CB68D2600A44EC6 /* TipKitDebugOptionsUIActionHandling.swift */; };
Expand Down Expand Up @@ -1678,6 +1680,8 @@
6FEC0B872C999961006B4F6E /* FavoritesListInteractingAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesListInteractingAdapter.swift; sourceTree = "<group>"; };
6FF915802B88E0750042AC87 /* AdAttributionFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdAttributionFetcherTests.swift; sourceTree = "<group>"; };
6FF9AD442CE766F700C5A406 /* NewTabPageControllerPixelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageControllerPixelTests.swift; sourceTree = "<group>"; };
6FF9AD3E2CE63DC200C5A406 /* TabSwitcherOpenDailyPixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabSwitcherOpenDailyPixel.swift; sourceTree = "<group>"; };
6FF9AD402CE6610600C5A406 /* TabSwitcherDailyPixelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabSwitcherDailyPixelTests.swift; sourceTree = "<group>"; };
7B1604E72CB685B400A44EC6 /* Logger+TipKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+TipKit.swift"; sourceTree = "<group>"; };
7B1604EB2CB68BDA00A44EC6 /* TipKitController+ConvenienceInitializers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TipKitController+ConvenienceInitializers.swift"; sourceTree = "<group>"; };
7B1604ED2CB68D2600A44EC6 /* TipKitDebugOptionsUIActionHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipKitDebugOptionsUIActionHandling.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5839,6 +5843,7 @@
F1386BA21E6846320062FC3C /* TabSwitcher */ = {
isa = PBXGroup;
children = (
6FF9AD3E2CE63DC200C5A406 /* TabSwitcherOpenDailyPixel.swift */,
85DFEDF824CF3D0E00973FE7 /* TabsBarCell.swift */,
9872D204247DCAC100CEF398 /* TabPreviewsSource.swift */,
8586A10F24CCCD040049720E /* TabsBarViewController.swift */,
Expand Down Expand Up @@ -5927,6 +5932,7 @@
F13B4BF71F18C9E800814661 /* Tabs */ = {
isa = PBXGroup;
children = (
6FF9AD402CE6610600C5A406 /* TabSwitcherDailyPixelTests.swift */,
85010503292FFB080033978F /* FireproofFaviconUpdaterTests.swift */,
8565A34C1FC8DFE400239327 /* LaunchTabNotificationTests.swift */,
984D035F24AF49160066CFB8 /* TabPreviewsSourceTests.swift */,
Expand Down Expand Up @@ -7894,6 +7900,7 @@
C13F3F682B7F88100083BE40 /* AuthConfirmationPromptView.swift in Sources */,
F1617C191E573EA800DEDCAF /* TabSwitcherDelegate.swift in Sources */,
4B5C462A2AF2A6E6002A4432 /* VPNIntents.swift in Sources */,
6FF9AD3F2CE63DD800C5A406 /* TabSwitcherOpenDailyPixel.swift in Sources */,
310742A62848CD780012660B /* BackForwardMenuHistoryItem.swift in Sources */,
9FDEC7B82C9004D600C7A692 /* OnboardingIntroViewModel+Copy.swift in Sources */,
859DB8162CE6263C001F7210 /* TextZoomEditorView.swift in Sources */,
Expand Down Expand Up @@ -8062,6 +8069,7 @@
C1B7B53428944EFA0098FD6A /* CoreDataTestUtilities.swift in Sources */,
859DB81E2CE62766001F7210 /* TextZoomTests.swift in Sources */,
1DE384E42BC41E2500871AF6 /* PixelExperimentTests.swift in Sources */,
6FF9AD412CE6610F00C5A406 /* TabSwitcherDailyPixelTests.swift in Sources */,
CBDD5DE129A6741300832877 /* MockBundle.swift in Sources */,
C158AC7B297AB5DC0008723A /* MockSecureVault.swift in Sources */,
569437342BE4E41500C0881B /* SyncErrorHandlerSyncErrorsAlertsTests.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2549,6 +2549,8 @@ extension MainViewController: TabSwitcherButtonDelegate {

func showTabSwitcher(_ button: TabSwitcherButton) {
Pixel.fire(pixel: .tabBarTabSwitcherPressed)
DailyPixel.fireDaily(.tabSwitcherOpenDaily, withAdditionalParameters: TabSwitcherOpenDailyPixel().parameters(with: tabManager.model.tabs))

performCancel()
showTabSwitcher()
}
Expand Down
68 changes: 68 additions & 0 deletions DuckDuckGo/TabSwitcherOpenDailyPixel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// TabSwitcherOpenDailyPixel.swift
// DuckDuckGo
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

struct TabSwitcherOpenDailyPixel {
func parameters(with tabs: [Tab]) -> [String: String] {
var parameters = [String: String]()

parameters[ParameterName.tabCount] = tabCountBucket(for: tabs)
parameters[ParameterName.newTabCount] = newTabCountBucket(for: tabs)

return parameters
}

private func tabCountBucket(for tabs: [Tab]) -> String? {
let count = tabs.count

switch count {
case 0: return nil
case 1...1: return "1"
case 2...5: return "2-5"
case 6...10: return "6-10"
case 11...20: return "11-20"
case 21...40: return "21-40"
case 41...60: return "41-60"
case 61...80: return "61-80"
case 81...100: return "81-100"
case 101...125: return "101-125"
case 126...150: return "126-150"
case 151...250: return "151-250"
case 251...500: return "251-500"
default: return "501+"
}

}

private func newTabCountBucket(for tabs: [Tab]) -> String? {
let count = tabs.count { $0.link == nil }

switch count {
case 0...1: return "0-1"
case 2...10: return "2-10"
default: return "11+"
}
}

private enum ParameterName {
static let tabCount = "tab_count"
static let newTabCount = "new_tab_count"
}
}
99 changes: 99 additions & 0 deletions DuckDuckGoTests/TabSwitcherDailyPixelTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// TabSwitcherDailyPixelTests.swift
// DuckDuckGo
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import XCTest
import Core
@testable import DuckDuckGo

final class TabSwitcherDailyPixelTests: XCTestCase {
func testPopulatesParameters() {
let tabs = [Tab(), Tab(), Tab()]
let pixel = TabSwitcherOpenDailyPixel()

let parameters = pixel.parameters(with: tabs)

XCTAssertNotNil(parameters[ParameterName.tabCount])
XCTAssertNotNil(parameters[ParameterName.newTabCount])
}

func testIncludesProperCountsForParameters() {
let tabs = [Tab(), Tab(), .mock()]
let pixel = TabSwitcherOpenDailyPixel()

let parameters = pixel.parameters(with: tabs)

XCTAssertEqual(parameters[ParameterName.tabCount], "2-5")
XCTAssertEqual(parameters[ParameterName.newTabCount], "2-10")
}

func testBucketsAggregation() {
let bucketValues = [
1...1: "1",
2...5: "2-5",
6...10: "6-10",
11...20: "11-20",
21...40: "21-40",
41...60: "41-60",
61...80: "61-80",
81...100: "81-100",
101...125: "101-125",
126...150: "126-150",
151...250: "151-250",
251...500: "251-500",
501...504: "501+"]

for bucket in bucketValues {
for value in bucket.key {
let tabs = Array(repeating: Tab.mock(), count: value)

let countParameter = TabSwitcherOpenDailyPixel().parameters(with: tabs)[ParameterName.tabCount]

XCTAssertEqual(countParameter, bucket.value)
}
}
}

func testNewTabBucketsAggregation() {
let bucketValues = [
0...1: "0-1",
2...10: "2-10",
11...20: "11+"]

for bucket in bucketValues {
for value in bucket.key {
let tabs = Array(repeating: Tab(), count: value)

let countParameter = TabSwitcherOpenDailyPixel().parameters(with: tabs)[ParameterName.newTabCount]

XCTAssertEqual(countParameter, bucket.value)
}
}
}
}

private extension Tab {
static func mock() -> Tab {
Tab(link: Link(title: nil, url: URL("https://example.com")!))
}
}

private enum ParameterName {
static let newTabCount = "new_tab_count"
static let tabCount = "tab_count"
}

0 comments on commit 8925890

Please sign in to comment.