"use strict";

add_task(async function test_sessions_tab_value() {
  info("Testing set/get/deleteTabValue.");

  async function background() {
    let tests = [{key: "tabkey1", value: "Tab Value"},
                 {key: "tabkey2", value: 25},
                 {key: "tabkey3", value: {val: "Tab Value"}},
                 {key: "tabkey4", value: function() { return null; }}];

    async function test(params) {
      let {key, value} = params;
      let tabs = await browser.tabs.query({currentWindow: true, active: true});
      let currentTabId = tabs[0].id;

      browser.sessions.setTabValue(currentTabId, key, value);

      let testValue1 = await browser.sessions.getTabValue(currentTabId, key);
      let valueType = typeof value;

      browser.test.log(`Test that setting, getting and deleting tab value behaves properly when value is type "${valueType}"`);

      if (valueType == "string") {
        browser.test.assertEq(value, testValue1, `Value for key '${key}' should be '${value}'.`);
        browser.test.assertEq("string", (typeof testValue1), "typeof value should be '${valueType}'.");
      } else if (valueType == "number") {
        browser.test.assertEq(value, testValue1, `Value for key '${key}' should be '${value}'.`);
        browser.test.assertEq("number", (typeof testValue1), "typeof value should be '${valueType}'.");
      } else if (valueType == "object") {
        let innerVal1 = value.val;
        let innerVal2 = testValue1.val;
        browser.test.assertEq(innerVal1, innerVal2, `Value for key '${key}' should be '${innerVal1}'.`);
      } else if (valueType == "function") {
        browser.test.assertEq(null, testValue1, `Value for key '${key}' is non-JSON-able and should be 'null'.`);
      }

      // Remove the tab key/value.
      browser.sessions.removeTabValue(currentTabId, key);

      // This should now return undefined.
      testValue1 = await browser.sessions.getTabValue(currentTabId, key);
      browser.test.assertEq(undefined, testValue1, `Key has been deleted and value for key "${key}" should be 'undefined'.`);
    }

    for (let params of tests) {
      await test(params);
    }

    // Attempt to remove a non-existent key, should not throw error.
    let tabs = await browser.tabs.query({currentWindow: true, active: true});
    await browser.sessions.removeTabValue(tabs[0].id, "non-existent-key");
    browser.test.succeed("Attempting to remove a non-existent key should not fail.");

    browser.test.sendMessage("testComplete");
  }

  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      applications: {
        gecko: {
          id: "exampleextension@mozilla.org",
        },
      },
      permissions: ["sessions", "tabs"],
    },
    background,
  });

  await extension.startup();

  await extension.awaitMessage("testComplete");
  ok(true, "Testing completed for set/get/deleteTabValue.");

  await extension.unload();
});

add_task(async function test_sessions_tab_value_persistence() {
  info("Testing for persistence of set tab values.");

  async function background() {
    let key = "tabkey1";
    let value1 = "Tab Value 1a";
    let value2 = "Tab Value 1b";

    browser.test.log("Test that two different tabs hold different values for a given key.");

    await browser.tabs.create({url: "http://example.com"});

    // Wait until the newly created tab has completed loading or it will still have
    // about:blank url when it gets removed and will not appear in the removed tabs history.
    browser.webNavigation.onCompleted.addListener(async function newTabListener(details) {
      browser.webNavigation.onCompleted.removeListener(newTabListener);

      let tabs = await browser.tabs.query({currentWindow: true});

      let tabId_1 = tabs[0].id;
      let tabId_2 = tabs[1].id;

      browser.sessions.setTabValue(tabId_1, key, value1);
      browser.sessions.setTabValue(tabId_2, key, value2);

      let testValue1 = await browser.sessions.getTabValue(tabId_1, key);
      let testValue2 = await browser.sessions.getTabValue(tabId_2, key);

      browser.test.assertEq(value1, testValue1, `Value for key '${key}' should be '${value1}'.`);
      browser.test.assertEq(value2, testValue2, `Value for key '${key}' should be '${value2}'.`);

      browser.test.log("Test that value is copied to duplicated tab for a given key.");

      let duptab = await browser.tabs.duplicate(tabId_2);
      let tabId_3 = duptab.id;

      let testValue3 = await browser.sessions.getTabValue(tabId_3, key);

      browser.test.assertEq(value2, testValue3, `Value for key '${key}' should be '${value2}'.`);

      browser.test.log("Test that restored tab still holds the value for a given key.");

      await browser.tabs.remove([tabId_3]);

      let sessions = await browser.sessions.getRecentlyClosed({maxResults: 1});

      let sessionData = await browser.sessions.restore(sessions[0].tab.sessionId);
      let restoredId = sessionData.tab.id;

      let testValue = await browser.sessions.getTabValue(restoredId, key);

      browser.test.assertEq(value2, testValue, `Value for key '${key}' should be '${value2}'.`);

      await browser.tabs.remove(tabId_2);
      await browser.tabs.remove(restoredId);

      browser.test.sendMessage("testComplete");
    }, {url: [{hostContains: "example.com"}]});
  }

  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      applications: {
        gecko: {
          id: "exampleextension@mozilla.org",
        },
      },
      permissions: ["sessions", "tabs", "webNavigation"],
    },
    background,
  });

  await extension.startup();

  await extension.awaitMessage("testComplete");
  ok(true, "Testing completed for persistance of set tab values.");

  await extension.unload();
});

add_task(async function test_sessions_window_value() {
  info("Testing set/get/deleteWindowValue.");

  async function background() {
    let tests = [{key: "winkey1", value: "Window Value"},
                 {key: "winkey2", value: 25},
                 {key: "winkey3", value: {val: "Window Value"}},
                 {key: "winkey4", value: function() { return null; }}];

    async function test(params) {
      let {key, value} = params;
      let win = await browser.windows.getCurrent();
      let currentWinId = win.id;

      browser.sessions.setWindowValue(currentWinId, key, value);

      let testValue1 = await browser.sessions.getWindowValue(currentWinId, key);
      let valueType = typeof value;

      browser.test.log(`Test that setting, getting and deleting window value behaves properly when value is type "${valueType}"`);

      if (valueType == "string") {
        browser.test.assertEq(value, testValue1, `Value for key '${key}' should be '${value}'.`);
        browser.test.assertEq("string", (typeof testValue1), "typeof value should be '${valueType}'.");
      } else if (valueType == "number") {
        browser.test.assertEq(value, testValue1, `Value for key '${key}' should be '${value}'.`);
        browser.test.assertEq("number", (typeof testValue1), "typeof value should be '${valueType}'.");
      } else if (valueType == "object") {
        let innerVal1 = value.val;
        let innerVal2 = testValue1.val;
        browser.test.assertEq(innerVal1, innerVal2, `Value for key '${key}' should be '${innerVal1}'.`);
      } else if (valueType == "function") {
        browser.test.assertEq(null, testValue1, `Value for key '${key}' is non-JSON-able and should be 'null'.`);
      }

      // Remove the window key/value.
      browser.sessions.removeWindowValue(currentWinId, key);

      // This should return undefined as the key no longer exists.
      testValue1 = await browser.sessions.getWindowValue(currentWinId, key);
      browser.test.assertEq(undefined, testValue1, `Key has been deleted and value for key '${key}' should be 'undefined'.`);
    }

    for (let params of tests) {
      await test(params);
    }

    // Attempt to remove a non-existent key, should not throw error.
    let win = await browser.windows.getCurrent();
    await browser.sessions.removeWindowValue(win.id, "non-existent-key");
    browser.test.succeed("Attempting to remove a non-existent key should not fail.");

    browser.test.sendMessage("testComplete");
  }

  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      applications: {
        gecko: {
          id: "exampleextension@mozilla.org",
        },
      },
      permissions: ["sessions"],
    },
    background,
  });

  await extension.startup();

  await extension.awaitMessage("testComplete");
  ok(true, "Testing completed for set/get/deleteWindowValue.");

  await extension.unload();
});

add_task(async function test_sessions_window_value_persistence() {
  info("Testing that different values for the same key in different windows are persisted properly.");

  async function background() {
    let key = "winkey1";
    let value1 = "Window Value 1a";
    let value2 = "Window Value 1b";

    let window1 = await browser.windows.getCurrent();
    let window2 = await browser.windows.create({});

    let window1Id = window1.id;
    let window2Id = window2.id;

    browser.sessions.setWindowValue(window1Id, key, value1);
    browser.sessions.setWindowValue(window2Id, key, value2);

    let testValue1 = await browser.sessions.getWindowValue(window1Id, key);
    let testValue2 = await browser.sessions.getWindowValue(window2Id, key);

    browser.test.assertEq(value1, testValue1, `Value for key '${key}' should be '${value1}'.`);
    browser.test.assertEq(value2, testValue2, `Value for key '${key}' should be '${value2}'.`);

    await browser.windows.remove(window2Id);
    browser.test.sendMessage("testComplete");
  }

  let extension = ExtensionTestUtils.loadExtension({
    manifest: {
      applications: {
        gecko: {
          id: "exampleextension@mozilla.org",
        },
      },
      permissions: ["sessions"],
    },
    background,
  });

  await extension.startup();

  await extension.awaitMessage("testComplete");
  ok(true, "Testing completed for persistance of set window values.");

  await extension.unload();
});

