chromium/chrome/test/data/webui/cr_elements/cr_icon_instrumented.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 {CrIconElement} from '//resources/cr_elements/cr_icon/cr_icon.js';
import {PromiseResolver} from '//resources/js/promise_resolver.js';
import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';

// A subclass of CrIconElement instrumented to expose a public 'updatedComplete'
// promise that can be leveraged to catch any errors thrown during the updated()
// lifecycle callback, since Lit does not natively expose a good way to catch
// such errors. This subclass doesn't have anything specific to CrIconElement
// and similar approach could be used for any other element. It is implemented
// as a subclass instead of being added to CrIconElement or CrLitElement
// directly to avoid any unnecessary overhead in production, since the
// `updatedComplete` promise is only used in tests.
class CrIconInstrumentedElement extends CrIconElement {
  static override get is() {
    return 'cr-icon-instrumented';
  }

  private updatedCompleteResolver_: PromiseResolver<void> =
      new PromiseResolver();

  // Don't confuse with LitElement's `updateComplete`, note the extra 'd'
  get updatedComplete() {
    return this.updatedCompleteResolver_.promise;
  }

  override shouldUpdate(changedProperties: Map<string, any>) {
    const shouldUpdate = super.shouldUpdate(changedProperties);
    if (shouldUpdate && this.hasUpdated) {
      this.updatedCompleteResolver_ = new PromiseResolver();
    }
    return shouldUpdate;
  }

  override updated(changedProperties: PropertyValues<this>) {
    try {
      super.updated(changedProperties);
      this.updatedCompleteResolver_.resolve();
    } catch (e) {
      this.updatedCompleteResolver_.reject(e);
    }
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'cr-icon-instrumented': CrIconInstrumentedElement;
  }
}

customElements.define(CrIconInstrumentedElement.is, CrIconInstrumentedElement);