0

welcome_tour: Advancing to V3

We will have small changes to the V2:
1. Use V1 intro dialogue copy
2. Keep the V2 Files step
3. Remove the Quick Settings step
4. Update copy in the Explore step to remove mention of 'apps'

Bug: 383375717
Test: Manual tested and updated tests
Change-Id: Icd68b07d0b7759ce25b959a4f00c6944924e32d5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6091397
Commit-Queue: Tao Wu <wutao@chromium.org>
Reviewed-by: Li Lin <llin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1395718}
This commit is contained in:
Tao Wu
2024-12-12 17:47:28 -08:00
committed by Chromium LUCI CQ
parent 324947176e
commit ebd6bab600
17 changed files with 227 additions and 204 deletions

@ -8212,17 +8212,11 @@ To shut down the device, press and hold the power button on the device again.
<message name="IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT" desc="Text shown in the body of the dialog as part of the System UI Welcome Tour.">
Take a quick tour to learn how to get around your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph>. Get up and running in 5 steps.
</message>
<message name="IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT_V2" desc="Text shown in the body of the dialog as part of the System UI Welcome Tour V2.">
Take a quick tour to learn how to get around your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph>
</message>
<message name="IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT" desc="Text shown as the Welcome Tour dialog title.">
Hi there. <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph> is a little different.
</message>
<message name="IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT_V2" desc="Text shown as the Welcome Tour V2 dialog title.">
6 tips to get started on <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph>
</message>
<message name="IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT" desc="Text shown in the body of a help bubble anchored to the Explore app as part of the System UI Welcome Tour.">
Those were the basics! Continue in Explore, our built-in app for tips and help. Youll find tips for getting started, recommended apps, special offers, and the newest <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph> features.
Those were the basics! Continue in Explore, our built-in app for tips and help. Youll find tips for getting started, special offers, and the newest <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph> features.
</message>
<message name="IDS_ASH_WELCOME_TOUR_FILES_APP_BUBBLE_ACCNAME" desc="The accessible name of a help bubble anchored to the Files app as part of the System UI Welcome Tour.">
Tour step <ph name="STEP">$1<ex>1</ex></ph> of <ph name="TOTAL_STEPS">$2<ex>5</ex></ph>. You can find the Files app in Launcher. Manage your local files and your Google Drive files, all in the Files app.

@ -1 +0,0 @@
51a3652b0cb8ef770f15462f0519ea031f18d413

@ -1 +0,0 @@
51a3652b0cb8ef770f15462f0519ea031f18d413

@ -1 +1 @@
3e66510f6e69fdb7d266853f790053f92bb45bf3
436236cc943f42618bc1d415957f6c71a4918893

@ -3115,10 +3115,10 @@ BASE_FEATURE(kWelcomeTourHoldbackArm,
"WelcomeTourHoldbackArm",
base::FEATURE_DISABLED_BY_DEFAULT);
// Enables the Welcome Tour V2 that has different strings and steps than V1.
// Enables the Welcome Tour V3 that has different strings and steps than V1.
// Enabling this flag has no effect unless `kWelcomeTour` is also enabled.
BASE_FEATURE(kWelcomeTourV2,
"WelcomeTourV2",
BASE_FEATURE(kWelcomeTourV3,
"WelcomeTourV3",
base::FEATURE_DISABLED_BY_DEFAULT);
// Controls whether to enable MAC Address Randomization on WiFi connection.
@ -4677,8 +4677,8 @@ bool IsWelcomeTourHoldbackEnabled() {
base::FeatureList::IsEnabled(kWelcomeTourHoldbackArm);
}
bool IsWelcomeTourV2Enabled() {
return IsWelcomeTourEnabled() && base::FeatureList::IsEnabled(kWelcomeTourV2);
bool IsWelcomeTourV3Enabled() {
return IsWelcomeTourEnabled() && base::FeatureList::IsEnabled(kWelcomeTourV3);
}
bool IsWifiConcurrencyEnabled() {

@ -993,7 +993,7 @@ BASE_DECLARE_FEATURE(kWelcomeTourForceUserEligibility);
COMPONENT_EXPORT(ASH_CONSTANTS)
BASE_DECLARE_FEATURE(kWelcomeTourHoldbackArm);
COMPONENT_EXPORT(ASH_CONSTANTS)
BASE_DECLARE_FEATURE(kWelcomeTourV2);
BASE_DECLARE_FEATURE(kWelcomeTourV3);
COMPONENT_EXPORT(ASH_CONSTANTS)
BASE_DECLARE_FEATURE(kWifiConnectMacAddressRandomization);
COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kWifiConcurrency);
@ -1413,7 +1413,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourCounterfactuallyEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourForceUserEligibilityEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourHoldbackEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourV2Enabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourV3Enabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWifiConcurrencyEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWifiDirectEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWifiSyncAndroidEnabled();

@ -66,9 +66,8 @@ namespace {
// The singleton instance owned by the `UserEducationController`.
WelcomeTourController* g_instance = nullptr;
// Strings.
constexpr char16_t kTotalStepsV1[] = u"5";
constexpr char16_t kTotalStepsV2[] = u"6";
// Strings. V3 adds a Files app step, but removes the status area step.
constexpr char16_t kTotalSteps[] = u"5";
// Helpers ---------------------------------------------------------------------
@ -184,11 +183,29 @@ ui::ElementContext WelcomeTourController::GetInitialElementContext() const {
GetMatchingViewInPrimaryRootWindow(kShelfViewElementId));
}
// There are some difference between V1 and V3. Here are the steps in summary.
// V1:
// 0. Optin dialog
// 1. Shelf
// 2. Status area
// 3. Home button
// 4. Search box
// 5. Settings app
// 6. Explore app
// 7. Explore app window
//
// V3:
// 0. Optin dialog
// 1. Shelf
// 2. Home button
// 3. Search box
// 4. Files app
// 5. Settings app
// 6. Explore app
// 7. Explore app window
user_education::TutorialDescription
WelcomeTourController::GetTutorialDescription() const {
const std::u16string product_name = ui::GetChromeOSDeviceName();
const std::u16string total_steps =
features::IsWelcomeTourV2Enabled() ? kTotalStepsV2 : kTotalStepsV1;
int current_step = 1;
user_education::TutorialDescription tutorial_description;
@ -219,59 +236,70 @@ WelcomeTourController::GetTutorialDescription() const {
/*accessible_name=*/
l10n_util::GetStringFUTF8(
IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps),
base::NumberToString16(current_step++), kTotalSteps),
/*body_text=*/
l10n_util::GetStringUTF8(
IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_BODY_TEXT)))
.AddCustomNextButton(DefaultNextButtonCallback().Then(
base::BindRepeating(&WelcomeTourController::SetCurrentStep,
weak_ptr_factory_.GetMutableWeakPtr(),
welcome_tour_metrics::Step::kStatusArea))));
.AddCustomNextButton(
DefaultNextButtonCallback().Then(base::BindRepeating(
&WelcomeTourController::SetCurrentStep,
weak_ptr_factory_.GetMutableWeakPtr(),
features::IsWelcomeTourV3Enabled()
? welcome_tour_metrics::Step::kHomeButton
: welcome_tour_metrics::Step::kStatusArea))));
// Wait for "Next" button click before proceeding to the next bubble step.
// NOTE: This event step also ensures that the next bubble step will show on
// the primary display by naming the primary root window's status area.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::EventStep(
user_education::kHelpBubbleNextButtonClickedEvent,
kShelfViewElementId)
.NameElements(NameMatchingElementInPrimaryRootWindowCallback(
kUnifiedSystemTrayElementId, kUnifiedSystemTrayElementName))
.InSameContext());
if (!features::IsWelcomeTourV3Enabled()) {
// Wait for "Next" button click before proceeding to the next bubble step.
// NOTE: This event step also ensures that the next bubble step will show on
// the primary display by naming the primary root window's status area.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::EventStep(
user_education::kHelpBubbleNextButtonClickedEvent,
kShelfViewElementId)
.NameElements(NameMatchingElementInPrimaryRootWindowCallback(
kUnifiedSystemTrayElementId, kUnifiedSystemTrayElementName))
.InSameContext());
// Step 2: Status area.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::BubbleStep(
kUnifiedSystemTrayElementName)
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomRight)
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT)
.SetExtendedProperties(CreateHelpBubbleExtendedProperties(
HelpBubbleId::kWelcomeTourStatusArea,
/*accessible_name=*/
l10n_util::GetStringFUTF8(
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps),
/*body_text=*/
l10n_util::GetStringUTF8(
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT)))
.AddCustomNextButton(DefaultNextButtonCallback().Then(
base::BindRepeating(&WelcomeTourController::SetCurrentStep,
weak_ptr_factory_.GetMutableWeakPtr(),
welcome_tour_metrics::Step::kHomeButton)))
.InAnyContext());
// Step 2 in V1: Status area.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::BubbleStep(
kUnifiedSystemTrayElementName)
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomRight)
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT)
.SetExtendedProperties(CreateHelpBubbleExtendedProperties(
HelpBubbleId::kWelcomeTourStatusArea,
/*accessible_name=*/
l10n_util::GetStringFUTF8(
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), kTotalSteps),
/*body_text=*/
l10n_util::GetStringUTF8(
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT)))
.AddCustomNextButton(DefaultNextButtonCallback().Then(
base::BindRepeating(&WelcomeTourController::SetCurrentStep,
weak_ptr_factory_.GetMutableWeakPtr(),
welcome_tour_metrics::Step::kHomeButton)))
.InAnyContext());
}
// Wait for "Next" button click before proceeding to the next bubble step.
// NOTE: This event step also ensures that the next bubble step will show on
// the primary display by naming the primary root window's home button.
std::variant<ui::ElementIdentifier, std::string> current_element;
if (features::IsWelcomeTourV3Enabled()) {
current_element = kShelfViewElementId;
} else {
current_element = kUnifiedSystemTrayElementName;
}
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::EventStep(
user_education::kHelpBubbleNextButtonClickedEvent,
kUnifiedSystemTrayElementName)
user_education::kHelpBubbleNextButtonClickedEvent, current_element)
.NameElements(NameMatchingElementInPrimaryRootWindowCallback(
kHomeButtonElementId, kHomeButtonElementName))
.InSameContext());
// Step 3: Home button.
// Step 3 in V1 and step 2 in V3: Home button.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::BubbleStep(kHomeButtonElementName)
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomLeft)
@ -281,7 +309,7 @@ WelcomeTourController::GetTutorialDescription() const {
/*accessible_name=*/
l10n_util::GetStringFUTF8(
IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps,
base::NumberToString16(current_step++), kTotalSteps,
product_name),
/*body_text=*/
l10n_util::GetStringFUTF8(
@ -303,7 +331,7 @@ WelcomeTourController::GetTutorialDescription() const {
welcome_tour_metrics::Step::kSearch)))
.InAnyContext());
// Step 4: Search box.
// Step 4 in V1 and step 3 in V3: Search box.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::BubbleStep(kSearchBoxViewElementId)
.SetBubbleArrow(user_education::HelpBubbleArrow::kTopCenter)
@ -313,7 +341,7 @@ WelcomeTourController::GetTutorialDescription() const {
/*accessible_name=*/
l10n_util::GetStringFUTF8(
IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps,
base::NumberToString16(current_step++), kTotalSteps,
product_name),
/*body_text=*/
l10n_util::GetStringFUTF8(
@ -323,7 +351,7 @@ WelcomeTourController::GetTutorialDescription() const {
DefaultNextButtonCallback().Then(base::BindRepeating(
&WelcomeTourController::SetCurrentStep,
weak_ptr_factory_.GetMutableWeakPtr(),
features::IsWelcomeTourV2Enabled()
features::IsWelcomeTourV3Enabled()
? welcome_tour_metrics::Step::kFilesApp
: welcome_tour_metrics::Step::kSettingsApp)))
.InAnyContext());
@ -335,8 +363,8 @@ WelcomeTourController::GetTutorialDescription() const {
kSearchBoxViewElementId)
.InSameContext());
if (features::IsWelcomeTourV2Enabled()) {
// Step 5 in V2: Files app.
if (features::IsWelcomeTourV3Enabled()) {
// Step 4 in V3: Files app.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::BubbleStep(kFilesAppElementId)
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomLeft)
@ -346,7 +374,7 @@ WelcomeTourController::GetTutorialDescription() const {
/*accessible_name=*/
l10n_util::GetStringFUTF8(
IDS_ASH_WELCOME_TOUR_FILES_APP_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps),
base::NumberToString16(current_step++), kTotalSteps),
/*body_text=*/
l10n_util::GetStringUTF8(
IDS_ASH_WELCOME_TOUR_FILES_APP_BUBBLE_BODY_TEXT)))
@ -364,7 +392,7 @@ WelcomeTourController::GetTutorialDescription() const {
.InSameContext());
}
// Step 5 in V1 and step 6 in V2: Settings app.
// Step 5: Settings app.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::BubbleStep(kSettingsAppElementId)
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomLeft)
@ -374,7 +402,7 @@ WelcomeTourController::GetTutorialDescription() const {
/*accessible_name=*/
l10n_util::GetStringFUTF8(
IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps,
base::NumberToString16(current_step++), kTotalSteps,
product_name),
/*body_text=*/
l10n_util::GetStringFUTF8(
@ -393,7 +421,7 @@ WelcomeTourController::GetTutorialDescription() const {
kSettingsAppElementId)
.InSameContext());
// Step 6 in V1 and step 7 in V2: Explore app.
// Step 6: Explore app.
// NOTE: The accessible name is the same as the body text.
tutorial_description.steps.emplace_back(
user_education::TutorialDescription::BubbleStep(kExploreAppElementId)
@ -406,7 +434,7 @@ WelcomeTourController::GetTutorialDescription() const {
product_name)))
.InSameContext());
// Step 7 in V1 and step 8 in V2: Explore app window.
// Step 7: Explore app window.
// Implemented in `WelcomeTourController::OnWelcomeTourEnded()`.
return tutorial_description;

@ -102,8 +102,7 @@ using ContextMode = TutorialDescription::ContextMode;
using ElementSpecifier = TutorialDescription::ElementSpecifier;
// Strings.
constexpr char16_t kTotalStepsV1[] = u"5";
constexpr char16_t kTotalStepsV2[] = u"6";
constexpr char16_t kTotalSteps[] = u"5";
// Actions ---------------------------------------------------------------------
@ -570,31 +569,31 @@ TEST_F(WelcomeTourControllerTest, AbortsTourAndPropagatesEvents) {
EXPECT_TRUE(ended_future.Wait());
}
// WelcomeTourControllerV2Test -------------------------------------------------
// WelcomeTourControllerV3Test -------------------------------------------------
// Base class for tests of the `WelcomeTourController` which are concerned with
// the behavior of WelcomeTourV2 experiment arms, parameterized by whether the
// Welcome Tour V2 feature is enabled.
class WelcomeTourControllerV2Test
// the behavior of WelcomeTourV3 experiment arms, parameterized by whether the
// Welcome Tour V3 feature is enabled.
class WelcomeTourControllerV3Test
: public WelcomeTourControllerTest,
public ::testing::WithParamInterface<std::tuple<
/*is_welcome_tour_v2_enabled=*/bool,
/*is_welcome_tour_v3_enabled=*/bool,
/*is_welcome_tour_counterfactually_enabled=*/bool>> {
public:
WelcomeTourControllerV2Test() {
WelcomeTourControllerV3Test() {
// Only one of those features can be enabled at a time.
scoped_feature_list_.InitWithFeatureStates(
{{features::kWelcomeTourV2,
IsWelcomeTourV2Enabled() && !IsWelcomeTourCounterfactuallyEnabled()},
{{features::kWelcomeTourV3,
IsWelcomeTourV3Enabled() && !IsWelcomeTourCounterfactuallyEnabled()},
{features::kWelcomeTourCounterfactualArm,
IsWelcomeTourCounterfactuallyEnabled()},
{features::kWelcomeTourHoldbackArm, false}});
}
protected:
// Returns whether the WelcomeTourV2 feature is enabled given test
// Returns whether the WelcomeTourV3 feature is enabled given test
// parameterization.
bool IsWelcomeTourV2Enabled() const { return std::get<0>(GetParam()); }
bool IsWelcomeTourV3Enabled() const { return std::get<0>(GetParam()); }
// Returns whether the WelcomeTour feature is counterfactually enabled given
// test parameterization.
@ -608,19 +607,17 @@ class WelcomeTourControllerV2Test
INSTANTIATE_TEST_SUITE_P(
All,
WelcomeTourControllerV2Test,
WelcomeTourControllerV3Test,
testing::Combine(
/*is_welcome_tour_v2_enabled=*/testing::Bool(),
/*is_welcome_tour_v3_enabled=*/testing::Bool(),
/*is_welcome_tour_counterfactually_enabled=*/testing::Bool()));
// Verifies that `GetTutorialDescription()` returns expected values.
TEST_P(WelcomeTourControllerV2Test, GetTutorialDescription) {
TEST_P(WelcomeTourControllerV3Test, GetTutorialDescription) {
auto* welcome_tour_controller = WelcomeTourController::Get();
ASSERT_TRUE(welcome_tour_controller);
const std::u16string product_name = ui::GetChromeOSDeviceName();
const std::u16string total_steps =
features::IsWelcomeTourV2Enabled() ? kTotalStepsV2 : kTotalStepsV1;
int current_step = 1;
using ::testing::Matcher;
@ -634,63 +631,73 @@ TEST_P(WelcomeTourControllerV2Test, GetTutorialDescription) {
ElementSpecifier(kShelfViewElementId), ContextMode::kInitial,
HelpBubbleId::kWelcomeTourShelf,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps),
base::NumberToString16(current_step++), kTotalSteps),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringUTF8Eq(IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_BODY_TEXT),
HelpBubbleArrow::kBottomCenter,
/*has_next_button=*/true),
EventStep(ElementSpecifier(kShelfViewElementId),
ContextMode::kFromPreviousStep,
/*has_name_elements_callback=*/true),
BubbleStep(
ElementSpecifier(kUnifiedSystemTrayElementName), ContextMode::kAny,
HelpBubbleId::kWelcomeTourStatusArea,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringUTF8Eq(IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT),
HelpBubbleArrow::kBottomRight,
/*has_next_button=*/true),
EventStep(ElementSpecifier(kUnifiedSystemTrayElementName),
ContextMode::kFromPreviousStep,
/*has_name_elements_callback=*/true),
BubbleStep(
ElementSpecifier(kHomeButtonElementName), ContextMode::kAny,
HelpBubbleId::kWelcomeTourHomeButton,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps,
product_name),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringFUTF8Eq(
(chromeos::GetDeviceType() == chromeos::DeviceType::kChromebook)
? IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK
: IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES,
product_name),
HelpBubbleArrow::kBottomLeft,
/*has_next_button=*/true),
BubbleStep(ElementSpecifier(kSearchBoxViewElementId), ContextMode::kAny,
HelpBubbleId::kWelcomeTourSearchBox,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_ACCNAME,
base::NumberToString16(current_step++),
total_steps, product_name),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT,
product_name),
HelpBubbleArrow::kTopCenter,
/*has_next_button=*/true),
EventStep(ElementSpecifier(kSearchBoxViewElementId),
ContextMode::kFromPreviousStep,
/*has_name_elements_callback=*/false)};
/*has_name_elements_callback=*/true)};
if (features::IsWelcomeTourV2Enabled()) {
if (!features::IsWelcomeTourV3Enabled()) {
// No status area step for V3.
expected_steps.insert(
expected_steps.end(),
{// Files app step for V2.
{BubbleStep(
ElementSpecifier(kUnifiedSystemTrayElementName), ContextMode::kAny,
HelpBubbleId::kWelcomeTourStatusArea,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), kTotalSteps),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringUTF8Eq(IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT),
HelpBubbleArrow::kBottomRight,
/*has_next_button=*/true),
EventStep(ElementSpecifier(kUnifiedSystemTrayElementName),
ContextMode::kFromPreviousStep,
/*has_name_elements_callback=*/true)});
}
expected_steps.insert(
expected_steps.end(),
{BubbleStep(
ElementSpecifier(kHomeButtonElementName), ContextMode::kAny,
HelpBubbleId::kWelcomeTourHomeButton,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), kTotalSteps,
product_name),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringFUTF8Eq(
(chromeos::GetDeviceType() == chromeos::DeviceType::kChromebook)
? IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK
: IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES,
product_name),
HelpBubbleArrow::kBottomLeft,
/*has_next_button=*/true),
BubbleStep(
ElementSpecifier(kSearchBoxViewElementId), ContextMode::kAny,
HelpBubbleId::kWelcomeTourSearchBox,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), kTotalSteps,
product_name),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT,
product_name),
HelpBubbleArrow::kTopCenter,
/*has_next_button=*/true),
EventStep(ElementSpecifier(kSearchBoxViewElementId),
ContextMode::kFromPreviousStep,
/*has_name_elements_callback=*/false)});
if (features::IsWelcomeTourV3Enabled()) {
expected_steps.insert(
expected_steps.end(),
{// Files app step for V3.
BubbleStep(
ElementSpecifier(kFilesAppElementId),
ContextMode::kFromPreviousStep, HelpBubbleId::kWelcomeTourFilesApp,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_FILES_APP_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps),
base::NumberToString16(current_step++), kTotalSteps),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringUTF8Eq(IDS_ASH_WELCOME_TOUR_FILES_APP_BUBBLE_BODY_TEXT),
HelpBubbleArrow::kBottomLeft,
@ -707,7 +714,7 @@ TEST_P(WelcomeTourControllerV2Test, GetTutorialDescription) {
ContextMode::kFromPreviousStep,
HelpBubbleId::kWelcomeTourSettingsApp,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_ACCNAME,
base::NumberToString16(current_step++), total_steps,
base::NumberToString16(current_step++), kTotalSteps,
product_name),
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
StringFUTF8Eq(IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT,
@ -878,7 +885,7 @@ class WelcomeTourControllerHoldbackTest
if (const auto& is_holdback = IsHoldback()) {
scoped_feature_list_.InitWithFeatureStates(
{{features::kWelcomeTourHoldbackArm, is_holdback.value()},
{features::kWelcomeTourV2, false},
{features::kWelcomeTourV3, false},
{features::kWelcomeTourCounterfactualArm, false}});
}
}

@ -71,7 +71,6 @@ WelcomeTourDialog::WelcomeTourDialog(base::OnceClosure accept_callback,
g_instance = this;
const std::u16string product_name = ui::GetChromeOSDeviceName();
bool is_welcome_tour_v2_enabled = features::IsWelcomeTourV2Enabled();
views::Builder<SystemDialogDelegateView>(this)
.SetAcceptButtonText(l10n_util::GetStringUTF16(
@ -82,16 +81,11 @@ WelcomeTourDialog::WelcomeTourDialog(base::OnceClosure accept_callback,
.SetCancelCallback(std::move(cancel_callback))
.SetCloseCallback(std::move(close_callback))
.SetDescription(l10n_util::GetStringFUTF16(
is_welcome_tour_v2_enabled
? IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT_V2
: IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT,
product_name))
IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT, product_name))
.SetModalType(ui::mojom::ModalType::kSystem)
.SetProperty(views::kElementIdentifierKey, kWelcomeTourDialogElementId)
.SetTitleText(l10n_util::GetStringFUTF16(
is_welcome_tour_v2_enabled ? IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT_V2
: IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT,
product_name))
IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT, product_name))
.SetTopContentView(views::Builder<views::ImageView>()
.SetImage(ui::ResourceBundle::GetSharedInstance()
.GetThemedLottieImageNamed(

@ -45,22 +45,22 @@ void MaybeActivateExperimentalArm(PrefService* prefs) {
// NOTE: Checking feature flag state activates experimental arms.
const bool is_holdback_enabled = features::IsWelcomeTourHoldbackEnabled();
const bool is_v1_enabled = features::IsWelcomeTourCounterfactuallyEnabled();
const bool is_v2_enabled = features::IsWelcomeTourV2Enabled();
const bool is_v3_enabled = features::IsWelcomeTourV3Enabled();
bool changed_experimental_arm = false;
const auto experimental_arm = first_experimental_arm.value();
switch (experimental_arm) {
case welcome_tour_metrics::ExperimentalArm::kHoldback:
changed_experimental_arm =
!is_holdback_enabled && (is_v1_enabled || is_v2_enabled);
!is_holdback_enabled && (is_v1_enabled || is_v3_enabled);
break;
case welcome_tour_metrics::ExperimentalArm::kV1:
changed_experimental_arm =
!is_v1_enabled && (is_holdback_enabled || is_v2_enabled);
!is_v1_enabled && (is_holdback_enabled || is_v3_enabled);
break;
case welcome_tour_metrics::ExperimentalArm::kV2:
case welcome_tour_metrics::ExperimentalArm::kV3:
changed_experimental_arm =
!is_v2_enabled && (is_holdback_enabled || is_v1_enabled);
!is_v3_enabled && (is_holdback_enabled || is_v1_enabled);
break;
}
@ -78,16 +78,16 @@ void MaybeRecordExperimentalArm(PrefService* prefs) {
std::optional<ExperimentalArm> experimental_arm;
if (features::IsWelcomeTourCounterfactuallyEnabled()) {
CHECK(!features::IsWelcomeTourHoldbackEnabled());
CHECK(!features::IsWelcomeTourV2Enabled());
CHECK(!features::IsWelcomeTourV3Enabled());
experimental_arm = ExperimentalArm::kV1;
} else if (features::IsWelcomeTourHoldbackEnabled()) {
CHECK(!features::IsWelcomeTourCounterfactuallyEnabled());
CHECK(!features::IsWelcomeTourV2Enabled());
CHECK(!features::IsWelcomeTourV3Enabled());
experimental_arm = ExperimentalArm::kHoldback;
} else if (features::IsWelcomeTourV2Enabled()) {
} else if (features::IsWelcomeTourV3Enabled()) {
CHECK(!features::IsWelcomeTourCounterfactuallyEnabled());
CHECK(!features::IsWelcomeTourHoldbackEnabled());
experimental_arm = ExperimentalArm::kV2;
experimental_arm = ExperimentalArm::kV3;
}
if (!experimental_arm) {

@ -52,8 +52,9 @@ enum class ExperimentalArm {
kMinValue = 0,
kHoldback = kMinValue,
kV1 = 1,
kV2 = 2,
kMaxValue = kV2,
// kV2 = 2, Deprecated.
kV3 = 3,
kMaxValue = kV3,
};
static constexpr auto kAllExperimentalArmsSet =
@ -62,7 +63,7 @@ static constexpr auto kAllExperimentalArmsSet =
ExperimentalArm::kMaxValue>({
ExperimentalArm::kHoldback,
ExperimentalArm::kV1,
ExperimentalArm::kV2,
ExperimentalArm::kV3,
});
// Enumeration of interactions users may engage in after the Welcome Tour. These

@ -59,7 +59,7 @@ class WelcomeTourChangedExperimentalArmMetricTest
scoped_feature_list.InitWithFeatureStates(
{{features::kWelcomeTourCounterfactualArm, IsV1Enabled()},
{features::kWelcomeTourHoldbackArm, IsHoldbackEnabled()},
{features::kWelcomeTourV2, IsV2Enabled()},
{features::kWelcomeTourV3, IsV3Enabled()},
{features::kWelcomeTourForceUserEligibility, true}});
}
@ -77,7 +77,7 @@ class WelcomeTourChangedExperimentalArmMetricTest
bool IsPrefValueV1() const { return GetPrefValue() == ExperimentalArm::kV1; }
bool IsPrefValueV2() const { return GetPrefValue() == ExperimentalArm::kV2; }
bool IsPrefValueV3() const { return GetPrefValue() == ExperimentalArm::kV3; }
bool IsHoldbackEnabled() const {
return GetEnabledArm() == ExperimentalArm::kHoldback;
@ -85,7 +85,7 @@ class WelcomeTourChangedExperimentalArmMetricTest
bool IsV1Enabled() const { return GetEnabledArm() == ExperimentalArm::kV1; }
bool IsV2Enabled() const { return GetEnabledArm() == ExperimentalArm::kV2; }
bool IsV3Enabled() const { return GetEnabledArm() == ExperimentalArm::kV3; }
private:
base::test::ScopedFeatureList scoped_feature_list;
@ -99,12 +99,12 @@ INSTANTIATE_TEST_SUITE_P(
::testing::Values(std::nullopt,
std::make_optional(ExperimentalArm::kHoldback),
std::make_optional(ExperimentalArm::kV1),
std::make_optional(ExperimentalArm::kV2)),
std::make_optional(ExperimentalArm::kV3)),
/*enabled_arm=*/
::testing::Values(std::nullopt,
std::make_optional(ExperimentalArm::kHoldback),
std::make_optional(ExperimentalArm::kV1),
std::make_optional(ExperimentalArm::kV2))));
std::make_optional(ExperimentalArm::kV3))));
// Tests -----------------------------------------------------------------------
@ -161,7 +161,7 @@ class WelcomeTourExperimentalArmMetricTest
scoped_feature_list.InitWithFeatureStates(
{{features::kWelcomeTourCounterfactualArm, IsV1Enabled()},
{features::kWelcomeTourHoldbackArm, IsHoldbackEnabled()},
{features::kWelcomeTourV2, IsV2Enabled()},
{features::kWelcomeTourV3, IsV3Enabled()},
{features::kWelcomeTourForceUserEligibility, true}});
}
@ -173,7 +173,7 @@ class WelcomeTourExperimentalArmMetricTest
bool IsV1Enabled() const { return GetEnabledArm() == ExperimentalArm::kV1; }
bool IsV2Enabled() const { return GetEnabledArm() == ExperimentalArm::kV2; }
bool IsV3Enabled() const { return GetEnabledArm() == ExperimentalArm::kV3; }
private:
base::test::ScopedFeatureList scoped_feature_list;
@ -186,7 +186,7 @@ INSTANTIATE_TEST_SUITE_P(
::testing::Values(std::nullopt,
std::make_optional(ExperimentalArm::kHoldback),
std::make_optional(ExperimentalArm::kV1),
std::make_optional(ExperimentalArm::kV2)));
std::make_optional(ExperimentalArm::kV3)));
// Tests -----------------------------------------------------------------------
@ -225,7 +225,7 @@ class WelcomeTourInteractionMetricsTest
// Only one of those features can be enabled at a time.
scoped_feature_list.InitWithFeatureStates(
{{features::kWelcomeTourHoldbackArm, IsHoldback()},
{features::kWelcomeTourV2, false},
{features::kWelcomeTourV3, false},
{features::kWelcomeTourCounterfactualArm, false}});
}
@ -377,7 +377,7 @@ TEST_F(WelcomeTourMetricsEnumTest, AllExperimentalArms) {
switch (arm) {
case ExperimentalArm::kHoldback:
case ExperimentalArm::kV1:
case ExperimentalArm::kV2:
case ExperimentalArm::kV3:
should_exist_in_all_set = true;
}

@ -29,6 +29,8 @@ namespace {
// static constexpr char kReasonForFirstTourPrevention[] =
// "ash.welcome_tour.prevented.first_reason";
// We will reuse the `v2` prefs for `v3` since there is no conflict.
// TODO: crbug.com/353568276 - Clean up the prefs after full launch.
static constexpr char kFirstExperimentalArm[] =
"ash.welcome_tour.v2.experimental_arm.first";
static constexpr char kTimeOfFirstInteractionPrefPrefix[] =

@ -104,7 +104,7 @@ TEST_F(WelcomeTourPrefsTest, FirstExperimentalArm) {
// For any call beyond the first, the function should return false and the
// marked first experimental arm should not change.
EXPECT_FALSE(MarkFirstExperimentalArm(pref_service(), ExperimentalArm::kV2));
EXPECT_FALSE(MarkFirstExperimentalArm(pref_service(), ExperimentalArm::kV3));
EXPECT_EQ(GetFirstExperimentalArm(pref_service()),
ExperimentalArm::kHoldback);
}

@ -47,7 +47,7 @@ using ::testing::Property;
using TestVariantsParam = std::tuple<
/*is_apps_collections_enabled=*/bool,
/*is_welcome_tour_v2_enabled=*/bool,
/*is_welcome_tour_v3_enabled=*/bool,
/*is_welcome_tour_counterfactaully_enabled=*/bool>;
// Matchers --------------------------------------------------------------------
@ -66,7 +66,7 @@ bool IsAppsCollectionsEnabled(TestVariantsParam param) {
return std::get<0>(param);
}
bool IsWelcomeTourV2Enabled(TestVariantsParam param) {
bool IsWelcomeTourV3Enabled(TestVariantsParam param) {
return std::get<1>(param);
}
@ -77,7 +77,7 @@ bool IsWelcomeTourCounterfactuallyEnabled(TestVariantsParam param) {
std::string GenerateTestSuffix(
const testing::TestParamInfo<TestVariantsParam>& info) {
return base::StrCat(
{IsWelcomeTourV2Enabled(info.param) ? "V2" : "V1", "_",
{IsWelcomeTourV3Enabled(info.param) ? "V3" : "V1", "_",
IsWelcomeTourCounterfactuallyEnabled(info.param) ? "Counterfactual_"
: "",
IsAppsCollectionsEnabled(info.param) ? "AppsCollectionsEnabled"
@ -97,12 +97,12 @@ class WelcomeTourInteractiveUiTest
// NOTE: These tests are not concerned with user eligibility, so explicitly
// force user eligibility for the Welcome Tour.
// Only one of `kWelcomeTourHoldbackArm`, `kWelcomeTourCounterfactualArm`
// and `kWelcomeTourV2` can be enabled at a time.
// and `kWelcomeTourV3` can be enabled at a time.
scoped_feature_list_.InitWithFeatureStates(
{{ash::features::kWelcomeTour, true},
{ash::features::kWelcomeTourForceUserEligibility, true},
{ash::features::kWelcomeTourV2,
IsWelcomeTourV2Enabled() && !IsWelcomeTourCounterfactuallyEnabled()},
{ash::features::kWelcomeTourV3,
IsWelcomeTourV3Enabled() && !IsWelcomeTourCounterfactuallyEnabled()},
{ash::features::kWelcomeTourCounterfactualArm,
IsWelcomeTourCounterfactuallyEnabled()},
{ash::features::kWelcomeTourHoldbackArm, false},
@ -144,10 +144,10 @@ class WelcomeTourInteractiveUiTest
return ::IsAppsCollectionsEnabled(GetParam());
}
// Returns whether the WelcomeTourV2 feature is enabled given test
// Returns whether the WelcomeTourV3 feature is enabled given test
// parameterization.
bool IsWelcomeTourV2Enabled() const {
return ::IsWelcomeTourV2Enabled(GetParam());
bool IsWelcomeTourV3Enabled() const {
return ::IsWelcomeTourV3Enabled(GetParam());
}
// Returns whether the WelcomeTour feature is counterfactually enabled given
@ -306,7 +306,7 @@ INSTANTIATE_TEST_SUITE_P(
WelcomeTourInteractiveUiTest,
testing::Combine(
/*is_apps_collections_enabled=*/testing::Bool(),
/*is_welcome_tour_v2_enabled=*/testing::Bool(),
/*is_welcome_tour_v3_enabled=*/testing::Bool(),
/*is_welcome_tour_counterfactually_enabled=*/testing::Bool()),
&GenerateTestSuffix);
@ -319,17 +319,12 @@ IN_PROC_BROWSER_TEST_P(WelcomeTourInteractiveUiTest, WelcomeTour) {
RunTestSequence(
// Step 0: Dialog.
InAnyContext(WaitForDialogVisibility(true)),
InSameContext(
Steps(CheckDialogAcceptButtonFocus(true),
CheckDialogAcceptButtonText(), CheckDialogCancelButtonText(),
CheckDialogDescription(
ash::features::IsWelcomeTourV2Enabled()
? IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT_V2
: IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT),
CheckDialogTitle(ash::features::IsWelcomeTourV2Enabled()
? IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT_V2
: IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT),
PressDialogAcceptButton())),
InSameContext(Steps(
CheckDialogAcceptButtonFocus(true), CheckDialogAcceptButtonText(),
CheckDialogCancelButtonText(),
CheckDialogDescription(IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT),
CheckDialogTitle(IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT),
PressDialogAcceptButton())),
// Step 1: Shelf.
InAnyContext(WaitForHelpBubble()),
@ -341,17 +336,18 @@ IN_PROC_BROWSER_TEST_P(WelcomeTourInteractiveUiTest, WelcomeTour) {
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
PressHelpBubbleDefaultButton())),
// Step 2: Status area.
InAnyContext(WaitForHelpBubble()),
InSameContext(
Steps(CheckHelpBubbleAnchor(ash::kUnifiedSystemTrayElementId),
CheckHelpBubbleBodyText(l10n_util::GetStringUTF16(
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT)),
CheckHelpBubbleDefaultButtonFocus(true),
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
PressHelpBubbleDefaultButton())),
// Step 2 in V1: Status area.
If([&] { return !ash::features::IsWelcomeTourV3Enabled(); },
InAnyContext(
Steps(WaitForHelpBubble(),
CheckHelpBubbleAnchor(ash::kUnifiedSystemTrayElementId),
CheckHelpBubbleBodyText(l10n_util::GetStringUTF16(
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT)),
CheckHelpBubbleDefaultButtonFocus(true),
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
PressHelpBubbleDefaultButton()))),
// Step 3: Home button.
// Step 3 in V1 and step 2 in V3: Home button.
InAnyContext(WaitForHelpBubble()),
InSameContext(Steps(
CheckHelpBubbleAnchor(ash::kHomeButtonElementId),
@ -364,7 +360,7 @@ IN_PROC_BROWSER_TEST_P(WelcomeTourInteractiveUiTest, WelcomeTour) {
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
PressHelpBubbleDefaultButton())),
// Step 4: Search box.
// Step 4 in V1 and step 3 in V3: Search box.
InAnyContext(WaitForHelpBubble()),
InSameContext(Steps(
CheckAppListBubbleVisibility(true),
@ -375,8 +371,8 @@ IN_PROC_BROWSER_TEST_P(WelcomeTourInteractiveUiTest, WelcomeTour) {
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
PressHelpBubbleDefaultButton())),
// Step 5 in V2: Files app.
If([&] { return ash::features::IsWelcomeTourV2Enabled(); },
// Step 4 in V3: Files app.
If([&] { return ash::features::IsWelcomeTourV3Enabled(); },
InAnyContext(
Steps(WaitForHelpBubble(), CheckAppListBubbleVisibility(true),
CheckHelpBubbleAnchor(ash::kFilesAppElementId),
@ -386,7 +382,7 @@ IN_PROC_BROWSER_TEST_P(WelcomeTourInteractiveUiTest, WelcomeTour) {
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
PressHelpBubbleDefaultButton()))),
// Step 5 in V1 and step 6 in V2: Settings app.
// Step 5: Settings app.
InAnyContext(WaitForHelpBubble()),
InSameContext(
Steps(CheckAppListBubbleVisibility(true),
@ -398,7 +394,7 @@ IN_PROC_BROWSER_TEST_P(WelcomeTourInteractiveUiTest, WelcomeTour) {
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
PressHelpBubbleDefaultButton())),
// Step 6 in V1 and step 7 in V2: Explore app.
// Step 6: Explore app.
InAnyContext(WaitForHelpBubble()),
InSameContext(Steps(
CheckAppListBubbleVisibility(true),
@ -410,7 +406,7 @@ IN_PROC_BROWSER_TEST_P(WelcomeTourInteractiveUiTest, WelcomeTour) {
IDS_ASH_WELCOME_TOUR_COMPLETE_BUTTON_TEXT),
PressHelpBubbleDefaultButton())),
// Step 7 in V1 and step 8 in V2: Explore app window.
// Step 7: Explore app window.
InAnyContext(WaitForBrowser()),
InSameContext(Steps(WaitForAppListBubbleToHide(),
CheckBrowserIsForWebApp(ash::kHelpAppId))));

@ -4868,11 +4868,12 @@
{
"name": "Enabled",
"enable_features": [
"WelcomeTourV2"
"WelcomeTourV3"
],
"disable_features": [
"WelcomeTourCounterfactualArm",
"WelcomeTourHoldbackArm"
"WelcomeTourHoldbackArm",
"WelcomeTourV2"
]
}
]

@ -54,6 +54,7 @@ chromium-metrics-reviews@google.com.
<int value="0" label="kHoldback"/>
<int value="1" label="kV1"/>
<int value="2" label="kV2"/>
<int value="3" label="kV3"/>
</enum>
<enum name="WelcomeTourInteraction">
@ -75,6 +76,7 @@ chromium-metrics-reviews@google.com.
<int value="6" label="kUserNotNewCrossDevice"/>
<int value="7" label="kUserTypeNotRegular"/>
<int value="8" label="kUserNotNewLocally"/>
<int value="9" label="kHoldbackExperimentArm"/>
</enum>
<enum name="WelcomeTourResult">