chromium/chrome/browser/page_load_metrics/integration_tests/data/resources/util.js

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

// Returns a promise that is resolved when the specified number of animation
// frames has occurred.
async function waitUntilAfterNextLayout() {
  let frameCount = 2;
  return new Promise(resolve => {
    const handleFrame = () => {
      if (--frameCount <= 0)
        resolve();
      else
        requestAnimationFrame(handleFrame);
    };
    requestAnimationFrame(handleFrame);
  });
};

async function observeUntilNumEntries(n, opts) {
  return new Promise((resolve) => {
    const entries = [];
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        entries.push(entry);
      }

      if (entries.length >= n) {
        observer.disconnect();
        resolve(entries);
      }
    });
    observer.observe(opts);
  });
}

async function waitForNumLayoutShiftEntries(num) {
  let entries = await observeUntilNumEntries(num, {
    type: 'layout-shift',
    buffered: true,
  });

  return entries.map((entry) => ({
    startTime: entry.startTime,
    score: entry.value,
    hadRecentInput: entry.hadRecentInput,
  }));
}

const getLcp = async (resource) => {
  return await new Promise(resolve => {
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries().filter(e => e.url.includes(resource));
      if (entries.length > 0) {
        resolve(entries[entries.length - 1].startTime)
      }
    });
    observer.observe({ type: "largest-contentful-paint", buffered: true });
  })
}


const getRequestStart = (name) =>
  getResourceTimingEntry(name).requestStart;

const getResponseEnd = (name) =>
  getResourceTimingEntry(name).responseEnd;

const getStartTime = (name) =>
  getResourceTimingEntry(name).startTime;


const getResourceTimingEntry = (name) => {
  return performance.getEntriesByType('resource').filter(
    e => e.name.includes(name))[0];
}

const addImageWithUrl = async (url) => {
  await new Promise(resolve => {
    const img = document.createElement('img');
    img.addEventListener('load', resolve);
    img.src = url;
    document.body.appendChild(img);
  });
}