| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "base/ios/ios_util.h" |
| #import "components/metrics/dwa/dwa_recorder.h" |
| #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h" |
| #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_ui_test_util.h" |
| #import "ios/chrome/browser/metrics/model/metrics_app_interface.h" |
| #import "ios/chrome/browser/signin/model/fake_system_identity.h" |
| #import "ios/chrome/test/earl_grey/chrome_actions.h" |
| #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" |
| #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h" |
| #import "ios/chrome/test/earl_grey/chrome_matchers.h" |
| #import "ios/chrome/test/earl_grey/chrome_test_case.h" |
| #import "ios/testing/earl_grey/app_launch_configuration.h" |
| #import "ios/testing/earl_grey/earl_grey_test.h" |
| #import "net/test/embedded_test_server/default_handlers.h" |
| |
| using chrome_test_util::GoogleServicesSettingsButton; |
| using chrome_test_util::SettingsDoneButton; |
| |
| @interface DWATestCase : ChromeTestCase |
| |
| @end |
| |
| @implementation DWATestCase |
| |
| + (void)setUpForTestCase { |
| [super setUpForTestCase]; |
| } |
| |
| - (void)setUp { |
| [super setUp]; |
| |
| // These tests enable history synchronization via Recent Tabs. |
| // If the list contains too many tabs, the button at the bottom of |
| // the view moves offscreen, and its animation causes the tests to freeze, |
| // similar to crbug.com/640977. |
| if (![ChromeTestCase forceRestartAndWipe]) { |
| [ChromeEarlGrey clearBrowsingHistory]; |
| } |
| |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"setUp: Failed to assert that DWA was not enabled."); |
| |
| // Sign in to Chrome and enable history sync. |
| |
| // Note: URL-keyed anonymized data collection is turned on as part of the |
| // flow to Sign in to Chrome and enable history sync. This matches the main |
| // user flow that enables DWA. |
| [SigninEarlGreyUI signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1] |
| enableHistorySync:YES]; |
| [ChromeEarlGrey waitForSyncTransportStateActiveWithTimeout: |
| syncher::kSyncDWAOperationsTimeout]; |
| |
| // Grant metrics consent and update MetricsServicesManager. |
| [MetricsAppInterface overrideMetricsAndCrashReportingForTesting]; |
| |
| GREYAssert(![MetricsAppInterface setMetricsAndCrashReportingForTesting:YES], |
| @"setUp: Unpaired set/reset of user consent."); |
| |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:YES], |
| @"setUp: Failed to assert that DWA was enabled."); |
| GREYAssert([MetricsAppInterface DWARecorderAllowedForAllProfiles:YES], |
| @"setUp: Failed to assert that DWA was allowed for all profiles."); |
| |
| net::test_server::RegisterDefaultHandlers(self.testServer); |
| GREYAssertTrue(self.testServer->Start(), @"Server did not start."); |
| } |
| |
| - (void)tearDownHelper { |
| [ChromeEarlGrey waitForSyncTransportStateActiveWithTimeout: |
| syncher::kSyncDWAOperationsTimeout]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:YES], |
| @"tearDownHelper: Failed to assert that DWA was enabled."); |
| [MetricsAppInterface clearDWARecorder]; |
| [self assertDwaRecorderIsEmpty]; |
| |
| // Revoke metrics consent and update MetricsServicesManager. |
| GREYAssert([MetricsAppInterface setMetricsAndCrashReportingForTesting:NO], |
| @"tearDownHelper: Unpaired set/reset of user consent."); |
| [MetricsAppInterface stopOverridingMetricsAndCrashReportingForTesting]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"tearDownHelper: Failed to assert that DWA was not enabled."); |
| |
| // Sign out of Chrome. |
| |
| // Note: URL-keyed anonymized data collection is turned off as part of the |
| // flow to Sign out of Chrome. This matches the main user flow that disables |
| // DWA. |
| [SigninEarlGrey signOut]; |
| |
| [ChromeEarlGrey clearFakeSyncServerData]; |
| |
| [super tearDownHelper]; |
| } |
| |
| // Enable the DWA feature |
| - (AppLaunchConfiguration)appConfigurationForTestCase { |
| AppLaunchConfiguration config = [super appConfigurationForTestCase]; |
| |
| config.features_enabled.push_back(metrics::dwa::kDwaFeature); |
| |
| return config; |
| } |
| |
| #pragma mark - Helpers |
| |
| // Waits for a new incognito tab to be opened. |
| - (void)openNewIncognitoTab { |
| const NSUInteger incognitoTabCount = [ChromeEarlGrey incognitoTabCount]; |
| [ChromeEarlGrey openNewIncognitoTab]; |
| [ChromeEarlGrey waitForIncognitoTabCount:(incognitoTabCount + 1)]; |
| GREYAssert([ChromeEarlGrey isIncognitoMode], |
| @"openNewIncognitoTab: Failed to switch to incognito mode."); |
| } |
| |
| // Waits for the current incognito tab to be closed. |
| - (void)closeCurrentIncognitoTab { |
| const NSUInteger incognitoTabCount = [ChromeEarlGrey incognitoTabCount]; |
| [ChromeEarlGrey closeCurrentTab]; |
| [ChromeEarlGrey waitForIncognitoTabCount:(incognitoTabCount - 1)]; |
| } |
| |
| // Waits for all incognito tabs to be closed. |
| - (void)closeAllIncognitoTabs { |
| [ChromeEarlGrey closeAllIncognitoTabs]; |
| [ChromeEarlGrey waitForIncognitoTabCount:0]; |
| |
| // The user is dropped into the tab grid after closing the last incognito tab. |
| // Therefore this test must manually switch back to showing the normal tabs. |
| [[EarlGrey |
| selectElementWithMatcher:chrome_test_util::TabGridOpenTabsPanelButton()] |
| performAction:grey_tap()]; |
| [[EarlGrey selectElementWithMatcher:chrome_test_util::TabGridDoneButton()] |
| performAction:grey_tap()]; |
| GREYAssert(![ChromeEarlGrey isIncognitoMode], |
| @"closeAllIncognitoTabs: Failed to switch to normal mode."); |
| } |
| |
| // Waits for a new tab to be opened. |
| - (void)openNewRegularTab { |
| const NSUInteger tabCount = [ChromeEarlGrey mainTabCount]; |
| [ChromeEarlGrey openNewTab]; |
| [ChromeEarlGrey waitForMainTabCount:(tabCount + 1)]; |
| } |
| |
| // Toggle "Make searches and browsing better" switch on. |
| - (void)turnOnMsbbSwitch { |
| // Wait for the Msbb switch to appear. |
| [ChromeEarlGrey waitForUIElementToAppearWithMatcher: |
| chrome_test_util::TableViewSwitchCell( |
| @"betterSearchAndBrowsingItem_switch", NO)]; |
| [[EarlGrey |
| selectElementWithMatcher:chrome_test_util::TableViewSwitchCell( |
| @"betterSearchAndBrowsingItem_switch", NO)] |
| performAction:chrome_test_util::TurnTableViewSwitchOn(YES)]; |
| } |
| |
| // Toggle "Make searches and browsing better" switch off. |
| - (void)turnOffMsbbSwitch { |
| [ChromeEarlGrey waitForUIElementToAppearWithMatcher: |
| chrome_test_util::TableViewSwitchCell( |
| @"betterSearchAndBrowsingItem_switch", YES)]; |
| [[EarlGrey |
| selectElementWithMatcher:chrome_test_util::TableViewSwitchCell( |
| @"betterSearchAndBrowsingItem_switch", YES)] |
| performAction:chrome_test_util::TurnTableViewSwitchOn(NO)]; |
| } |
| |
| // Assertions to check that the DWA recorder is enabled and allowed for all |
| // profiles. |
| - (void)assertDwaIsEnabledAndAllowed { |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:YES], |
| @"Failed to assert that DWA was enabled."); |
| GREYAssert([MetricsAppInterface DWARecorderAllowedForAllProfiles:YES], |
| @"Failed to assert that DWA was " |
| @"allowed for all profiles."); |
| } |
| |
| // Assertions to check that the DWA recorder has entries. |
| - (void)assertDwaRecorderHasMetrics { |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:YES], |
| @"DWA Recorder should have " |
| @"entries."); |
| } |
| |
| // Assertions to check that the DWA recorder is not enabled and not allowed for |
| // all profiles. |
| - (void)assertDwaIsDisabledAndDisallowed { |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"Failed to assert that DWA was not enabled."); |
| GREYAssert([MetricsAppInterface DWARecorderAllowedForAllProfiles:NO], |
| @"Failed to assert that DWA was not " |
| @"allowed for all profiles."); |
| } |
| |
| // Assertions to check that the DWA recorder has no entries. |
| - (void)assertDwaRecorderIsEmpty { |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:NO], |
| @"DWA Recorder should not have any " |
| @"entries."); |
| } |
| |
| #pragma mark - Tests |
| |
| // The tests in this file should correspond to the tests in |
| // //chrome/browser/metrics/dwa_browsertest.cc |
| |
| // LINT.IfChange(DwaServiceCheck) |
| - (void)testDwaServiceCheck { |
| // [Note: Tests begin with an open regular tab. This tab is opened in setUp. |
| // DWA should be enabled with first regular browser. |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:YES], |
| @"Failed to assert that DWA was enabled."); |
| |
| // Records a DWA entry metric. |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| GREYAssert([MetricsAppInterface hasUnsentDWALogs:NO], |
| @"DWA Service should not have unsent logs."); |
| |
| [MetricsAppInterface DWAServiceFlushCall]; |
| [self assertDwaRecorderIsEmpty]; |
| GREYAssert([MetricsAppInterface hasUnsentDWALogs:YES], |
| @"DWA Service should have unsent logs."); |
| } |
| // LINT.ThenChange(/chrome/browser/metrics/dwa_browsertest.cc:DwaServiceCheck) |
| |
| // Make sure that DWA is disabled and purged while an incognito window is open. |
| // LINT.IfChange(RegularBrowserPlusIncognitoCheck) |
| - (void)testRegularBrowserPlusIncognitoCheck { |
| // Note: Tests begin with an open regular tab. This tab is opened in setUp. |
| // DWA should be enabled with first regular browser. |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:YES], |
| @"Failed to assert that DWA " |
| @"was enabled."); |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:YES], |
| @"Failed to record test " |
| @"entry metric."); |
| |
| // Opening an incognito browser should disable DwaRecorder and metrics |
| // should be purged. |
| [self openNewIncognitoTab]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"Failed to assert that DWA " |
| @"was not enabled."); |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:NO], |
| @"DWA Recorder should not " |
| @"have any entries."); |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:NO], |
| @"DWA Recorder should not " |
| @"have any entries."); |
| |
| // Opening another regular tab mustn't enabled DWA. |
| [self openNewRegularTab]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"Failed to assert that DWA was not enabled."); |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:NO], |
| @"DWA Recorder should not " |
| @"have any entries."); |
| |
| // Opening and closing another incognito browser must not enable DWA. |
| [self openNewIncognitoTab]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"Failed to assert that DWA was not enabled."); |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:NO], |
| @"DWA Recorder should not " |
| @"have any entries."); |
| |
| [self closeCurrentIncognitoTab]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"Failed to assert that DWA was not enabled."); |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:NO], |
| @"DWA Recorder should not " |
| @"have any entries."); |
| |
| // TODO(crbug.com/41271925): Due to continuous animations, it is not feasible |
| // (i) to use the tab switcher to switch between modes or (ii) to omit the |
| // below code block and simply call [ChromeEarlGrey closeAllIncognitoTabs]; |
| // from incognito mode. |
| [self openNewRegularTab]; |
| [ChromeEarlGrey closeCurrentTab]; |
| [ChromeEarlGrey closeCurrentTab]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"Failed to assert that DWA " |
| @"was not enabled."); |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:NO], |
| @"DWA Recorder should not " |
| @"have any entries."); |
| |
| // At this point, there is one open regular tab and one open incognito tab. |
| [ChromeEarlGrey closeAllIncognitoTabs]; |
| // All incognito tabs have been closed, so DWA should be enabled. |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:YES], |
| @"Failed to assert that DWA " |
| @"was enabled."); |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:NO], |
| @"DWA Recorder should not " |
| @"have any entries."); |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| GREYAssert([MetricsAppInterface DWARecorderHasEntries:YES], |
| @"Failed to record test " |
| @"entry metric."); |
| } |
| // LINT.ThenChange(/chrome/browser/metrics/dwa_browsertest.cc:RegularBrowserPlusIncognitoCheck) |
| |
| // Make sure opening a regular browser after Incognito doesn't enable DWA. |
| // LINT.IfChange(IncognitoBrowserPlusRegularCheck) |
| - (void)testIncognitoBrowserPlusRegularCheck { |
| // Note: Tests begin with an open regular tab. This tab is opened in setUp. |
| |
| // TODO(crbug.com/41271925): Due to continuous animations, it is not feasible |
| // to close the regular tab that is already open. The functions closeAllTabs, |
| // closeCurrentTab, and closeAllTabsInCurrentMode close the tab and then hang. |
| // As a workaround, we open an incognito tab and then close the regular tab to |
| // get to a state in which a single incognito tab is open. |
| |
| // Opening an incognito browser should disable DwaRecorder and metrics should |
| // be purged. |
| [self openNewIncognitoTab]; |
| [ChromeEarlGrey closeAllNormalTabs]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"Failed to assert that DWA was not enabled."); |
| |
| // Opening another regular browser must not enable DWA. |
| [self openNewRegularTab]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:NO], |
| @"Failed to assert that DWA was not enabled."); |
| |
| // All incognito tabs have been closed, so DWA should be enabled. |
| [ChromeEarlGrey closeAllIncognitoTabs]; |
| GREYAssert([MetricsAppInterface checkDWARecordingEnabled:YES], |
| @"Failed to assert that DWA was enabled."); |
| } |
| // LINT.ThenChange(/chrome/browser/metrics/dwa_browsertest.cc:IncognitoBrowserPlusRegularCheck) |
| |
| // Tests that disabling MSBB UKM consent disables and purges DWA. |
| // Additionally tests that DWA is disabled until all UKM consents are enabled. |
| // LINT.IfChange(UkmMsbbConsentChangeCheck) |
| - (void)testUkmMsbbConsentChangeCheck { |
| // Note: Tests begin with an open regular tab. This tab is opened in setUp. |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| [self assertDwaIsEnabledAndAllowed]; |
| [self assertDwaRecorderHasMetrics]; |
| |
| [ChromeEarlGreyUI openSettingsMenu]; |
| [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: |
| GoogleServicesSettingsButton()]; |
| |
| // Open Google services settings. |
| [ChromeEarlGreyUI tapSettingsMenuButton:GoogleServicesSettingsButton()]; |
| [ChromeEarlGreyUI waitForAppToIdle]; |
| |
| // Toggle "Make searches and browsing better" switch off. |
| [self turnOffMsbbSwitch]; |
| [self assertDwaIsDisabledAndDisallowed]; |
| [self assertDwaRecorderIsEmpty]; |
| |
| // Toggle "Make searches and browsing better" switch on. |
| [self turnOnMsbbSwitch]; |
| [self assertDwaIsEnabledAndAllowed]; |
| [self assertDwaRecorderIsEmpty]; |
| |
| // Validate DWA entries is able to be recorded when all consents are enabled. |
| [MetricsAppInterface recordTestDWAEntryMetric]; |
| [self assertDwaRecorderHasMetrics]; |
| |
| [[EarlGrey selectElementWithMatcher:SettingsDoneButton()] |
| performAction:grey_tap()]; |
| } |
| // LINT.ThenChange(/chrome/browser/metrics/dwa_browsertest.cc:UkmMsbbConsentChangeCheck) |
| |
| @end |