"use strict";

var _react = _interopRequireDefault(require("react"));

var _immutable = _interopRequireDefault(require("immutable"));

var _reactDndTestBackend = _interopRequireDefault(require("react-dnd-test-backend"));

var _enzyme = require("enzyme");

var _reactDnd = require("react-dnd");

var _index = require("../index");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

// Sortable component requires a test HTML5 backend to render in unit tests
var Sortable = (0, _reactDnd.DragDropContext)(_reactDndTestBackend["default"])(_index.UnwrappedSortable);

var normalSortableData = _immutable["default"].fromJS([{
  id: 1,
  type: "item",
  text: "Item A"
}, {
  id: 2,
  type: "item",
  text: "Item B"
}, {
  id: 3,
  type: "item",
  text: "Item C"
}, {
  id: 4,
  type: "item",
  text: "Item D"
}, {
  id: 5,
  type: "item",
  text: "Item E"
}]);

var groupedSortableData = _immutable["default"].fromJS([{
  id: 1,
  type: "item",
  text: "Item A"
}, {
  id: 2,
  type: "item",
  text: "Item B"
}, [{
  id: 4,
  type: "item",
  text: "Item C"
}, {
  id: 5,
  type: "item",
  text: "Item D"
}, {
  id: 6,
  type: "item",
  text: "Item E"
}]]);

var renderItem = function renderItem(_ref) {
  var item = _ref.item,
      connectDragSource = _ref.connectDragSource;
  return connectDragSource(_react["default"].createElement("div", null, item.get("text")));
};

describe("components/Sortable", function () {
  var spyObj;
  beforeEach(function () {
    spyObj = {
      changeHandler: function changeHandler() {
        return null;
      }
    };
    spyOn(spyObj, "changeHandler").and.stub();
  });
  describe("Normal Sorting", function () {
    var component; // eslint-disable-line

    beforeEach(function () {
      component = (0, _enzyme.mount)(_react["default"].createElement(Sortable, {
        defaultValue: normalSortableData,
        renderItem: renderItem,
        allowGrouping: false,
        onChange: spyObj.changeHandler
      }));
    });
    it("should render all draggable items in order", function () {
      var itemA = component.find('[data-test-section="sortable-item-0"]');
      expect(itemA.length).toBe(1);
      expect(itemA.text()).toContain(normalSortableData.getIn([0, "text"]));
      var itemB = component.find('[data-test-section="sortable-item-1"]');
      expect(itemB.length).toBe(1);
      expect(itemB.text()).toContain(normalSortableData.getIn([1, "text"]));
      var itemC = component.find('[data-test-section="sortable-item-2"]');
      expect(itemC.length).toBe(1);
      expect(itemC.text()).toContain(normalSortableData.getIn([2, "text"]));
      var itemD = component.find('[data-test-section="sortable-item-3"]');
      expect(itemD.length).toBe(1);
      expect(itemD.text()).toContain(normalSortableData.getIn([3, "text"]));
      var itemE = component.find('[data-test-section="sortable-item-4"]');
      expect(itemE.length).toBe(1);
      expect(itemE.text()).toContain(normalSortableData.getIn([4, "text"]));
    });
    it("should allow items to be dragged and reordered", function () {
      var backend = component.instance().getManager().getBackend();
      var sourceItem = component.find('[data-test-section="sortable-item-0"]').closest("DragSource(SortableItem)");
      expect(sourceItem.length).toBe(1);
      var destinationTarget = component.find('[data-test-section="sortable-target-3"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1);
      backend.simulateBeginDrag([sourceItem.instance().getHandlerId()]);
      backend.simulateHover([destinationTarget.instance().getHandlerId()]);
      backend.simulateDrop();
      backend.simulateEndDrag();
      component.update(); // Expect that the change callback was invoked

      expect(spyObj.changeHandler.calls.count()).toBe(1); // Expect that the callback received a reordered result

      var stateAfterDragging = spyObj.changeHandler.calls.first().args[0];
      expect(stateAfterDragging.size).toBe(normalSortableData.size);
      expect(stateAfterDragging.map(function (item) {
        return item.get("id");
      }).toJS()).toEqual([2, 3, 1, 4, 5]); // Expect that the list was rerendered with the new order

      var itemA = component.find('[data-test-section="sortable-item-2"]');
      expect(itemA.length).toBe(1);
      expect(itemA.text()).toContain(normalSortableData.getIn([0, "text"]));
    });
    it("should render preview of item in drag layer when dragging", function () {
      var backend = component.instance().getManager().getBackend();
      var sourceItem = component.find('[data-test-section="sortable-item-0"]').closest("DragSource(SortableItem)");
      expect(sourceItem.length).toBe(1);
      var destinationTarget = component.find('[data-test-section="sortable-target-3"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1); // Suspend react-dnd in a dragging state and provide fake coordinates

      backend.simulateBeginDrag([sourceItem.instance().getHandlerId()], {
        clientOffset: {
          x: 0,
          y: 0
        },
        getSourceClientOffset: function getSourceClientOffset() {
          return {
            x: 3,
            y: 4
          };
        }
      });
      component.update(); // Expect that the item preview was rerendered

      var previewA = component.find('[data-test-section="sortable-item-preview-0"]');
      expect(previewA.length).toBe(1);
      expect(previewA.text()).toContain(normalSortableData.getIn([0, "text"])); // Expect that the preview was rendered in the correct location

      var previewContainer = previewA.closest(".oui-sortable__drag-preview__item");
      expect(previewContainer.length).toBe(1);
      expect(previewContainer.props().style).toHaveProperty("transform", "translate(3px, 4px)"); // Release dragging state

      backend.simulateDrop();
      backend.simulateEndDrag();
    });
    it("should not reorder if dragging item over its leading target", function () {
      var backend = component.instance().getManager().getBackend(); // Select group containing three items

      var sourceItem = component.find('[data-test-section="sortable-item-1"]').closest("DragSource(SortableItem)");
      expect(sourceItem.length).toBe(1); // Select a target above group

      var destinationTarget = component.find('[data-test-section="sortable-target-1"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1);
      backend.simulateBeginDrag([sourceItem.instance().getHandlerId()]);
      backend.simulateHover([destinationTarget.instance().getHandlerId()]);
      backend.simulateDrop();
      backend.simulateEndDrag();
      component.update(); // Expect that the change callback was invoked

      expect(spyObj.changeHandler.calls.count()).toBe(1); // Expect that the value was not changed

      var editingState = spyObj.changeHandler.calls.first().args[0];
      expect(_immutable["default"].is(editingState, groupedSortableData));
    });
    it("should not reorder if dragging item over its trailing target", function () {
      var backend = component.instance().getManager().getBackend(); // Select group containing three items

      var sourceItem = component.find('[data-test-section="sortable-item-1"]').closest("DragSource(SortableItem)");
      expect(sourceItem.length).toBe(1); // Select a target above group

      var destinationTarget = component.find('[data-test-section="sortable-target-2"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1);
      backend.simulateBeginDrag([sourceItem.instance().getHandlerId()]);
      backend.simulateHover([destinationTarget.instance().getHandlerId()]);
      backend.simulateDrop();
      backend.simulateEndDrag();
      component.update(); // Expect that the change callback was invoked

      expect(spyObj.changeHandler.calls.count()).toBe(1); // Expect that the value was not changed

      var editingState = spyObj.changeHandler.calls.first().args[0];
      expect(_immutable["default"].is(editingState, groupedSortableData));
    });
    it("should convert ungrouped items to groups when enabling grouping", function () {
      // Expect ungrouped item to be rendered when grouping is disabled
      var itemA = component.find('[data-test-section="sortable-item-0"]');
      expect(itemA.length).toBe(1);
      expect(itemA.text()).toContain(normalSortableData.getIn([0, "text"])); // Enable grouping

      component.setProps({
        allowGrouping: true
      });
      component.update(); // Expect that ungrouped items are automatically wrapped in groups

      var groupedItemA = component.find('[data-test-section="sortable-item-0-0"]');
      expect(groupedItemA.length).toBe(1);
      expect(groupedItemA.text()).toContain(normalSortableData.getIn([0, "text"]));
    });
  });
  describe("Grouped Sorting", function () {
    var component; // eslint-disable-line

    beforeEach(function () {
      component = (0, _enzyme.mount)(_react["default"].createElement(Sortable, {
        defaultValue: groupedSortableData,
        renderItem: renderItem,
        allowGrouping: true,
        onChange: spyObj.changeHandler
      }));
    });
    it("should render all items in groups and in order", function () {
      // Automatically created groups (ungrouped items automatically grouped)
      // Group 1: [ itemA ]
      var itemA = component.find('[data-test-section="sortable-item-0-0"]');
      expect(itemA.length).toBe(1);
      expect(itemA.text()).toContain(groupedSortableData.getIn([0, "text"])); // Group 2: [ itemB ]

      var itemB = component.find('[data-test-section="sortable-item-1-0"]');
      expect(itemB.length).toBe(1);
      expect(itemB.text()).toContain(groupedSortableData.getIn([1, "text"])); // Manually created groups (defined in groupedSortableData)
      // Group 3: [ itemC, itemD, itemE ]

      var itemC = component.find('[data-test-section="sortable-item-2-0"]');
      expect(itemC.length).toBe(1);
      expect(itemC.text()).toContain(groupedSortableData.getIn([2, 0, "text"]));
      var itemD = component.find('[data-test-section="sortable-item-2-1"]');
      expect(itemD.length).toBe(1);
      expect(itemD.text()).toContain(groupedSortableData.getIn([2, 1, "text"]));
      var itemE = component.find('[data-test-section="sortable-item-2-2"]');
      expect(itemE.length).toBe(1);
      expect(itemE.text()).toContain(groupedSortableData.getIn([2, 2, "text"]));
    });
    it("should allow items to be reordered within a group", function () {
      var backend = component.instance().getManager().getBackend(); // Select item and target within the same group

      var sourceItem = component.find('[data-test-section="sortable-item-2-0"]').closest("DragSource(SortableItem)");
      expect(sourceItem.length).toBe(1);
      var destinationTarget = component.find('[data-test-section="sortable-target-2-3"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1);
      backend.simulateBeginDrag([sourceItem.instance().getHandlerId()]);
      backend.simulateHover([destinationTarget.instance().getHandlerId()]);
      backend.simulateDrop();
      backend.simulateEndDrag();
      component.update(); // Expect that the change callback was invoked

      expect(spyObj.changeHandler.calls.count()).toBe(1); // Expect that the items in the group were reordered

      var itemC = component.find('[data-test-section="sortable-item-2-0"]');
      expect(itemC.length).toBe(1);
      expect(itemC.text()).toContain(groupedSortableData.getIn([2, 1, "text"]));
      var itemD = component.find('[data-test-section="sortable-item-2-1"]');
      expect(itemD.length).toBe(1);
      expect(itemD.text()).toContain(groupedSortableData.getIn([2, 2, "text"]));
      var itemE = component.find('[data-test-section="sortable-item-2-2"]');
      expect(itemE.length).toBe(1);
      expect(itemE.text()).toContain(groupedSortableData.getIn([2, 0, "text"]));
    });
    it("should allow items to be dragged outside of a group", function () {
      var backend = component.instance().getManager().getBackend(); // Select item in a group and a target outside of that group

      var sourceItem = component.find('[data-test-section="sortable-item-2-2"]').closest("DragSource(SortableItem)");
      expect(sourceItem.length).toBe(1);
      var destinationTarget = component.find('[data-test-section="sortable-target-3"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1);
      backend.simulateBeginDrag([sourceItem.instance().getHandlerId()]);
      backend.simulateHover([destinationTarget.instance().getHandlerId()]);
      backend.simulateDrop();
      backend.simulateEndDrag();
      component.update(); // Expect that the change callback was invoked

      expect(spyObj.changeHandler.calls.count()).toBe(1); // Expect that item was removed from original group

      var itemE = component.find('[data-test-section="sortable-item-2-2"]');
      expect(itemE.length).toBe(0); // Expect that item was moved to new position and within new group

      var movedItemE = component.find('[data-test-section="sortable-item-3-0"]');
      expect(movedItemE.length).toBe(1);
      expect(movedItemE.text()).toContain(groupedSortableData.getIn([2, 2, "text"]));
    });
    it("should allow groups to be reordered", function () {
      var backend = component.instance().getManager().getBackend(); // Select group containing three items

      var sourceGroup = component.find('[data-test-section="sortable-group-2"]').closest("DragSource(SortableGroup)");
      expect(sourceGroup.length).toBe(1); // Select a target above group

      var destinationTarget = component.find('[data-test-section="sortable-target-1"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1);
      backend.simulateBeginDrag([sourceGroup.instance().getHandlerId()]);
      backend.simulateHover([destinationTarget.instance().getHandlerId()]);
      backend.simulateDrop();
      backend.simulateEndDrag();
      component.update(); // Expect that the change callback was invoked

      expect(spyObj.changeHandler.calls.count()).toBe(1); // Expect that the groups were reordered and maintain item order

      var itemC = component.find('[data-test-section="sortable-item-1-0"]');
      expect(itemC.length).toBe(1);
      expect(itemC.text()).toContain(groupedSortableData.getIn([2, 0, "text"]));
      var itemD = component.find('[data-test-section="sortable-item-1-1"]');
      expect(itemD.length).toBe(1);
      expect(itemD.text()).toContain(groupedSortableData.getIn([2, 1, "text"]));
      var itemE = component.find('[data-test-section="sortable-item-1-2"]');
      expect(itemE.length).toBe(1);
      expect(itemE.text()).toContain(groupedSortableData.getIn([2, 2, "text"]));
    });
    it("should not reorder if dragging group over itself", function () {
      var backend = component.instance().getManager().getBackend(); // Select group containing three items

      var sourceGroup = component.find('[data-test-section="sortable-group-2"]').closest("DragSource(SortableGroup)");
      expect(sourceGroup.length).toBe(1); // Select a target above group

      var destinationTarget = component.find('[data-test-section="sortable-target-2"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1);
      backend.simulateBeginDrag([sourceGroup.instance().getHandlerId()]);
      backend.simulateHover([destinationTarget.instance().getHandlerId()]);
      backend.simulateDrop();
      backend.simulateEndDrag();
      component.update(); // Expect that the change callback was invoked

      expect(spyObj.changeHandler.calls.count()).toBe(1); // Expect that the value was not changed

      var editingState = spyObj.changeHandler.calls.first().args[0];
      expect(_immutable["default"].is(editingState.get(2), groupedSortableData.get(2)));
    });
    it("should not reorder if dragging group over another group", function () {
      var backend = component.instance().getManager().getBackend(); // Select group containing three items

      var sourceGroup = component.find('[data-test-section="sortable-group-2"]').closest("DragSource(SortableGroup)");
      expect(sourceGroup.length).toBe(1); // Select a target within a different group

      var destinationTarget = component.find('[data-test-section="sortable-target-1-0"]').closest("DropTarget(SortTarget)");
      expect(destinationTarget.length).toBe(1);
      backend.simulateBeginDrag([sourceGroup.instance().getHandlerId()]);
      backend.simulateHover([destinationTarget.instance().getHandlerId()]);
      backend.simulateDrop();
      backend.simulateEndDrag();
      component.update(); // Expect that the change callback was invoked

      expect(spyObj.changeHandler.calls.count()).toBe(1); // Expect that the value was not changed

      var editingState = spyObj.changeHandler.calls.first().args[0];
      expect(_immutable["default"].is(editingState.get(2), groupedSortableData.get(2)));
    });
    it("should extract all items from groups when grouping is disabled", function () {
      // Expect grouped item to be rendered when grouping is enabled
      var itemA = component.find('[data-test-section="sortable-item-0-0"]');
      expect(itemA.length).toBe(1);
      expect(itemA.text()).toContain(groupedSortableData.getIn([0, "text"])); // Disable grouping

      component.setProps({
        allowGrouping: false
      });
      component.update(); // Expect that grouped items are automatically extracted from groups

      var ungroupedItemA = component.find('[data-test-section="sortable-item-0"]');
      expect(ungroupedItemA.length).toBe(1);
      expect(ungroupedItemA.text()).toContain(groupedSortableData.getIn([0, "text"]));
    });
  });
  describe("when given a className", function () {
    it("appends it to the root element", function () {
      var component = (0, _enzyme.mount)(_react["default"].createElement(Sortable, {
        className: "extra-class",
        defaultValue: normalSortableData,
        renderItem: renderItem,
        allowGrouping: false,
        onChange: spyObj.changeHandler
      }));
      expect(component.prop("className").includes("extra-class")).toBe(true);
    });
  });
  describe("when given an unknown prop", function () {
    it("passes it to the root element", function () {
      // @ts-ignore: `unknownProps` is not part of any typed interface and therefore a good test value.
      var component = (0, _enzyme.mount)(_react["default"].createElement(Sortable, {
        unknownProp: "some value",
        defaultValue: normalSortableData,
        renderItem: renderItem,
        allowGrouping: false,
        onChange: spyObj.changeHandler
      }));
      expect(component).toHaveProp({
        unknownProp: "some value"
      });
    });
  });
});