async function runSourceMaps(mapsAndScripts) {
    // pre-condition: should only define map or fetchError, not both.
    for (const {map, fetchError} of mapsAndScripts) {
      if (map && fetchError) {
        throw new Error('should only define map or fetchError, not both.');
      }
    }

    const driver = createMockDriver();

    const Scripts = [];

    for (const mapAndScript of mapsAndScripts) {
      const {
        script,
        map,
        status = null,
        resolvedSourceMapUrl,
        fetchError,
      } = mapAndScript;

      Scripts.push(script);

      if (script.sourceMapURL.startsWith('data:')) {
        // Only the source maps that need to be fetched use the `fetchMock` code path.
        continue;
      }

      driver.fetcher.fetchResource.mockImplementationOnce(async (sourceMapUrl) => {
        // Check that the source map url was resolved correctly.
        if (resolvedSourceMapUrl) {
          expect(sourceMapUrl).toBe(resolvedSourceMapUrl);
        }

        if (fetchError) {
          throw new Error(fetchError);
        }

        return {content: map, status};
      });
    }

    const sourceMaps = new SourceMaps();
    return sourceMaps.getArtifact({driver, dependencies: {Scripts}});
  }
`script` mocks the `sourceMapURL` and `url` seen from the protocol.
`map` mocks the (JSON) of the source maps that `Runtime.evaluate` returns.
`resolvedSourceMapUrl` is used to assert that the SourceMaps gatherer is using the expected
                       url to fetch the source map.
`fetchError` mocks an error that happens in the page. Only fetch error message make sense.
@param {Array<{script: LH.Artifacts.Script, map: string, status?: number, resolvedSourceMapUrl?: string, fetchError: string}>} mapsAndScripts
@return {Promise<LH.Artifacts['SourceMaps']>}