/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * These tests check that we record the number of times search terms
 * persist in the Urlbar, and when search terms are cleared due to a
 * PopupNotification.
 *
 * This is different from existing telemetry that tracks whether users
 * interacted with the Urlbar or made another search while the search
 * terms were peristed.
 */

let defaultTestEngine;

// The main search string used in tests
const SEARCH_STRING = "chocolate cake";

// Telemetry keys.
const PERSISTED_VIEWED = "urlbar.persistedsearchterms.view_count";
const PERSISTED_REVERTED = "urlbar.persistedsearchterms.revert_by_popup_count";

add_setup(async function () {
  await SpecialPowers.pushPrefEnv({
    set: [["browser.urlbar.showSearchTerms.featureGate", true]],
  });
  await SearchTestUtils.installSearchExtension(
    {
      name: "MozSearch",
      search_url: "https://www.example.com/",
      search_url_get_params: "q={searchTerms}&pc=fake_code",
    },
    { setAsDefault: true }
  );
  defaultTestEngine = Services.search.getEngineByName("MozSearch");
  Services.telemetry.clearScalars();

  registerCleanupFunction(async function () {
    await PlacesUtils.history.clear();
    Services.telemetry.clearScalars();
  });
});

// Starts a search with a tab and asserts that
// the state of the Urlbar contains the search term.
async function searchWithTab(
  searchString,
  tab = null,
  engine = defaultTestEngine,
  assertSearchString = true
) {
  if (!tab) {
    tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
  }

  let [expectedSearchUrl] = UrlbarUtils.getSearchQueryUrl(engine, searchString);
  let browserLoadedPromise = BrowserTestUtils.browserLoaded(
    tab.linkedBrowser,
    false,
    expectedSearchUrl
  );

  gURLBar.focus();
  await UrlbarTestUtils.promiseAutocompleteResultPopup({
    window,
    waitForFocus,
    value: searchString,
    fireInputEvent: true,
  });
  EventUtils.synthesizeKey("KEY_Enter");
  await browserLoadedPromise;

  if (assertSearchString) {
    assertSearchStringIsInUrlbar(searchString);
  }

  return { tab, expectedSearchUrl };
}

add_task(async function load_page_with_persisted_search() {
  let { tab } = await searchWithTab(SEARCH_STRING);
  const scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
});

add_task(async function load_page_without_persisted_search() {
  await SpecialPowers.pushPrefEnv({
    set: [["browser.urlbar.showSearchTerms.featureGate", false]],
  });

  let { tab } = await searchWithTab(
    SEARCH_STRING,
    null,
    defaultTestEngine,
    false
  );

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, undefined);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
  await SpecialPowers.popPrefEnv();
});

// Multiple searches should result in the correct number of recorded views.
add_task(async function load_page_n_times() {
  let N = 5;
  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
  for (let index = 0; index < N; ++index) {
    await searchWithTab(SEARCH_STRING, tab);
  }

  const scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, N);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
});

// A persisted search view event should not be recorded when unfocusing the urlbar.
add_task(async function focus_and_unfocus() {
  let { tab } = await searchWithTab(SEARCH_STRING);

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  gURLBar.focus();
  gURLBar.select();
  gURLBar.blur();

  // Focusing and unfocusing the urlbar shouldn't change the persisted view count.
  scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
});

// A persisted search view event should not be recorded by a
// pushState event after a page has been loaded.
add_task(async function history_api() {
  let { tab } = await searchWithTab(SEARCH_STRING);

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  await SpecialPowers.spawn(tab.linkedBrowser, [], function () {
    let url = new URL(content.window.location);
    let someState = { value: true };
    url.searchParams.set("pc", "fake_code_2");
    content.history.pushState(someState, "", url);
    someState.value = false;
    content.history.replaceState(someState, "", url);
  });

  scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
});

// A persisted search view event should be recorded when switching back to a tab
// that contains search terms.
add_task(async function switch_tabs() {
  let { tab } = await searchWithTab(SEARCH_STRING);

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser);
  await BrowserTestUtils.switchTab(gBrowser, tab);

  scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 2);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
  BrowserTestUtils.removeTab(tab2);
});

// A telemetry event should be recorded when returning to a persisted SERP via tabhistory.
add_task(async function tabhistory() {
  let { tab } = await searchWithTab(SEARCH_STRING);

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
  BrowserTestUtils.loadURIString(
    tab.linkedBrowser,
    "https://www.example.com/some_url"
  );
  await browserLoadedPromise;

  let pageShowPromise = BrowserTestUtils.waitForContentEvent(
    tab.linkedBrowser,
    "pageshow"
  );
  tab.linkedBrowser.goBack();
  await pageShowPromise;

  scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 2);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
});

// PopupNotification's that rely on an anchor element in the urlbar should trigger
// an increment of the revert counter.
// This assumes the anchor element is present in the Urlbar during a valid pageproxystate.
add_task(async function popup_in_urlbar() {
  let { tab } = await searchWithTab(SEARCH_STRING);
  let promisePopupShown = BrowserTestUtils.waitForEvent(
    PopupNotifications.panel,
    "popupshown"
  );
  PopupNotifications.show(
    gBrowser.selectedBrowser,
    "test-notification",
    "This is a sample popup."
  );
  await promisePopupShown;

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, 1);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
});

// Non-persistent PopupNotifications won't re-appear if a user switches
// tabs and returns to the tab that had the Popup.
add_task(async function non_persistent_popup_in_urlbar_switch_tab() {
  let { tab } = await searchWithTab(SEARCH_STRING);
  let promisePopupShown = BrowserTestUtils.waitForEvent(
    PopupNotifications.panel,
    "popupshown"
  );
  PopupNotifications.show(
    gBrowser.selectedBrowser,
    "test-notification",
    "This is a sample popup.",
    "geo-notification-icon"
  );
  await promisePopupShown;

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, 1);

  let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser);
  await BrowserTestUtils.switchTab(gBrowser, tab);

  scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 2);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, 1);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
  BrowserTestUtils.removeTab(tab2);
});

// Persistent PopupNotifications will constantly appear to users
// even if they switch to another tab and switch back.
add_task(async function persistent_popup_in_urlbar_switch_tab() {
  let { tab } = await searchWithTab(SEARCH_STRING);
  let promisePopupShown = BrowserTestUtils.waitForEvent(
    PopupNotifications.panel,
    "popupshown"
  );
  PopupNotifications.show(
    gBrowser.selectedBrowser,
    "test-notification",
    "This is a sample popup.",
    "geo-notification-icon",
    null,
    null,
    { persistent: true }
  );
  await promisePopupShown;

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, 1);

  let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser);
  promisePopupShown = BrowserTestUtils.waitForEvent(
    PopupNotifications.panel,
    "popupshown"
  );
  await BrowserTestUtils.switchTab(gBrowser, tab);
  await promisePopupShown;

  scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 2);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, 2);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
  BrowserTestUtils.removeTab(tab2);
});

// If the persist feature is not enabled, a telemetry event should not be recorded
// if a PopupNotification uses an anchor in the Urlbar.
add_task(async function popup_in_urlbar_without_feature() {
  await SpecialPowers.pushPrefEnv({
    set: [["browser.urlbar.showSearchTerms.featureGate", false]],
  });

  let { tab } = await searchWithTab(
    SEARCH_STRING,
    null,
    defaultTestEngine,
    false
  );
  let promisePopupShown = BrowserTestUtils.waitForEvent(
    PopupNotifications.panel,
    "popupshown"
  );
  PopupNotifications.show(
    gBrowser.selectedBrowser,
    "test-notification",
    "This is a sample popup."
  );
  await promisePopupShown;

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, undefined);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
  await SpecialPowers.popPrefEnv();
});

// If the anchor element for the PopupNotification is not located in the Urlbar,
// a telemetry event should not be recorded.
add_task(async function popup_not_in_urlbar() {
  let { tab } = await searchWithTab(SEARCH_STRING);

  let promisePopupShown = BrowserTestUtils.waitForEvent(
    PopupNotifications.panel,
    "popupshown"
  );
  PopupNotifications.show(
    gBrowser.selectedBrowser,
    "test-notification",
    "This is a sample popup that uses the unified extensions button.",
    gUnifiedExtensions.getPopupAnchorID(gBrowser.selectedBrowser, window)
  );
  await promisePopupShown;

  let scalars = TelemetryTestUtils.getProcessScalars("parent", false, true);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_VIEWED, 1);
  TelemetryTestUtils.assertScalar(scalars, PERSISTED_REVERTED, undefined);

  // Clean up.
  BrowserTestUtils.removeTab(tab);
});
