waitForEnd(mediaElement) {
    this.improveTestSpeed_(mediaElement);

    // The name of what we're waiting for.
    const goalName = 'end of media';

    // The conditions for success.  Don't rely on either time, or the ended
    // flag, or the ended event, specifically.  Any of these is sufficient.
    // This flexibility cuts down on test flake on Safari (currently 14) in
    // particular, where the flag might be set, but the ended event did not
    // fire.  We also see flake on Firefox (currently 99), where test playbacks
    // sometimes stop right before duration is reached.  (Duration 60.01, audio
    // buffered to 60.01, video buffered to exactly 60.)
    const hasEnded = () => {
      return mediaElement.currentTime >= mediaElement.duration - 0.1 ||
          mediaElement.ended;
    };

    if (hasEnded()) {
      return Promise.resolve();
    }

    // Cleanup on timeout.
    let timer = null;
    const cleanup = () => {
      if (timer) {
        timer.stop();
      }
      this.eventManager_.unlisten(mediaElement, 'timeupdate');
      this.eventManager_.unlisten(mediaElement, 'ended');
      this.eventManager_.unlisten(mediaElement, 'durationchange');
    };

    const p = new Promise((resolve) => {
      const check = () => {
        if (hasEnded()) {
          cleanup();
          resolve();
        }
      };

      // In Firefox 99, there appears to be some bug in the browser preventing
      // events from being triggered at the end of the presentation.  So we
      // check for the end once per second.  Without this, StreamingEngine
      // integration tests were failing.
      timer = new shaka.util.Timer(check);
      timer.tickEvery(/* seconds= */ 1);
      this.eventManager_.listen(mediaElement, 'timeupdate', check);
      this.eventManager_.listen(mediaElement, 'ended', check);
      // Some tests on Safari expose a condition where the ended flag can be
      // found in the desired state in a brief window during a durationchange
      // event.
      this.eventManager_.listen(mediaElement, 'durationchange', check);
    });

    return this.waitUntilGeneric_(goalName, p, cleanup, mediaElement);
  }
Wait for the video to end.

@param {!HTMLMediaElement} mediaElement
@return {!Promise}