/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.module('goog.dom.ControlRangeTest');
goog.setTestOnly();

const DomControlRange = goog.require('goog.dom.ControlRange');
const DomTextRange = goog.require('goog.dom.TextRange');
const RangeType = goog.require('goog.dom.RangeType');
const TagName = goog.require('goog.dom.TagName');
const dom = goog.require('goog.dom');
const testSuite = goog.require('goog.testing.testSuite');
const testingDom = goog.require('goog.testing.dom');
const userAgent = goog.require('goog.userAgent');

let logo;
let table;

function helpTestBounds(range) {
  assertEquals('Start node is logo', logo, range.getStartNode());
  assertEquals('Start offset is 0', 0, range.getStartOffset());
  assertEquals('End node is table', table, range.getEndNode());
  assertEquals('End offset is 1', 1, range.getEndOffset());
}

testSuite({
  setUpPage() {
    logo = dom.getElement('logo');
    table = dom.getElement('table');
  },

  testCreateFromElement() {
    if (!userAgent.IE) {
      return;
    }
    assertNotNull(
        'Control range object can be created for element',
        DomControlRange.createFromElements(logo));
  },

  testCreateFromRange() {
    if (!userAgent.IE) {
      return;
    }
    const range = document.body.createControlRange();
    range.addElement(table);
    assertNotNull(
        'Control range object can be created for element',
        DomControlRange.createFromBrowserRange(range));
  },

  /** @suppress {checkTypes} suppression added to enable type checking */
  testControlRangeIterator() {
    if (!userAgent.IE) {
      return;
    }
    // Each node is included twice - once as a start tag, once as an end.
    const expectedContent = [
      '#logo', '#logo', '#table', '#tbody', '#tr1',   '#td11',  'a', '#td11',
      '#td12', 'b',     '#td12',  '#tr1',   '#tr2',   '#td21',  'c', '#td21',
      '#td22', 'd',     '#td22',  '#tr2',   '#tbody', '#table',
    ];
    testingDom.assertNodesMatch(
        DomControlRange.createFromElements(logo, table), expectedContent, true);
    testingDom.assertNodesMatch(
        DomControlRange.createFromElements(logo, table), expectedContent,
        false);
  },

  testBounds() {
    if (!userAgent.IE) {
      return;
    }

    // Initialize in both orders.
    helpTestBounds(DomControlRange.createFromElements(logo, table));
    helpTestBounds(DomControlRange.createFromElements(table, logo));
  },

  /** @suppress {checkTypes} suppression added to enable type checking */
  testCollapse() {
    if (!userAgent.IE) {
      return;
    }

    const range = DomControlRange.createFromElements(logo, table);
    assertFalse('Not initially collapsed', range.isCollapsed());
    range.collapse();
    assertTrue('Successfully collapsed', range.isCollapsed());
  },

  testGetContainer() {
    if (!userAgent.IE) {
      return;
    }

    let range = DomControlRange.createFromElements(logo);
    assertEquals(
        'Single element range is contained by itself', logo,
        range.getContainer());

    range = DomControlRange.createFromElements(logo, table);
    assertEquals(
        'Two element range is contained by body', document.body,
        range.getContainer());
  },

  /** @suppress {checkTypes} suppression added to enable type checking */
  testSave() {
    if (!userAgent.IE) {
      return;
    }

    let range = DomControlRange.createFromElements(logo, table);
    const savedRange = range.saveUsingDom();

    range.collapse();
    assertTrue('Successfully collapsed', range.isCollapsed());

    range = savedRange.restore();
    assertEquals(
        'Restored a control range', RangeType.CONTROL, range.getType());
    assertFalse('Not collapsed after restore', range.isCollapsed());
    helpTestBounds(range);
  },

  testRemoveContents() {
    if (!userAgent.IE) {
      return;
    }

    const img = dom.createDom(TagName.IMG);
    /**
     * @suppress {strictMissingProperties} suppression added to enable type
     * checking
     */
    img.src = logo.src;

    const div = dom.getElement('test1');
    dom.removeChildren(div);
    div.appendChild(img);
    assertEquals('Div has 1 child', 1, div.childNodes.length);

    const range = DomControlRange.createFromElements(img);
    range.removeContents();
    assertEquals('Div has 0 children', 0, div.childNodes.length);
    assertTrue('Range is collapsed', range.isCollapsed());
  },

  /**
     @suppress {strictMissingProperties} suppression added to enable type
     checking
   */
  testReplaceContents() {
    // Test a control range.
    if (!userAgent.IE) {
      return;
    }

    const outer = dom.getElement('test1');
    outer.innerHTML = '<div contentEditable="true">' +
        'Hello <input type="text" value="World">' +
        '</div>';
    const range = DomControlRange.createFromElements(
        dom.getElementsByTagName(TagName.INPUT, outer)[0]);
    DomControlRange.createFromElements(table);
    range.replaceContentsWithNode(dom.createTextNode('World'));
    assertEquals('Hello World', outer.firstChild.innerHTML);
  },

  testContainsRange() {
    if (!userAgent.IE) {
      return;
    }

    const table2 = dom.getElement('table2');
    const table2td = dom.getElement('table2td');
    const logo2 = dom.getElement('logo2');

    let range = DomControlRange.createFromElements(logo, table);
    let range2 = DomControlRange.createFromElements(logo);
    assertTrue(
        'Control range contains the other control range',
        range.containsRange(range2));
    assertTrue(
        'Control range partially contains the other control range',
        range2.containsRange(range, true));

    range2 = DomControlRange.createFromElements(table2);
    assertFalse(
        'Control range does not contain the other control range',
        range.containsRange(range2));

    range = DomControlRange.createFromElements(table2);
    range2 = DomTextRange.createFromNodeContents(table2td);
    assertTrue(
        'Control range contains text range', range.containsRange(range2));

    range2 = DomTextRange.createFromNodeContents(table);
    assertFalse(
        'Control range does not contain text range',
        range.containsRange(range2));

    range = DomControlRange.createFromElements(logo2);
    range2 = DomTextRange.createFromNodeContents(table2);
    assertFalse(
        'Control range does not fully contain text range',
        range.containsRange(range2, false));

    range2 = DomControlRange.createFromElements(table2);
    assertTrue(
        'Control range contains the other control range (2)',
        range2.containsRange(range));
  },

  /**
     @suppress {strictMissingProperties} suppression added to enable type
     checking
   */
  testCloneRange() {
    if (!userAgent.IE) {
      return;
    }
    const range = DomControlRange.createFromElements(logo);
    assertNotNull('Control range object created for element', range);

    const cloneRange = range.clone();
    assertNotNull('Cloned control range object', cloneRange);
    assertArrayEquals(
        'Control range and clone have same elements', range.getElements(),
        cloneRange.getElements());
  },
});
