function createFakeNetworkEvents(requestId, record, timing) {
    if (!('connectionTiming' in timing)) {
      throw new Error('Network node timing incomplete');
    }

    // 0ms requests get super-messed up rendering
    // Use 0.3ms instead so they're still hoverable, https://github.com/GoogleChrome/lighthouse/pull/5350#discussion_r194563201
    let {startTime, endTime} = timing;
    if (startTime === endTime) endTime += 0.3;

    const requestData = {requestId: requestId.toString(), frame};
    // No `dur` on network instant events but add to keep types happy.
    /** @type {LH.Util.StrictOmit<LH.TraceEvent, 'name'|'ts'|'args'>} */
    const baseRequestEvent = {...baseEvent, ph: 'I', s: 't', dur: 0};

    const sendRequestData = {
      ...requestData,
      requestMethod: record.requestMethod,
      url: record.url,
      priority: record.priority,
    };

    const {dnsResolutionTime, connectionTime, sslTime, timeToFirstByte} = timing.connectionTiming;
    let sslStart = -1;
    let sslEnd = -1;
    if (connectionTime !== undefined && sslTime !== undefined) {
      sslStart = connectionTime - sslTime;
      sslEnd = connectionTime;
    }
    const receiveResponseData = {
      ...requestData,
      statusCode: record.statusCode,
      mimeType: record.mimeType,
      encodedDataLength: record.transferSize,
      fromCache: record.fromDiskCache,
      fromServiceWorker: record.fetchedViaServiceWorker,
      timing: {
        // `requestTime` is in seconds.
        requestTime: toMicroseconds(startTime) / (1000 * 1000),
        // Remaining values are milliseconds after `requestTime`.
        dnsStart: dnsResolutionTime === undefined ? -1 : 0,
        dnsEnd: dnsResolutionTime ?? -1,
        connectStart: dnsResolutionTime ?? -1,
        connectEnd: connectionTime ?? -1,
        sslStart,
        sslEnd,
        sendStart: connectionTime ?? 0,
        sendEnd: connectionTime ?? 0,
        receiveHeadersEnd: timeToFirstByte,
        workerStart: -1,
        workerReady: -1,
        proxyStart: -1,
        proxyEnd: -1,
        pushStart: 0,
        pushEnd: 0,
      },
    };

    const resourceFinishData = {
      requestId: requestId.toString(),
      encodedDataLength: record.transferSize,
      decodedBodyLength: record.resourceSize,
      didFail: !!record.failed,
      finishTime: toMicroseconds(endTime) / (1000 * 1000),
    };

    /** @type {LH.TraceEvent[]} */
    const events = [];

    // Navigation request needs an additional ResourceWillSendRequest event.
    if (requestId === 1) {
      events.push(createWillSendRequestEvent(requestId, record, timing));
    }

    events.push(
      {
        ...baseRequestEvent,
        name: 'ResourceSendRequest',
        ts: toMicroseconds(startTime),
        args: {data: sendRequestData},
      },
      {
        ...baseRequestEvent,
        name: 'ResourceFinish',
        ts: toMicroseconds(endTime),
        args: {data: resourceFinishData},
      }
    );

    if (!record.failed) {
      events.push({
        ...baseRequestEvent,
        name: 'ResourceReceiveResponse',
        // Event `ts` isn't meaningful, so just pick a time.
        ts: toMicroseconds((startTime + endTime) / 2),
        args: {data: receiveResponseData},
      });
    }

    return events;
  }
@param {number} requestId
@param {LH.Artifacts.NetworkRequest} record
@param {CompleteNodeTiming} timing
@return {LH.TraceEvent[]}