chromium/ui/file_manager/file_manager/foreground/js/ui/file_tap_handler_unittest.ts

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {assertEquals} from 'chrome://webui-test/chromeos/chai_assert.js';

import {FileTapHandler, TapEvent} from './file_tap_handler.js';

let handler: FileTapHandler;

let dummyTarget: Element;

let events: Array<{index: number, eventType: TapEvent}>;

const handleTap =
    (_e: TouchEvent, index: number, eventType: TapEvent): boolean => {
      events.push({index, eventType});
      return false;
    };

function createTouch(identifier: number, clientX: number, clientY: number) {
  return new Touch({
    identifier,
    clientX,
    clientY,
    target: dummyTarget,
  });
}

export function setUp() {
  handler = new FileTapHandler();
  dummyTarget = document.body;
  events = [];
}

export function testTap() {
  const touch = createTouch(0, 300, 400);

  handler.handleTouchEvents(
      new TouchEvent('touchstart', {
        cancelable: true,
        targetTouches: [touch],
        touches: [touch],
      }),
      0, handleTap);
  // Callback should be called after touchend.
  assertEquals(0, events.length);
  handler.handleTouchEvents(
      new TouchEvent('touchend', {
        cancelable: true,
        targetTouches: [],
        touches: [],
      }),
      0, handleTap);

  // A tap event should be emitted for a single tap.
  assertEquals(1, events.length);
  assertEquals(TapEvent.TAP, events[0]!.eventType);
  assertEquals(0, events[0]!.index);
}

export function testTapMoveTolerance() {
  const touch0 = createTouch(0, 300, 400);
  const touch1 = createTouch(0, 303, 404);  // moved slightly

  handler.handleTouchEvents(
      new TouchEvent('touchstart', {
        cancelable: true,
        changedTouches: [touch0],
        targetTouches: [touch0],
        touches: [touch0],
      }),
      0, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchmove', {
        cancelable: true,
        changedTouches: [touch1],
        targetTouches: [touch1],
        touches: [touch1],
      }),
      1, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchend', {
        cancelable: true,
        changedTouches: [touch1],
        targetTouches: [],
        touches: [],
      }),
      1, handleTap);

  // No tap event should be emitted for a single tap with movement.
  assertEquals(0, events.length);
}

export async function testLongTap() {
  const touch0 = createTouch(0, 300, 400);
  const touch1 = createTouch(0, 300, 400);  // no movement.

  handler.handleTouchEvents(
      new TouchEvent('touchstart', {
        cancelable: true,
        changedTouches: [touch0],
        targetTouches: [touch0],
        touches: [touch0],
      }),
      0, handleTap);
  assertEquals(0, events.length);
  await new Promise(resolve => {
    // Wait for the long press threshold (500ms). No movement.
    setTimeout(resolve, 550);
  });
  // A long press should be emitted if there was no movement.
  assertEquals(1, events.length);
  assertEquals(TapEvent.LONG_PRESS, events[0]!.eventType);
  assertEquals(0, events[0]!.index);
  handler.handleTouchEvents(
      new TouchEvent('touchend', {
        cancelable: true,
        changedTouches: [touch1],
        targetTouches: [],
        touches: [],
      }),
      1, handleTap);
  // A long tap should be emitted if there was no movement.
  assertEquals(2, events.length);
  assertEquals(TapEvent.LONG_TAP, events[1]!.eventType);
  assertEquals(0, events[1]!.index);
}

export async function testLongTapMoveTolerance() {
  const touch0 = createTouch(0, 300, 400);
  const touch1 = createTouch(0, 303, 404);  // moved slightly

  handler.handleTouchEvents(
      new TouchEvent('touchstart', {
        cancelable: true,
        changedTouches: [touch0],
        targetTouches: [touch0],
        touches: [touch0],
      }),
      0, handleTap);
  assertEquals(0, events.length);
  await new Promise(resolve => {
    setTimeout(resolve, 250);
  });
  handler.handleTouchEvents(
      new TouchEvent('touchmove', {
        cancelable: true,
        changedTouches: [touch1],
        targetTouches: [touch1],
        touches: [touch1],
      }),
      0, handleTap);
  await new Promise(resolve => {
    // Exceeds the threshold (500ms) when added with the one above.
    setTimeout(resolve, 300);
  });

  // No tap event should be emitted for a long tap with movement.
  assertEquals(0, events.length);
}

export function testTwoFingerTap() {
  const touch0_0 = createTouch(0, 300, 400);
  const touch0_1 = createTouch(0, 303, 404);
  const touch1_0 = createTouch(1, 350, 400);
  const touch1_1 = createTouch(1, 354, 402);

  // case 1: Release the second touch point first.
  handler.handleTouchEvents(
      new TouchEvent('touchstart', {
        cancelable: true,
        changedTouches: [touch0_0],
        targetTouches: [touch0_0],
        touches: [touch0_0],
      }),
      0, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchstart', {
        cancelable: true,
        changedTouches: [touch1_0],
        targetTouches: [touch0_0, touch1_0],
        touches: [touch0_0, touch1_0],
      }),
      1, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchmove', {
        cancelable: true,
        changedTouches: [touch1_1],
        targetTouches: [touch0_0, touch1_1],
        touches: [touch0_0, touch1_1],
      }),
      1, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchend', {
        cancelable: true,
        changedTouches: [touch1_1],
        targetTouches: [touch0_0],
        touches: [touch0_0],
      }),
      1, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchmove', {
        cancelable: true,
        changedTouches: [touch0_1],
        targetTouches: [touch0_1],
        touches: [touch0_1],
      }),
      1, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchend', {
        cancelable: true,
        changedTouches: [touch0_1],
        targetTouches: [],
        touches: [],
      }),
      2, handleTap);

  // A two-finger tap event should be emitted, allowing for slight movement.
  assertEquals(1, events.length);
  assertEquals(TapEvent.TWO_FINGER_TAP, events[0]!.eventType);
  assertEquals(0, events[0]!.index);

  // case 2: Release the first touch point first.
  handler.handleTouchEvents(
      new TouchEvent('touchstart', {
        cancelable: true,
        changedTouches: [touch0_0],
        targetTouches: [touch0_0],
        touches: [touch0_0],
      }),
      10, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchstart', {
        cancelable: true,
        changedTouches: [touch1_0],
        targetTouches: [touch0_0, touch1_0],
        touches: [touch0_0, touch1_0],
      }),
      11, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchend', {
        cancelable: true,
        changedTouches: [touch0_0],
        targetTouches: [touch1_0],
        touches: [touch1_0],
      }),
      11, handleTap);
  handler.handleTouchEvents(
      new TouchEvent('touchend', {
        cancelable: true,
        changedTouches: [touch1_0],
        targetTouches: [],
        touches: [],
      }),
      10, handleTap);

  // A two-finger tap event should be emitted.
  assertEquals(2, events.length);
  assertEquals(TapEvent.TWO_FINGER_TAP, events[1]!.eventType);
  assertEquals(10, events[1]!.index);
}