/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

/**
 * These tests check on SPA page loads in a single tab.
 * They also ensure the SinglePageAppUtils method work as expected.
 */

"use strict";

add_setup(async function () {
  await initSinglePageAppTest();

  registerCleanupFunction(async () => {
    SearchSERPTelemetry.overrideSearchTelemetryForTests();
    resetTelemetry();
  });
});

add_task(async function test_load_serp() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();
  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 1 },
      "browser.search.withads.unknown": { "example1:tagged": 1 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);
  BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_and_push_unrelated_state() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();
  let searchParams = new URL(tab.linkedBrowser.currentURI.spec).searchParams;

  Assert.equal(
    searchParams.get("foobar"),
    null,
    "Query param value for: foobar"
  );

  await SinglePageAppUtils.pushUnrelatedState(tab, {
    key: "foobar",
    value: "baz",
  });
  searchParams = new URL(tab.linkedBrowser.currentURI.spec).searchParams;
  Assert.equal(
    searchParams.get("foobar"),
    "baz",
    "Query param value for: foobar"
  );

  // If the SERP adds query parameter unrelated to the search query or the
  // query param matching the default results page, we shouldn't record another
  // SERP load.
  /* eslint-disable-next-line mozilla/no-arbitrary-setTimeout */
  await new Promise(resolve => setTimeout(resolve, 1000));
  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 1 },
      "browser.search.withads.unknown": { "example1:tagged": 1 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);
  BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_and_load_non_serp_tab() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();

  await SinglePageAppUtils.clickImagesTab(tab);
  // If clicking another tab in a SPA, we shouldn't record another SERP load.
  /* eslint-disable-next-line mozilla/no-arbitrary-setTimeout */
  await new Promise(resolve => setTimeout(resolve, 1000));
  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 1 },
      "browser.search.withads.unknown": { "example1:tagged": 1 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_and_click_ad() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();

  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 1 },
      "browser.search.withads.unknown": { "example1:tagged": 1 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  await SinglePageAppUtils.clickAd(tab);
  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 1 },
      "browser.search.withads.unknown": { "example1:tagged": 1 },
      "browser.search.adclicks.unknown": { "example1:tagged": 1 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_and_click_redirect_ad() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();

  await SinglePageAppUtils.clickRedirectAd(tab);
  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 1 },
      "browser.search.withads.unknown": { "example1:tagged": 1 },
      "browser.search.adclicks.unknown": { "example1:tagged": 1 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);
  await BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_and_click_redirect_ad_in_new_tab() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();

  let redirectedTab = await SinglePageAppUtils.clickRedirectAdInNewTab(tab);
  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 1 },
      "browser.search.withads.unknown": { "example1:tagged": 1 },
      "browser.search.adclicks.unknown": { "example1:tagged": 1 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  await BrowserTestUtils.removeTab(tab);
  await BrowserTestUtils.removeTab(redirectedTab);
});

add_task(async function test_load_serp_click_a_related_search() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();
  await SinglePageAppUtils.visitRelatedSearch(tab);

  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 2 },
      "browser.search.withads.unknown": { "example1:tagged": 2 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
    {
      impression: {
        provider: "example1",
      },
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  await BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_click_a_related_search_click_ad() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();
  await SinglePageAppUtils.visitRelatedSearch(tab);
  await SinglePageAppUtils.clickAd(tab);
  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 2 },
      "browser.search.withads.unknown": { "example1:tagged": 2 },
      "browser.search.adclicks.unknown": { "example1:tagged": 1 },
    }
  );
  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  await BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_click_non_serp_tab_click_all() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();
  await SinglePageAppUtils.clickImagesTab(tab);

  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 1 },
      "browser.search.withads.unknown": { "example1:tagged": 1 },
    }
  );

  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  info("Click All tab to return to a SERP.");
  await SinglePageAppUtils.clickAllTab(tab);

  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 2 },
      "browser.search.withads.unknown": { "example1:tagged": 2 },
    }
  );

  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
    {
      impression: {
        provider: "example1",
      },
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  await BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_and_use_back_and_forward() {
  resetTelemetry();
  let tab = await SinglePageAppUtils.createTabAndLoadURL();
  await SinglePageAppUtils.visitRelatedSearch(tab);
  await SinglePageAppUtils.goBack(tab);
  await SinglePageAppUtils.goForward(tab);

  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 2 },
      "browser.search.withads.unknown": { "example1:tagged": 2 },
      "browser.search.content.tabhistory": { "example1:tagged:ff": 2 },
      "browser.search.withads.tabhistory": { "example1:tagged": 2 },
    }
  );

  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
    {
      impression: {
        provider: "example1",
      },
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
      abandonment: {
        reason: SearchSERPTelemetryUtils.ABANDONMENTS.NAVIGATION,
      },
    },
    {
      impression: {
        provider: "example1",
        source: "tabhistory",
      },
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
      abandonment: {
        reason: SearchSERPTelemetryUtils.ABANDONMENTS.NAVIGATION,
      },
    },
    {
      impression: {
        provider: "example1",
        source: "tabhistory",
      },
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  await BrowserTestUtils.removeTab(tab);
});

add_task(async function test_load_serp_and_click_shopping_tab() {
  resetTelemetry();

  const PROVIDERS = [
    {
      telemetryId: "example1",
      searchPageRegexp:
        /^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/telemetry\/searchTelemetrySinglePageApp/,
      queryParamNames: ["s"],
      codeParamName: "abc",
      taggedCodes: ["ff"],
      adServerAttributes: ["mozAttr"],
      extraAdServersRegexps: [
        /^https:\/\/example\.com\/ad/,
        /^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/telemetry\/redirect_ad/,
      ],
      components: [
        {
          included: {
            parent: {
              selector: "#searchbox-container",
            },
            related: {
              selector: "#searchbox-suggestions",
            },
            children: [
              {
                selector: "#searchbox",
              },
            ],
          },
          topDown: true,
          type: SearchSERPTelemetryUtils.COMPONENTS.INCONTENT_SEARCHBOX,
        },
        {
          type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          default: true,
        },
      ],
      pageTypeParam: {
        enableSPAHandling: true,
        keys: ["page"],
        pageTypes: [
          {
            name: "web",
            values: ["web"],
            target: "default_tab",
            enabled: true,
            isDefault: true,
          },
          {
            name: "shopping",
            values: ["shopping"],
            target: "shopping_tab",
            enabled: true,
          },
          {
            name: "images",
            values: ["images"],
            target: "images_tab",
            enabled: false,
          },
          {
            name: "videos",
            values: ["videos"],
            target: "videos_tab",
            enabled: false,
          },
          {
            name: "news",
            values: ["news"],
            target: "news_tab",
            enabled: false,
          },
        ],
      },
      impressionAttributes: [
        {
          key: "is_shopping_page",
          url: {
            regexp: "&page=shopping",
          },
        },
        {
          key: "shopping_tab_displayed",
          element: {
            selector: "nav a",
            attributeName: "href",
            regexp: "&page=shopping",
            component: {
              type: "shopping_tab",
              countImpressions: true,
            },
          },
        },
      ],
    },
  ];
  SearchSERPTelemetry.overrideSearchTelemetryForTests(PROVIDERS);
  await waitForIdle();

  let tab = await SinglePageAppUtils.createTabAndLoadURL();
  await SinglePageAppUtils.clickShoppingTab(tab);

  await assertSearchSourcesTelemetry(
    {},
    {
      "browser.search.content.unknown": { "example1:tagged:ff": 2 },
      "browser.search.withads.unknown": { "example1:tagged": 2 },
    }
  );

  assertSERPTelemetry([
    {
      impression: {
        provider: "example1",
        is_shopping_page: "false",
        shopping_tab_displayed: "true",
      },
      engagements: [
        {
          action: SearchSERPTelemetryUtils.ACTIONS.CLICKED,
          target: SearchSERPTelemetryUtils.COMPONENTS.SHOPPING_TAB,
        },
      ],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.SHOPPING_TAB,
          ads_loaded: "1",
          ads_visible: "1",
          ads_hidden: "0",
        },
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
    {
      impression: {
        provider: "example1",
        is_shopping_page: "true",
        shopping_tab_displayed: "true",
      },
      engagements: [],
      adImpressions: [
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.SHOPPING_TAB,
          ads_loaded: "1",
          ads_visible: "1",
          ads_hidden: "0",
        },
        {
          component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
          ads_loaded: "2",
          ads_visible: "2",
          ads_hidden: "0",
        },
      ],
    },
  ]);

  await BrowserTestUtils.removeTab(tab);
});
