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

"use strict";

/* import-globals-from ../../../../../toolkit/components/pictureinpicture/tests/head.js */

ChromeUtils.defineESModuleGetters(this, {
  TOGGLE_POLICIES: "resource://gre/modules/PictureInPictureControls.sys.mjs",
});

const TEST_URL =
  getRootDirectory(gTestPath).replace(
    "chrome://mochitests/content",
    "https://mochitest.youtube.com:443"
  ) + "test-mock-wrapper.html";
const TEST_URL_TOGGLE_VISIBILITY =
  getRootDirectory(gTestPath).replace(
    "chrome://mochitests/content",
    "https://mochitest.youtube.com:443"
  ) + "test-toggle-visibility.html";

/**
 * Tests the mock-wrapper.js video wrapper script selects the expected element
 * responsible for toggling the video player's mute status.
 */
add_task(async function test_mock_mute_button() {
  await BrowserTestUtils.withNewTab(TEST_URL, async browser => {
    await ensureVideosReady(browser);

    // Open the video in PiP
    let videoID = "mock-video-controls";
    let pipWin = await triggerPictureInPicture(browser, videoID);
    ok(pipWin, "Got Picture-in-Picture window.");

    // Mute audio
    await toggleMute(browser, pipWin);
    ok(await isVideoMuted(browser, videoID), "The audio is muted.");

    await SpecialPowers.spawn(browser, [], async function () {
      let muteButton = content.document.querySelector(".mute-button");
      ok(
        muteButton.getAttribute("isMuted"),
        "muteButton has isMuted attribute."
      );
    });

    // Unmute audio
    await toggleMute(browser, pipWin);
    ok(!(await isVideoMuted(browser, videoID)), "The audio is playing.");

    await SpecialPowers.spawn(browser, [], async function () {
      let muteButton = content.document.querySelector(".mute-button");
      ok(
        !muteButton.getAttribute("isMuted"),
        "muteButton does not have isMuted attribute"
      );
    });

    // Close PiP window
    let pipClosed = BrowserTestUtils.domWindowClosed(pipWin);
    let closeButton = pipWin.document.getElementById("close");
    EventUtils.synthesizeMouseAtCenter(closeButton, {}, pipWin);
    await pipClosed;
  });
});

/**
 * Tests the mock-wrapper.js video wrapper script selects the expected element
 * responsible for toggling the video player's play/pause status.
 */
add_task(async function test_mock_play_pause_button() {
  await BrowserTestUtils.withNewTab(TEST_URL, async browser => {
    await ensureVideosReady(browser);
    await setupVideoListeners(browser);

    // Open the video in PiP
    let videoID = "mock-video-controls";
    let pipWin = await triggerPictureInPicture(browser, videoID);
    ok(pipWin, "Got Picture-in-Picture window.");

    info("Test a wrapper method with a correct selector");
    // Play video
    let playbackPromise = waitForVideoEvent(browser, "playing");
    let playPause = pipWin.document.getElementById("playpause");
    EventUtils.synthesizeMouseAtCenter(playPause, {}, pipWin);
    await playbackPromise;
    ok(!(await isVideoPaused(browser, videoID)), "The video is playing.");

    info("Test a wrapper method with an incorrect selector");
    // Pause the video.
    let pausePromise = waitForVideoEvent(browser, "pause");
    EventUtils.synthesizeMouseAtCenter(playPause, {}, pipWin);
    await pausePromise;
    ok(await isVideoPaused(browser, videoID), "The video is paused.");

    // Close PiP window
    let pipClosed = BrowserTestUtils.domWindowClosed(pipWin);
    let closeButton = pipWin.document.getElementById("close");
    EventUtils.synthesizeMouseAtCenter(closeButton, {}, pipWin);
    await pipClosed;
  });
});

/**
 * Tests the mock-wrapper.js video wrapper script does not toggle mute/umute
 * state when increasing/decreasing the volume using the arrow keys.
 */
add_task(async function test_volume_change_with_keyboard() {
  await BrowserTestUtils.withNewTab(TEST_URL, async browser => {
    await ensureVideosReady(browser);
    await setupVideoListeners(browser);

    // Open the video in PiP
    let videoID = "mock-video-controls";
    let pipWin = await triggerPictureInPicture(browser, videoID);
    ok(pipWin, "Got Picture-in-Picture window.");

    // Initially set video to be muted
    await toggleMute(browser, pipWin);
    ok(await isVideoMuted(browser, videoID), "The audio is not playing.");

    // Decrease volume with arrow down
    EventUtils.synthesizeKey("KEY_ArrowDown", {}, pipWin);
    ok(!(await isVideoMuted(browser, videoID)), "The audio is playing.");

    // Increase volume with arrow up
    EventUtils.synthesizeKey("KEY_ArrowUp", {}, pipWin);
    ok(!(await isVideoMuted(browser, videoID)), "The audio is still playing.");

    await SpecialPowers.spawn(browser, [], async function () {
      let video = content.document.querySelector("video");
      ok(!video.muted, "Video should be unmuted.");
    });

    // Close PiP window
    let pipClosed = BrowserTestUtils.domWindowClosed(pipWin);
    let closeButton = pipWin.document.getElementById("close");
    EventUtils.synthesizeMouseAtCenter(closeButton, {}, pipWin);
    await pipClosed;
  });
});

function waitForVideoEvent(browser, eventType) {
  return BrowserTestUtils.waitForContentEvent(browser, eventType, true);
}

async function toggleMute(browser, pipWin) {
  let mutedPromise = waitForVideoEvent(browser, "volumechange");
  let audioButton = pipWin.document.getElementById("audio");
  EventUtils.synthesizeMouseAtCenter(audioButton, {}, pipWin);
  await mutedPromise;
}

async function setupVideoListeners(browser) {
  await SpecialPowers.spawn(browser, [], async function () {
    let video = content.document.querySelector("video");

    // Set a listener for "playing" event
    video.addEventListener("playing", async () => {
      info("Got playing event!");
      let playPauseButton =
        content.document.querySelector(".play-pause-button");
      ok(
        !playPauseButton.getAttribute("isPaused"),
        "playPauseButton does not have isPaused attribute."
      );
    });

    // Set a listener for "pause" event
    video.addEventListener("pause", async () => {
      info("Got pause event!");
      let playPauseButton =
        content.document.querySelector(".play-pause-button");
      // mock-wrapper's pause() method uses an invalid selector and should throw
      // an error. Test that the PiP wrapper uses the fallback pause() method.
      // This is to ensure PiP can handle cases where a site wrapper script is
      // incorrect, but does not break functionality altogether.
      ok(
        !playPauseButton.getAttribute("isPaused"),
        "playPauseButton still doesn't have isPaused attribute."
      );
    });
  });
}

/**
 * Tests that the mock-wrapper.js video wrapper hides the pip toggle when shouldHideToggle()
 * returns true.
 */
add_task(async function test_mock_should_hide_toggle() {
  await testToggle(TEST_URL_TOGGLE_VISIBILITY, {
    "mock-video-controls": { canToggle: false, policy: TOGGLE_POLICIES.HIDDEN },
  });
});

/**
 * Tests that the mock-wrapper.js video wrapper does not hide the pip toggle when shouldHideToggle()
 * returns false.
 */
add_task(async function test_mock_should_not_hide_toggle() {
  await testToggle(TEST_URL, {
    "mock-video-controls": { canToggle: true },
  });
});
