text: 'use strict';
  
  /**
    ResizeTestHelper is a framework to test ResizeObserver
    notifications. Use it to make assertions about ResizeObserverEntries.
    This framework is needed because ResizeObservations are
    delivered asynchronously inside the event loop.
  
    Features:
    - can queue multiple notification steps in a test
    - handles timeouts
    - returns Promise that is fulfilled when test completes.
      Use to chain tests (since parallel async ResizeObserver tests
      would conflict if reusing same DOM elements).
  
    Usage:
  
    create ResizeTestHelper for every test.
    Make assertions inside notify, timeout callbacks.
    Start tests with helper.start()
    Chain tests with Promises.
    Counts animation frames, see startCountingRaf
  */
  
  /*
    @param name: test name
    @param steps:
    {
      setup: function(ResizeObserver) {
        // called at the beginning of the test step
        // your observe/resize code goes here
      },
      notify: function(entries, observer) {
        // ResizeObserver callback.
        // Make assertions here.
        // Return true if next step should start on the next event loop.
      },
      timeout: function() {
        // Define this if your test expects to time out.
        // If undefined, timeout is assert_unreached.
      }
    }
  */
  function ResizeTestHelper(name, steps)
  {
      this._name = name;
      this._steps = steps || [];
      this._stepIdx = -1;
      this._harnessTest = null;
      this._observer = new ResizeObserver(this._handleNotification.bind(this));
      this._timeoutBind = this._handleTimeout.bind(this);
      this._nextStepBind = this._nextStep.bind(this);
  }
  
  ResizeTestHelper.TIMEOUT = 100;
  
  ResizeTestHelper.prototype = {
    get _currentStep() {
      return this._steps[this._stepIdx];
    },
  
    _nextStep: function() {
      if (++this._stepIdx == this._steps.length)
        return this._done();
      this._timeoutId = this._harnessTest.step_timeout(
        this._timeoutBind, ResizeTestHelper.TIMEOUT);
      try {
        this._steps[this._stepIdx].setup(this._observer);
      }
      catch(err) {
        this._harnessTest.step(() => {
          assert_unreached("Caught a throw, possible syntax error");
        });
      }
    },
  
    _handleNotification: function(entries) {
      if (this._timeoutId) {
        window.clearTimeout(this._timeoutId);
        delete this._timeoutId;
      }
      this._harnessTest.step(() => {
        try {
          let rafDelay = this._currentStep.notify(entries, this._observer);
          if (rafDelay)
            window.requestAnimationFrame(this._nextStepBind);
          else
            this._nextStep();
        }
        catch(err) {
          this._harnessTest.step(() => {
            throw err;
          });
          // Force to _done() the current test.
          this._done();
        }
      });
    },
  
    _handleTimeout: function() {
      delete this._timeoutId;
      this._harnessTest.step(() => {
        if (this._currentStep.timeout) {
          this._currentStep.timeout();
        }
        else {
          this._harnessTest.step(() => {
            assert_unreached("Timed out waiting for notification. (" + ResizeTestHelper.TIMEOUT + "ms)");
          });
        }
        this._nextStep();
      });
    },
  
    _done: function() {
      this._observer.disconnect();
      delete this._observer;
      this._harnessTest.done();
      if (this._rafCountRequest) {
        window.cancelAnimationFrame(this._rafCountRequest);
        delete this._rafCountRequest;
      }
      window.requestAnimationFrame(() => { this._resolvePromise(); });
    },
  
    start: function(cleanup) {
      this._harnessTest = async_test(this._name);
  
      if (cleanup) {
        this._harnessTest.add_cleanup(cleanup);
      }
  
      this._harnessTest.step(() => {
        assert_equals(this._stepIdx, -1, "start can only be called once");
        this._nextStep();
      });
      return new Promise( (resolve, reject) => {
        this._resolvePromise = resolve;
        this._rejectPromise = reject;
      });
    },
  
    get rafCount() {
      if (!this._rafCountRequest)
        throw "rAF count is not active";
      return this._rafCount;
    },
  
    get test() {
      if (!this._harnessTest) {
        throw "_harnessTest is not initialized";
      }
      return this._harnessTest;
    },
  
    _incrementRaf: function() {
      if (this._rafCountRequest) {
        this._rafCount++;
        this._rafCountRequest = window.requestAnimationFrame(this._incrementRafBind);
      }
    },
  
    startCountingRaf: function() {
      if (this._rafCountRequest)
        window.cancelAnimationFrame(this._rafCountRequest);
      if (!this._incrementRafBind)
        this._incrementRafBind = this._incrementRaf.bind(this);
      this._rafCount = 0;
      this._rafCountRequest = window.requestAnimationFrame(this._incrementRafBind);
    }
  }
  
  function createAndAppendElement(tagName, parent) {
    if (!parent) {
      parent = document.body;
    }
    const element = document.createElement(tagName);
    parent.appendChild(element);
    return element;
  }
  