/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

// Test that console command input is persisted across toolbox loads.
// See Bug 943306.

"use strict";

requestLongerTimeout(2);

const TEST_URI = "data:text/html;charset=utf-8,Web Console test for persisting history";
const INPUT_HISTORY_COUNT = 10;

const {
  getHistoryEntries,
} = require("devtools/client/webconsole/selectors/history");

add_task(async function() {
  // Run test with legacy JsTerm
  await pushPref("devtools.webconsole.jsterm.codeMirror", false);
  await testHistory();
  // And then run it with the CodeMirror-powered one.
  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
  await testHistory();
});

async function testHistory() {
  info("Setting custom input history pref to " + INPUT_HISTORY_COUNT);
  Services.prefs.setIntPref("devtools.webconsole.inputHistoryCount", INPUT_HISTORY_COUNT);

  // First tab: run a bunch of commands and then make sure that you can
  // navigate through their history.
  const hud1 = await openNewTabAndConsole(TEST_URI);
  let state1 = hud1.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state1)),
     "[]",
     "No history on first tab initially");
  await populateInputHistory(hud1);

  state1 = hud1.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state1)),
     '["0","1","2","3","4","5","6","7","8","9"]',
     "First tab has populated history");

  // Second tab: Just make sure that you can navigate through the history
  // generated by the first tab.
  const hud2 = await openNewTabAndConsole(TEST_URI, false);
  let state2 = hud2.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state2)),
     '["0","1","2","3","4","5","6","7","8","9"]',
     "Second tab has populated history");
  await testNavigatingHistoryInUI(hud2);

  state2 = hud2.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state2)),
    '["0","1","2","3","4","5","6","7","8","9"]',
    "An empty entry has been added in the second tab due to history perusal");
  is(state2.history.originalUserValue, "",
     "An empty value has been stored as the current input value");

  // Third tab: Should have the same history as first tab, but if we run a
  // command, then the history of the first and second shouldn't be affected
  const hud3 = await openNewTabAndConsole(TEST_URI, false);
  let state3 = hud3.ui.consoleOutput.getStore().getState();

  is(JSON.stringify(getHistoryEntries(state3)),
     '["0","1","2","3","4","5","6","7","8","9"]',
     "Third tab has populated history");

  // Set input value separately from execute so UP arrow accurately navigates
  // history.
  hud3.jsterm.setInputValue('"hello from third tab"');
  await hud3.jsterm.execute();

  state1 = hud1.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state1)),
     '["0","1","2","3","4","5","6","7","8","9"]',
     "First tab history hasn't changed due to command in third tab");

  state2 = hud2.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state2)),
    '["0","1","2","3","4","5","6","7","8","9"]',
    "Second tab history hasn't changed due to command in third tab");
  is(state2.history.originalUserValue, "",
     "Current input value hasn't changed due to command in third tab");

  state3 = hud3.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state3)),
     '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
     "Third tab has updated history (and purged the first result) after " +
     "running a command");

  // Fourth tab: Should have the latest command from the third tab, followed
  // by the rest of the history from the first tab.
  const hud4 = await openNewTabAndConsole(TEST_URI, false);
  let state4 = hud4.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state4)),
     '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
     "Fourth tab has most recent history");

  await hud4.jsterm.props.clearHistory();
  state4 = hud4.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state4)),
     "[]",
     "Clearing history for a tab works");

  const hud5 = await openNewTabAndConsole(TEST_URI, false);
  const state5 = hud5.ui.consoleOutput.getStore().getState();
  is(JSON.stringify(getHistoryEntries(state5)), "[]",
     "Clearing history carries over to a new tab");

  info("Clearing custom input history pref");
  Services.prefs.clearUserPref("devtools.webconsole.inputHistoryCount");
}

/**
 * Populate the history by running the following commands:
 *  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 */
async function populateInputHistory(hud) {
  const jsterm = hud.jsterm;

  for (let i = 0; i < INPUT_HISTORY_COUNT; i++) {
    // Set input value separately from execute so UP arrow accurately navigates history.
    jsterm.setInputValue(i.toString());
    await jsterm.execute();
  }
}

/**
 * Check pressing up results in history traversal like:
 *  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
 */
function testNavigatingHistoryInUI(hud) {
  const {jsterm} = hud;
  jsterm.focus();

  // Count backwards from original input and make sure that pressing up
  // restores this.
  for (let i = INPUT_HISTORY_COUNT - 1; i >= 0; i--) {
    EventUtils.synthesizeKey("KEY_ArrowUp");
    is(jsterm.getInputValue(), i, "Pressing up restores last input");
  }
}
