/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
"use strict";

// Test that when nodes are being deleted in the page, the current selection
// and therefore the markup view, css rule view, computed view, font view,
// box model view, and breadcrumbs, reset accordingly to show the right node

const TEST_PAGE = URL_ROOT +
  "doc_inspector_delete-selected-node-02.html";

add_task(function* () {
  let { inspector } = yield openInspectorForURL(TEST_PAGE);

  yield testManuallyDeleteSelectedNode();
  yield testAutomaticallyDeleteSelectedNode();
  yield testDeleteSelectedNodeContainerFrame();
  yield testDeleteWithNonElementNode();

  function* testManuallyDeleteSelectedNode() {
    info("Selecting a node, deleting it via context menu and checking that " +
          "its parent node is selected and breadcrumbs are updated.");

    yield deleteNodeWithContextMenu("#deleteManually");

    info("Performing checks.");
    yield assertNodeSelectedAndPanelsUpdated("#selectedAfterDelete",
                                             "li#selectedAfterDelete");
  }

  function* testAutomaticallyDeleteSelectedNode() {
    info("Selecting a node, deleting it via javascript and checking that " +
         "its parent node is selected and breadcrumbs are updated.");

    let div = yield getNodeFront("#deleteAutomatically", inspector);
    yield selectNode(div, inspector);

    info("Deleting selected node via javascript.");
    yield inspector.walker.removeNode(div);

    info("Waiting for inspector to update.");
    yield inspector.once("inspector-updated");

    info("Inspector updated, performing checks.");
    yield assertNodeSelectedAndPanelsUpdated("#deleteChildren",
                                             "ul#deleteChildren");
  }

  function* testDeleteSelectedNodeContainerFrame() {
    info("Selecting a node inside iframe, deleting the iframe via javascript " +
         "and checking the parent node of the iframe is selected and " +
         "breadcrumbs are updated.");

    info("Selecting an element inside iframe.");
    let iframe = yield getNodeFront("#deleteIframe", inspector);
    let div = yield getNodeFrontInFrame("#deleteInIframe", iframe, inspector);
    yield selectNode(div, inspector);

    info("Deleting selected node via javascript.");
    yield inspector.walker.removeNode(iframe);

    info("Waiting for inspector to update.");
    yield inspector.once("inspector-updated");

    info("Inspector updated, performing checks.");
    yield assertNodeSelectedAndPanelsUpdated("body", "body");
  }

  function* testDeleteWithNonElementNode() {
    info("Selecting a node, deleting it via context menu and checking that " +
         "its parent node is selected and breadcrumbs are updated " +
         "when the node is followed by a non-element node");

    yield deleteNodeWithContextMenu("#deleteWithNonElement");

    let expectedCrumbs = ["html", "body", "div#deleteToMakeSingleTextNode"];
    yield assertNodeSelectedAndCrumbsUpdated(expectedCrumbs,
                                             Node.TEXT_NODE);

    // Delete node with key, as cannot delete text node with
    // context menu at this time.
    inspector.markup._frame.focus();
    EventUtils.synthesizeKey("VK_DELETE", {});
    yield inspector.once("inspector-updated");

    expectedCrumbs = ["html", "body", "div#deleteToMakeSingleTextNode"];
    yield assertNodeSelectedAndCrumbsUpdated(expectedCrumbs,
                                             Node.ELEMENT_NODE);
  }

  function* deleteNodeWithContextMenu(selector) {
    yield selectNode(selector, inspector);
    let nodeToBeDeleted = inspector.selection.nodeFront;

    info("Getting the node container in the markup view.");
    let container = yield getContainerForSelector(selector, inspector);

    let allMenuItems = openContextMenuAndGetAllItems(inspector, {
      target: container.tagLine,
    });
    let menuItem = allMenuItems.find(item => item.id === "node-menu-delete");

    info("Clicking 'Delete Node' in the context menu.");
    is(menuItem.disabled, false, "delete menu item is enabled");
    menuItem.click();

    // close the open context menu
    EventUtils.synthesizeKey("VK_ESCAPE", {});

    info("Waiting for inspector to update.");
    yield inspector.once("inspector-updated");

    // Since the mutations are sent asynchronously from the server, the
    // inspector-updated event triggered by the deletion might happen before
    // the mutation is received and the element is removed from the
    // breadcrumbs. See bug 1284125.
    if (inspector.breadcrumbs.indexOf(nodeToBeDeleted) > -1) {
      info("Crumbs haven't seen deletion. Waiting for breadcrumbs-updated.");
      yield inspector.once("breadcrumbs-updated");
    }

    return menuItem;
  }

  function* assertNodeSelectedAndCrumbsUpdated(expectedCrumbs,
                                               expectedNodeType) {
    info("Performing checks");
    let actualNodeType = inspector.selection.nodeFront.nodeType;
    is(actualNodeType, expectedNodeType, "The node has the right type");

    let breadcrumbs = inspector.panelDoc.querySelectorAll(
      "#inspector-breadcrumbs .html-arrowscrollbox-inner > *");
    is(breadcrumbs.length, expectedCrumbs.length,
       "Have the correct number of breadcrumbs");
    for (let i = 0; i < breadcrumbs.length; i++) {
      is(breadcrumbs[i].textContent, expectedCrumbs[i],
         "Text content for button " + i + " is correct");
    }
  }

  function* assertNodeSelectedAndPanelsUpdated(selector, crumbLabel) {
    let nodeFront = yield getNodeFront(selector, inspector);
    is(inspector.selection.nodeFront, nodeFront, "The right node is selected");

    let breadcrumbs = inspector.panelDoc.querySelector(
      "#inspector-breadcrumbs .html-arrowscrollbox-inner");
    is(breadcrumbs.querySelector("button[checked=true]").textContent,
       crumbLabel,
       "The right breadcrumb is selected");
  }
});
