chromium/chrome/test/data/webui/cr_components/theme_color_picker/theme_hue_slider_dialog_test.ts

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

import 'chrome://customize-chrome-side-panel.top-chrome/app.js';

import type {ThemeHueSliderDialogElement} from 'chrome://resources/cr_components/theme_color_picker/theme_hue_slider_dialog.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';

suite('CrComponentsThemeHueSliderDialogTest', () => {
  let element: ThemeHueSliderDialogElement;

  setup(() => {
    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    element = document.createElement('cr-theme-hue-slider-dialog');
    document.body.appendChild(element);
  });

  test('SetsUpCrSliderValues', async () => {
    assertEquals(0, element.$.slider.min);
    assertEquals(359, element.$.slider.max);

    element.selectedHue = 200;
    await microtasksFinished();
    assertEquals(200, element.$.slider.value);
  });

  test('UpdatesCrSliderUi', async () => {
    const knobStyle =
        window.getComputedStyle(element.$.slider.$.knob, '::after');
    element.$.slider.value = 200;
    element.$.slider.dispatchEvent(new CustomEvent('cr-slider-value-changed'));
    await microtasksFinished();

    // window.getComputedStyle only returns color values in rgb format, so
    // rgb(0, 170, 255) is manually converted from hsl(200, 100%, 50%).
    assertEquals('rgb(0, 170, 255)', knobStyle.backgroundColor);

    element.$.slider.value = 300;
    element.$.slider.dispatchEvent(new CustomEvent('cr-slider-value-changed'));
    await microtasksFinished();

    // rgb(255, 0, 255) is manually converted from hsl(300, 100%, 50%).
    assertEquals('rgb(255, 0, 255)', knobStyle.backgroundColor);
  });

  test('UpdatesSelectedHue', () => {
    element.selectedHue = 0;

    // Changing the slider itself should not update selected hue.
    element.$.slider.value = 100;
    element.$.slider.dispatchEvent(new CustomEvent('cr-slider-value-changed'));
    assertEquals(0, element.selectedHue);

    // Only on pointerup should the selectedHue change.
    element.$.slider.dispatchEvent(new PointerEvent('pointerup'));
    assertEquals(100, element.selectedHue);

    // Changing the slider itself should not update selected hue.
    element.$.slider.value = 250;
    element.$.slider.dispatchEvent(new CustomEvent('cr-slider-value-changed'));
    assertEquals(100, element.selectedHue);

    // Only on keyup should the selectedHue change.
    element.$.slider.dispatchEvent(new KeyboardEvent('keyup'));
    assertEquals(250, element.selectedHue);
  });

  test('DispatchesSelectedHueChangedEvent', async () => {
    const selectedHueChangedEvent =
        eventToPromise('selected-hue-changed', element);
    element.$.slider.value = 100;
    element.$.slider.dispatchEvent(new PointerEvent('pointerup'));
    const e = await selectedHueChangedEvent;
    assertEquals(100, e.detail.selectedHue);
  });

  test('ShowsAndHides', () => {
    const anchor = document.createElement('div');
    document.body.appendChild(anchor);
    element.showAt(anchor);
    assertTrue(element.$.dialog.open);
    element.hide();
    assertFalse(element.$.dialog.open);
  });

  test('PositionsCorrectly', () => {
    const windowHeight = 1000;
    const dialogWidth = 100;
    const dialogHeight = 200;
    const anchorWidth = 50;
    const anchorHeight = 25;
    const anchorTop = 300;
    const anchorLeft = 400;

    // Force some dimensions to testing is more predictable.
    const anchor = document.createElement('div');
    anchor.style.position = 'fixed';
    anchor.style.top = `${anchorTop}px`;
    anchor.style.height = `${anchorHeight}px`;
    anchor.style.left = `${anchorLeft}px`;
    anchor.style.width = `${anchorWidth}px`;
    element.$.dialog.style.width = `${dialogWidth}px`;
    element.$.dialog.style.height = `${dialogHeight}px`;
    window.innerHeight = windowHeight;

    document.body.appendChild(anchor);
    element.showAt(anchor);

    assertEquals(`${anchorTop + anchorHeight}px`, element.$.dialog.style.top);
    assertEquals(
        `${anchorLeft + anchorWidth - dialogWidth}px`,
        element.$.dialog.style.left);
    element.hide();

    // Test that the top position changes if anchor is near bottom of window.
    const newAnchorTop = windowHeight;
    anchor.style.top = `${newAnchorTop}px`;
    element.showAt(anchor);
    assertEquals(
        `${newAnchorTop - dialogHeight}px`, element.$.dialog.style.top);
  });

  test('HidesWhenClickingOutsideDialog', () => {
    const anchor = document.createElement('div');
    document.body.appendChild(anchor);
    element.showAt(anchor);

    // Clicks within dialog should do nothing.
    element.$.dialog.dispatchEvent(
        new PointerEvent('pointerdown', {composed: true, bubbles: true}));
    assertTrue(element.$.dialog.open);

    // Clicking anywhere outside dialog should close the dialog.
    const externalElement = document.createElement('div');
    document.body.appendChild(externalElement);
    externalElement.dispatchEvent(
        new PointerEvent('pointerdown', {composed: true, bubbles: true}));
    assertFalse(element.$.dialog.open);
  });
});