tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test-helper.js (8409B)


      1 'use strict';
      2 
      3 const DropPosition = Object.freeze({
      4  CENTER: 'center',
      5  RIGHT_SCROLLBAR: 'right_scrollbar',
      6  LEFT_SCROLLBAR: 'left_scrollbar',
      7  HORIZONTAL_SCROLLBAR: 'horizontal_scrollbar',
      8 });
      9 
     10 // This method calculates the center of an element in an iframe in the
     11 // coordinate space of the top frame. We need this because TestDriver doesn't
     12 // support Actions `{origin}`s across two different frames.
     13 const getElemCenterInIframe = (element, iframe) => {
     14  const elemClientRect = element.getBoundingClientRect();
     15  const frameClientRect = iframe.getBoundingClientRect();
     16  const centerX = frameClientRect.left + (elemClientRect.left + elemClientRect
     17    .right) / 2;
     18  const centerY = frameClientRect.top + (elemClientRect.top + elemClientRect
     19    .bottom) / 2;
     20  return [centerX, centerY];
     21 };
     22 
     23 // This is a helper method that moves the pointer to the specified
     24 // position (center or scrollbar) of the element.
     25 const movePointerToPosition = (element, iframe, position, actions) => {
     26  if (position === DropPosition.CENTER) {
     27    return movePointerToCenter(element, iframe, actions);
     28  } else {
     29    return movePointerToScrollbar(element, iframe, position, actions);
     30  }
     31 }
     32 
     33 // This method appends a pointer move action to the `actions` argument that
     34 // moves the pointer to the center of the `element` and returns it.
     35 const movePointerToCenter = (element, iframe, actions) => {
     36  return (iframe == undefined) ? actions.pointerMove(0, 0, {
     37    origin: element
     38  }) : actions.pointerMove(...getElemCenterInIframe(element, iframe))
     39 }
     40 
     41 // Moves the pointer to the center of the specified scrollbar of the element.
     42 const movePointerToScrollbar = (element, iframe, scrollbarPosition, actions) => {
     43 
     44  const thickness = calculateScrollbarThickness();
     45  assert_greater_than(thickness, 0,
     46    'movePointerToScrollbar should not be called when overlay scrollbars are enabled');
     47 
     48  const hasVerticalScrollbar = (element, iframe) => {
     49    if (iframe == undefined) {
     50      return element.scrollHeight > element.clientHeight;
     51    }
     52    // If the element is in an iframe, it will become scrollable if
     53    // its scrollHeight is larger than the containing frame.
     54    return element.scrollHeight > iframe.clientHeight;
     55  };
     56 
     57  const hasHorizontalScrollbar = (element, iframe) => {
     58    if (iframe == undefined) {
     59      return element.scrollWidth > element.clientWidth;
     60    }
     61    // If the element is in an iframe, it will become scrollable if
     62    // its scrollWidth is larger than the containing frame.
     63    return element.scrollWidth > iframe.clientWidth;
     64  };
     65 
     66  // If the element is inside a frame, the tests will attempt to drop over the document's root
     67  // scrollbars. With this in mind, we calculate the scrollbar's position relative to the frame's
     68  // rectangle instead of the inner element.
     69  const rect = iframe ? iframe.getBoundingClientRect() : element.getBoundingClientRect();
     70  let x, y;
     71 
     72  if (scrollbarPosition === DropPosition.LEFT_SCROLLBAR &&
     73      hasVerticalScrollbar(element, iframe)) {
     74    x = rect.left + thickness / 2;
     75    y = rect.top + (rect.height / 2);
     76  } else if (scrollbarPosition === DropPosition.RIGHT_SCROLLBAR &&
     77      hasVerticalScrollbar(element, iframe)) {
     78    x = rect.right - thickness / 2;
     79    y = rect.top + (rect.height / 2);
     80  } else if (scrollbarPosition === DropPosition.HORIZONTAL_SCROLLBAR &&
     81      hasHorizontalScrollbar(element, iframe)) {
     82    // Horizontal scrollbar is positioned at the bottom.
     83    x = rect.left + (rect.width / 2);
     84    y = rect.bottom - thickness / 2;
     85  } else {
     86    throw new Error('Invalid position specified for scrollbar.');
     87  }
     88 
     89  return actions.pointerMove(x, y);
     90 }
     91 
     92 // The dragDropTest function can be used for tests which require the drag and drop movement.
     93 // `dragElement` takes the element that needs to be dragged. `dropElement` and `dropPosition`
     94 // is where you want to drop the `dragElement` on. By default, `dropPosition` is CENTER,
     95 // which means the center of the `dropElement`. And it can also target to the scrollbar of
     96 // `dropElement` (see DropPosition enum). `onDropCallBack` is called on the onDrop handler
     97 // and the test will only pass if this function returns true. Also, if the `dropElement`
     98 // is inside an iframe, use the optional `iframe` parameter to specify an iframe element
     99 // that contains the `dropElement` to ensure that tests with an iframe pass.
    100 function dragDropTest(dragElement, dropElement, onDropCallBack, testDescription,
    101  dragIframe = undefined, dropIframe = undefined, dropPosition = DropPosition.CENTER) {
    102  // Only verifies drop on scrollbar tests if non-overlay scrollbar is present.
    103  // Skips the test on platforms with overlay scrollbars.
    104  if (dropPosition !== DropPosition.CENTER && calculateScrollbarThickness() <= 0) {
    105    promise_test(async () => {
    106    }, testDescription + ' (skipped - no scrollbars)');
    107    return;
    108  }
    109  promise_test((t) => new Promise(async (resolve, reject) => {
    110    dropElement.addEventListener('drop', t.step_func((event) => {
    111      if (onDropCallBack(event) == true) {
    112        resolve();
    113      } else {
    114        reject();
    115      }
    116    }));
    117    try {
    118      var actions = new test_driver.Actions();
    119      actions = movePointerToCenter(dragElement, dragIframe, actions)
    120        .pointerDown();
    121      actions = movePointerToPosition(dropElement, dropIframe, dropPosition, actions)
    122        .pointerUp();
    123      await actions.send();
    124    } catch (e) {
    125      reject(e);
    126    }
    127  }, testDescription));
    128 }
    129 
    130 // Similar to `dragDropTest`, but instead of listening to the `drop` event on the
    131 // `dropElement`, this function listens to `dragend` on the `dragElement`.
    132 function dragEndTest(dragElement, dropElement, onDropCallBack, testDescription,
    133  dragIframe = undefined, dropIframe = undefined) {
    134  promise_test((t) => new Promise(async (resolve, reject) => {
    135    dragElement.addEventListener('dragend', t.step_func((event) => {
    136      if (onDropCallBack(event) == true) {
    137        resolve();
    138      } else {
    139        reject();
    140      }
    141    }));
    142    try {
    143      var actions = new test_driver.Actions();
    144      actions = movePointerToCenter(dragElement, dragIframe, actions)
    145        .pointerDown();
    146      actions = movePointerToCenter(dropElement, dropIframe, actions)
    147        .pointerUp();
    148      await actions.send();
    149    } catch (e) {
    150      reject(e);
    151    }
    152  }, testDescription));
    153 }
    154 
    155 // The dragDropTestNoDropEvent function performs a drag-and-drop test but expects
    156 // no drop event to occur. This is useful for testing scenarios where drag-and-drop
    157 // should be blocked or ignored (e.g., dropping on root scrollbars). The test
    158 // passes if no drop event fires within the timeout period, and fails immediately
    159 // if any drop event occurs.
    160 function dragDropTestNoDropEvent(dragElement, dropElement, testDescription,
    161  dragIframe = undefined, dropIframe = undefined, dropPosition = DropPosition.CENTER) {
    162  // Only verifies drop on scrollbar tests if non-overlay scrollbar is present.
    163  // Skips the test on platforms with overlay scrollbars.
    164  if (dropPosition !== DropPosition.CENTER && calculateScrollbarThickness() <= 0) {
    165    promise_test(async () => {
    166    }, testDescription + ' (skipped - no scrollbars)');
    167    return;
    168  }
    169  promise_test((t) => new Promise(async (resolve, reject) => {
    170    let dropEvent = false;
    171 
    172    dropElement.addEventListener('drop', t.step_func((event) => {
    173      dropEvent = true;
    174      reject(new Error('Drop event should not have fired'));
    175    }));
    176 
    177    try {
    178      var actions = new test_driver.Actions();
    179      actions = movePointerToCenter(dragElement, dragIframe, actions)
    180        .pointerDown();
    181      actions = movePointerToPosition(dropElement, dropIframe, dropPosition, actions)
    182        .pointerUp();
    183      await actions.send();
    184 
    185      if (!dropEvent) {
    186        resolve();
    187      }
    188    } catch (e) {
    189      reject(e);
    190    }
    191  }, testDescription));
    192 }
    193 
    194 const calculateScrollbarThickness = () => {
    195  var container = document.createElement("div");
    196  container.style.width = "100px";
    197  container.style.height = "100px";
    198  container.style.position = "absolute";
    199  container.style.visibility = "hidden";
    200  container.style.overflow = "auto";
    201 
    202  document.body.appendChild(container);
    203 
    204  var widthBefore = container.clientWidth;
    205  var longContent = document.createElement("div");
    206  longContent.style.height = "1000px";
    207  container.appendChild(longContent);
    208 
    209  var widthAfter = container.clientWidth;
    210 
    211  container.remove();
    212 
    213  return widthBefore - widthAfter;
    214 }