Skip to content

Commit

Permalink
Refactor logic to support defining App variants in tests and add onbo…
Browse files Browse the repository at this point in the history
…arding tests
  • Loading branch information
alessandroboron committed Aug 13, 2024
1 parent 2f38f75 commit 5e6b0bd
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 26 deletions.
29 changes: 29 additions & 0 deletions .maestro/onboarding_tests/01_control_group_onboarding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
appId: com.duckduckgo.mobile.ios
tags:
- onboarding

---

# Set up
- runFlow:
file: ../shared/setup.yaml
env:
ONBOARDING_COMPLETED: "false"
ONBOARDING_VARIANT: "ma"

# Load Site
- assertVisible:
id: "searchEntry"
- tapOn:
id: "searchEntry"
- inputText: "https://www.duckduckgo.com"
- pressKey: Enter

# Handle Onboarding
- assertVisible: "Got It"
- assertVisible: "Hide"
- tapOn: "Got It"
- assertVisible: "Close Tabs and Clear Data"
- tapOn: "Close Tabs and Clear Data"
- tapOn: "Close Tabs and Clear Data"
- assertVisible: "You’ve got this!\n\nRemember: Every time you browse with me, a creepy ad loses its wings. 👍"
33 changes: 33 additions & 0 deletions .maestro/onboarding_tests/02_control_group_hide_onboarding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
appId: com.duckduckgo.mobile.ios
tags:
- onboarding

---

# Set up
- runFlow:
file: ../shared/setup.yaml
env:
ONBOARDING_COMPLETED: "false"
ONBOARDING_VARIANT: "ma"

# Load Site
- assertVisible:
id: "searchEntry"
- tapOn:
id: "searchEntry"
- inputText: "https://www.duckduckgo.com"
- pressKey: Enter

# Handle Onboarding
- assertVisible: "Got It"
- assertVisible: "Hide"
- tapOn: "Hide"
- assertVisible: "Hide Tips Forever"
- tapOn: "Hide Tips Forever"

# Handle Fire Button
- assertVisible: "Close Tabs and Clear Data"
- tapOn: "Close Tabs and Clear Data"
- tapOn: "Close Tabs and Clear Data"
- assertNotVisible: "You’ve got this!\n\nRemember: Every time you browse with me, a creepy ad loses its wings. 👍"
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
appId: com.duckduckgo.mobile.ios
tags:
- onboarding

---

# Set up
- runFlow:
file: ../shared/setup.yaml
env:
ONBOARDING_COMPLETED: "false"
ONBOARDING_VARIANT: "mb"

# Handle Search Suggestions
- assertVisible: "Ready to get started?\nTry a search!"
- assertVisible: "Surprise Me!"
- tapOn: "Surprise Me!"

# Handle First Dax Dialog
- assertVisible: "That’s DuckDuckGo Search. Private. Fast. Fewer ads."
- assertVisible: "Got It!"
- tapOn: "Got It!"

# Handle Site Suggestions
- assertVisible: "Next, try visiting a site!"
- assertVisible: "Surprise Me!"
- tapOn: "Surprise Me!"

# Handle Privacy Dashboard
- assertVisible: "Got It!"
- tapOn:
point: "6%,10%" # Shield icon.
- assertVisible:
text: "View Tracker Companies"
- assertVisible:
text: "Done"
- tapOn: "Done"

# Handle Fire Message
- assertVisible: "Got It!"
- tapOn: "Got It!"
- assertVisible: "Instantly clear your browsing activity with the Fire Button.\n\nGive it a try! 🔥"

# Handle Fire Button
- assertVisible: "Close Tabs and Clear Data"
- tapOn: "Close Tabs and Clear Data"
- tapOn: "Close Tabs and Clear Data"

# Handle End of Journey Dialog
- assertVisible: "You’ve got this!"
- assertVisible: "High five!"
- tapOn: "High five!"
6 changes: 4 additions & 2 deletions .maestro/shared/setup.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ appId: com.duckduckgo.mobile.ios
clearState: true
clearKeychain: true
arguments:
isUITesting: true
isOnboardingCompleted: ${ONBOARDING_COMPLETED}
isUITesting: true # Renaming `isUITesting` requires to update LaunchOptionsHandler `isUITesting` key
isOnboardingCompleted: ${ONBOARDING_COMPLETED} # Renaming `isOnboardingCompleted` requires to update LaunchOptionsHandler `isOnboardingCompleted` key
onboardingVariant: ${ONBOARDING_VARIANT} # Renaming `onboardingVariant` requires to update LaunchOptionsHandler `onboardingVariant` key
currentAppVariant: ${APP_VARIANT} # Renaming `currentAppVariant` requires to update LaunchOptionsHandler `currentAppVariant` key

# Get past onboarding screens
- runFlow:
Expand Down
25 changes: 21 additions & 4 deletions Core/DefaultVariantManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,35 +81,48 @@ public protocol VariantRNG {

}

public protocol VariantNameOverriding {
var overriddenAppVariantName: String? { get }
var overriddenOnboardingVariantName: String? { get }
}

public class DefaultVariantManager: VariantManager {

public var currentVariant: Variant? {
let variantName = ProcessInfo.processInfo.environment["VARIANT", default: storage.variant ?? "" ]
let variantName = variantNameOverride.overriddenAppVariantName
?? ProcessInfo.processInfo.environment["VARIANT"]
?? storage.variant
?? ""

return variants.first(where: { $0.name == variantName })
}

private let variants: [Variant]
private let storage: StatisticsStore
private let rng: VariantRNG
private let returningUserMeasurement: ReturnUserMeasurement
private let variantNameOverride: VariantNameOverriding

init(variants: [Variant],
storage: StatisticsStore,
rng: VariantRNG,
returningUserMeasurement: ReturnUserMeasurement) {

returningUserMeasurement: ReturnUserMeasurement,
variantNameOverride: VariantNameOverriding
) {
self.variants = variants
self.storage = storage
self.rng = rng
self.returningUserMeasurement = returningUserMeasurement
self.variantNameOverride = variantNameOverride
}

public convenience init() {
self.init(
variants: VariantIOS.defaultVariants,
storage: StatisticsUserDefaults(),
rng: Arc4RandomUniformVariantRNG(),
returningUserMeasurement: KeychainReturnUserMeasurement()
returningUserMeasurement: KeychainReturnUserMeasurement(),
variantNameOverride: LaunchOptionsHandler()
)
}

Expand Down Expand Up @@ -141,6 +154,10 @@ public class DefaultVariantManager: VariantManager {
}

private func selectVariant() -> Variant? {
if let overriddenOnboardingVariant = variantNameOverride.overriddenOnboardingVariantName {
return variants.first(where: { $0.name == overriddenOnboardingVariant })
}

if returningUserMeasurement.isReturningUser {
return VariantIOS.returningUser
}
Expand Down
10 changes: 5 additions & 5 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,8 @@
9FB027192C26BC29009EA190 /* BrowsersComparisonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB027182C26BC29009EA190 /* BrowsersComparisonModel.swift */; };
9FB0271B2C2927D0009EA190 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB0271A2C2927D0009EA190 /* OnboardingView.swift */; };
9FB0271D2C293619009EA190 /* OnboardingIntroViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB0271C2C293619009EA190 /* OnboardingIntroViewModel.swift */; };
9FCFCD7E2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */; };
9FCFCD802C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD7F2C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift */; };
9FCFCD812C6B020D006EB7A0 /* LaunchOptionsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */; };
9FE05CEE2C36424E00D9046B /* OnboardingPixelReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE05CED2C36424E00D9046B /* OnboardingPixelReporter.swift */; };
9FE05CF12C36468A00D9046B /* OnboardingPixelReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE05CEF2C3642F900D9046B /* OnboardingPixelReporterTests.swift */; };
9FE08BD32C2A5B88001D5EBC /* OnboardingTextStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE08BD22C2A5B88001D5EBC /* OnboardingTextStyles.swift */; };
Expand Down Expand Up @@ -2433,7 +2433,7 @@
9FB027182C26BC29009EA190 /* BrowsersComparisonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowsersComparisonModel.swift; sourceTree = "<group>"; };
9FB0271A2C2927D0009EA190 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
9FB0271C2C293619009EA190 /* OnboardingIntroViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingIntroViewModel.swift; sourceTree = "<group>"; };
9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchOptionsHandler.swift; sourceTree = "<group>"; };
9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LaunchOptionsHandler.swift; path = ../DuckDuckGo/LaunchOptionsHandler.swift; sourceTree = "<group>"; };
9FCFCD7F2C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchOptionsHandlerTests.swift; sourceTree = "<group>"; };
9FE05CED2C36424E00D9046B /* OnboardingPixelReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPixelReporter.swift; sourceTree = "<group>"; };
9FE05CEF2C3642F900D9046B /* OnboardingPixelReporterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPixelReporterTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5537,6 +5537,7 @@
F143C3191E4A99DD00CFDE3A /* Utilities */ = {
isa = PBXGroup;
children = (
9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */,
B603974829C19F6F00902A34 /* Assertions.swift */,
CBAA195927BFE15600A4BD49 /* NSManagedObjectContextExtension.swift */,
4BE27566272F878F006B20B0 /* URLRequestExtension.swift */,
Expand Down Expand Up @@ -5621,7 +5622,6 @@
4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */,
85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */,
987243132C5232B5007ECC76 /* BookmarksDatabaseSetupTests.swift */,
9FCFCD7F2C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift */,
);
name = Application;
sourceTree = "<group>";
Expand Down Expand Up @@ -5701,6 +5701,7 @@
F198D78F1E3976300088DA8A /* Utilities */ = {
isa = PBXGroup;
children = (
9FCFCD7F2C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift */,
F198D78D1E39762C0088DA8A /* StringExtensionTests.swift */,
F14E491E1E391CE900DC037C /* URLExtensionTests.swift */,
F1DA2F7C1EBCF23700313F51 /* ExternalUrlSchemeTests.swift */,
Expand Down Expand Up @@ -5834,7 +5835,6 @@
85C8E61C2B0E47380029A6BD /* BookmarksDatabaseSetup.swift */,
9821234D2B6D0A6300F08C57 /* UserAuthenticator.swift */,
9821234F2B6D233E00F08C57 /* UserSession.swift */,
9FCFCD7D2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift */,
);
name = Application;
sourceTree = "<group>";
Expand Down Expand Up @@ -6969,7 +6969,6 @@
6FD1BAE42B87A107000C475C /* AdAttributionPixelReporter.swift in Sources */,
1E8AD1D927C4FEC100ABA377 /* DownloadsListSectioningHelper.swift in Sources */,
D60170BD2BA34CE8001911B5 /* Subscription.swift in Sources */,
9FCFCD7E2C6AF52A006EB7A0 /* LaunchOptionsHandler.swift in Sources */,
1E4DCF4827B6A35400961E25 /* DownloadsListModel.swift in Sources */,
C12726F02A5FF89900215B02 /* EmailSignupPromptViewModel.swift in Sources */,
31669B9A28020A460071CC18 /* SaveLoginViewModel.swift in Sources */,
Expand Down Expand Up @@ -7867,6 +7866,7 @@
F1134EA61F3E2AF400B73467 /* StatisticsStore.swift in Sources */,
F17D723C1E8BB374003E8B0E /* AppDeepLinkSchemes.swift in Sources */,
1E8AD1DB27C51AE000ABA377 /* TimeIntervalExtension.swift in Sources */,
9FCFCD812C6B020D006EB7A0 /* LaunchOptionsHandler.swift in Sources */,
B652DF0D287C2A6300C12A9C /* PrivacyFeatures.swift in Sources */,
F10E522D1E946F8800CE1253 /* NSAttributedStringExtension.swift in Sources */,
9887DC252354D2AA005C85F5 /* Database.swift in Sources */,
Expand Down
39 changes: 35 additions & 4 deletions DuckDuckGo/LaunchOptionsHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,54 @@

import Foundation

final class LaunchOptionsHandler {
public final class LaunchOptionsHandler {
private static let isUITesting = "isUITesting"
private static let isOnboardingcompleted = "isOnboardingCompleted"
private static let onboardingVariantName = "onboardingVariant"
private static let appVariantName = "currentAppVariant"

private let launchArguments: [String]
private let userDefaults: UserDefaults

init(launchArguments: [String] = ProcessInfo.processInfo.arguments, userDefaults: UserDefaults = .app) {
public init(launchArguments: [String] = ProcessInfo.processInfo.arguments, userDefaults: UserDefaults = .app) {
self.launchArguments = launchArguments
self.userDefaults = userDefaults
}

var isUITesting: Bool {
public var isUITesting: Bool {
launchArguments.contains(Self.isUITesting)
}

var isOnboardingCompleted: Bool {
public var isOnboardingCompleted: Bool {
userDefaults.string(forKey: Self.isOnboardingcompleted) == "true"
}

public var onboardingVariantName: String? {
sanitisedEnvParameter(string: userDefaults.string(forKey: Self.onboardingVariantName))
}

public var appVariantName: String? {
sanitisedEnvParameter(string: userDefaults.string(forKey: Self.appVariantName))
}

private func sanitisedEnvParameter(string: String?) -> String? {
guard let string, string != "null" else { return nil }
return string
}
}

// MARK: - LaunchOptionsHandler + VariantManager

extension LaunchOptionsHandler: VariantNameOverriding {

public var overriddenAppVariantName: String? {
guard isUITesting else { return nil }
return appVariantName
}

public var overriddenOnboardingVariantName: String? {
guard isUITesting else { return nil }
return onboardingVariantName
}

}
Loading

0 comments on commit 5e6b0bd

Please sign in to comment.