chromium/chrome/test/data/webui/cr_elements/cr_collapse_test.ts

// Copyright 2024 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://resources/cr_elements/cr_collapse/cr_collapse.js';

import type {CrCollapseElement} from 'chrome://resources/cr_elements/cr_collapse/cr_collapse.js';
import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {eventToPromise, isVisible} from 'chrome://webui-test/test_util.js';

suite('cr-collapse', function() {
  let collapse: CrCollapseElement;
  let child: HTMLElement;

  setup(function() {
    document.body.innerHTML = getTrustedHTML`
       <cr-collapse>
         <div style="height: 20px">Hello World</div>
       </cr-collapse>`;
    collapse = document.body.querySelector('cr-collapse')!;
    child = document.body.querySelector('div')!;
  });

  function assertCollapsedState() {
    assertFalse(isVisible(collapse));
    assertFalse(isVisible(child));
    assertEquals('0px', window.getComputedStyle(collapse).maxHeight);
  }

  function assertExpandedState() {
    assertTrue(isVisible(collapse));
    assertTrue(isVisible(child));
    assertEquals('none', window.getComputedStyle(collapse).maxHeight);
  }

  test('does not animate on first render', async () => {
    const transitions: string[] = [];
    collapse.addEventListener('transitionend', e => {
      assertEquals('max-height', e.propertyName);
      transitions.push(
          (collapse.computedStyleMap().get('max-height') as CSSKeywordValue)
              .value);
    });

    // 310ms = default animation duration + 10ms.
    await new Promise(resolve => setTimeout(resolve, 310));
    assertEquals(0, transitions.length);
    collapse.opened = true;
    await eventToPromise('transitionend', collapse);
    assertEquals(1, transitions.length);
    assertEquals('none', transitions[0]);
  });

  test('open/close with property', async() => {
    assertFalse(collapse.opened);
    assertCollapsedState();

    collapse.opened = true;
    await eventToPromise('transitionend', collapse);
    assertExpandedState();

    collapse.opened = false;
    await eventToPromise('transitionend', collapse);
    assertCollapsedState();
  });

  test('open/close with methods', async() => {
    assertFalse(collapse.opened);
    assertCollapsedState();

    collapse.show();
    await eventToPromise('transitionend', collapse);
    assertTrue(collapse.opened);
    assertExpandedState();

    collapse.hide();
    await eventToPromise('transitionend', collapse);
    assertFalse(collapse.opened);
    assertCollapsedState();

    collapse.toggle();
    await eventToPromise('transitionend', collapse);
    assertTrue(collapse.opened);
    assertExpandedState();
  });

  // Test that 2-way bindings with Polymer parent elements work.
  test('TwoWayBindingWithPolymerParent', async () => {
    class TestPolymerElement extends PolymerElement {
      static get is() {
        return 'test-polymer-element';
      }

      static get template() {
        return html`
          <cr-collapse opened="{{parentOpened}}" no-animation>
             <div>Some content</div>
          </cr-collapse>`;
      }

      static get properties() {
        return {
          parentOpened: Boolean,
        };
      }

      parentOpened: boolean = false;
    }

    customElements.define(TestPolymerElement.is, TestPolymerElement);

    document.body.innerHTML = window.trustedTypes!.emptyHTML;
    const element =
        document.createElement('test-polymer-element') as TestPolymerElement;
    document.body.appendChild(element);

    const collapse = element.shadowRoot!.querySelector('cr-collapse');
    assertTrue(!!collapse);
    const whenOpenedChanged = eventToPromise('opened-changed', collapse);
    collapse.toggle();
    await whenOpenedChanged;
    assertTrue(element.parentOpened);
  });
});