chromium/headless/test/data/protocol/helpers/frame-navigation-helper.js

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

/**
 * A helper class to keep track of renderer tests frame navigations.
 */
(class FrameNavigationHelper {
  /**
   * @param {!TestRunner} testRunner Host TestRunner instance.
   * @param {!Proxy} dp DevTools session protocol instance.
   * @param {!Page} page TestRunner.Page instance.
   */
  constructor(testRunner, dp, page) {
    this.testRunner_ = testRunner;
    this.dp_ = dp;
    this.page_ = page;
    this.frames_ = new Map();
    this.scheduledNavigations_ = new Map();
    this.state_ = 'INIT';
    this.mainFrameId_ = null;
  }

  /**
   * Navigates to the specified url.
   *
   * @param {!string} Url to navigate to.
   */
  async navigate(url) {
    this.state_ = 'STARTING';
    await this.dp_.Page.navigate({url});
  }

  /**
   * Initializes the helper returning reference to itself to allow assignment.
   *
   * @return {!object} FrameNavigationHelper reference.
   */
  async init() {
    await this.dp_.Page.enable();

    await this.dp_.Page.onFrameStartedLoading(event => {
      if (this.state_ === 'STARTING') {
        this.state_ = 'LOADING';
        this.mainFrameId_ = event.params.frameId;
      }
    });

    await this.dp_.Page.onLoadEventFired(event => {
      if (this.state_ === 'STARTING' || this.state_ === 'LOADING') {
        this.state_ = 'RENDERING';
      }
    });

    await this.dp_.Page.onFrameNavigated(event => {
      const frameId = event.params.frame.id;
      let value = this.frames_.get(frameId) || [];
      value.push(event.params.frame);
      this.frames_.set(frameId, value);
    });

    await this.dp_.Page.onFrameScheduledNavigation(event => {
      const frameId = event.params.frameId;
      const reason = event.params.reason;
      const url = event.params.url;
      let value = this.scheduledNavigations_.get(frameId) || [];
      value.push({url, reason});
      this.scheduledNavigations_.set(frameId, value);
    });

    return this;
  }

  /**
   * Logs navigated frames.
   */
  logFrames() {
    this.testRunner_.log(`Frames: ${this.frames_.size}`);
    for (const [frameId, frames] of this.frames_.entries()) {
      this.testRunner_.log(` frameId=${this.getFrameId_(frameId)}`);
      for (const frame of frames) {
        this.testRunner_.log(`  url=${frame.url}`);
      }
    }
  }

  /**
   * Logs scheduled navigations.
   */
  logScheduledNavigations() {
    this.testRunner_.log(
        `ScheduledNavigations: ${this.scheduledNavigations_.size}`);
    for (const [frameId, navs] of this.scheduledNavigations_.entries()) {
      this.testRunner_.log(` frameId=${this.getFrameId_(frameId)}`);
      for (const nav of navs) {
        this.testRunner_.log(`  url=${nav.url} reason=${nav.reason}`);
      }
    }
  }

  getFrameId_(frameId) {
    return frameId === this.mainFrameId_ ? 'MainFrame' : `<${typeof frameId}>`
  }

});