'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
exports.default = void 0;
function path() {
  const data = _interopRequireWildcard(require('path'));
  path = function () {
    return data;
  };
  return data;
}
function _v8Coverage() {
  const data = require('@bcoe/v8-coverage');
  _v8Coverage = function () {
    return data;
  };
  return data;
}
function _chalk() {
  const data = _interopRequireDefault(require('chalk'));
  _chalk = function () {
    return data;
  };
  return data;
}
function _glob() {
  const data = _interopRequireDefault(require('glob'));
  _glob = function () {
    return data;
  };
  return data;
}
function fs() {
  const data = _interopRequireWildcard(require('graceful-fs'));
  fs = function () {
    return data;
  };
  return data;
}
function _istanbulLibCoverage() {
  const data = _interopRequireDefault(require('istanbul-lib-coverage'));
  _istanbulLibCoverage = function () {
    return data;
  };
  return data;
}
function _istanbulLibReport() {
  const data = _interopRequireDefault(require('istanbul-lib-report'));
  _istanbulLibReport = function () {
    return data;
  };
  return data;
}
function _istanbulLibSourceMaps() {
  const data = _interopRequireDefault(require('istanbul-lib-source-maps'));
  _istanbulLibSourceMaps = function () {
    return data;
  };
  return data;
}
function _istanbulReports() {
  const data = _interopRequireDefault(require('istanbul-reports'));
  _istanbulReports = function () {
    return data;
  };
  return data;
}
function _v8ToIstanbul() {
  const data = _interopRequireDefault(require('v8-to-istanbul'));
  _v8ToIstanbul = function () {
    return data;
  };
  return data;
}
function _jestUtil() {
  const data = require('jest-util');
  _jestUtil = function () {
    return data;
  };
  return data;
}
function _jestWorker() {
  const data = require('jest-worker');
  _jestWorker = function () {
    return data;
  };
  return data;
}
var _BaseReporter = _interopRequireDefault(require('./BaseReporter'));
var _getWatermarks = _interopRequireDefault(require('./getWatermarks'));
function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : {default: obj};
}
function _getRequireWildcardCache(nodeInterop) {
  if (typeof WeakMap !== 'function') return null;
  var cacheBabelInterop = new WeakMap();
  var cacheNodeInterop = new WeakMap();
  return (_getRequireWildcardCache = function (nodeInterop) {
    return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
  })(nodeInterop);
}
function _interopRequireWildcard(obj, nodeInterop) {
  if (!nodeInterop && obj && obj.__esModule) {
    return obj;
  }
  if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
    return {default: obj};
  }
  var cache = _getRequireWildcardCache(nodeInterop);
  if (cache && cache.has(obj)) {
    return cache.get(obj);
  }
  var newObj = {};
  var hasPropertyDescriptor =
    Object.defineProperty && Object.getOwnPropertyDescriptor;
  for (var key in obj) {
    if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
      var desc = hasPropertyDescriptor
        ? Object.getOwnPropertyDescriptor(obj, key)
        : null;
      if (desc && (desc.get || desc.set)) {
        Object.defineProperty(newObj, key, desc);
      } else {
        newObj[key] = obj[key];
      }
    }
  }
  newObj.default = obj;
  if (cache) {
    cache.set(obj, newObj);
  }
  return newObj;
}
/**
 * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

const FAIL_COLOR = _chalk().default.bold.red;
const RUNNING_TEST_COLOR = _chalk().default.bold.dim;
class CoverageReporter extends _BaseReporter.default {
  _context;
  _coverageMap;
  _globalConfig;
  _sourceMapStore;
  _v8CoverageResults;
  static filename = __filename;
  constructor(globalConfig, context) {
    super();
    this._context = context;
    this._coverageMap = _istanbulLibCoverage().default.createCoverageMap({});
    this._globalConfig = globalConfig;
    this._sourceMapStore =
      _istanbulLibSourceMaps().default.createSourceMapStore();
    this._v8CoverageResults = [];
  }
  onTestResult(_test, testResult) {
    if (testResult.v8Coverage) {
      this._v8CoverageResults.push(testResult.v8Coverage);
      return;
    }
    if (testResult.coverage) {
      this._coverageMap.merge(testResult.coverage);
    }
  }
  async onRunComplete(testContexts, aggregatedResults) {
    await this._addUntestedFiles(testContexts);
    const {map, reportContext} = await this._getCoverageResult();
    try {
      const coverageReporters = this._globalConfig.coverageReporters || [];
      if (!this._globalConfig.useStderr && coverageReporters.length < 1) {
        coverageReporters.push('text-summary');
      }
      coverageReporters.forEach(reporter => {
        let additionalOptions = {};
        if (Array.isArray(reporter)) {
          [reporter, additionalOptions] = reporter;
        }
        _istanbulReports()
          .default.create(reporter, {
            maxCols: process.stdout.columns || Infinity,
            ...additionalOptions
          })
          .execute(reportContext);
      });
      aggregatedResults.coverageMap = map;
    } catch (e) {
      console.error(
        _chalk().default.red(`
        Failed to write coverage reports:
        ERROR: ${e.toString()}
        STACK: ${e.stack}
      `)
      );
    }
    this._checkThreshold(map);
  }
  async _addUntestedFiles(testContexts) {
    const files = [];
    testContexts.forEach(context => {
      const config = context.config;
      if (
        this._globalConfig.collectCoverageFrom &&
        this._globalConfig.collectCoverageFrom.length
      ) {
        context.hasteFS
          .matchFilesWithGlob(
            this._globalConfig.collectCoverageFrom,
            config.rootDir
          )
          .forEach(filePath =>
            files.push({
              config,
              path: filePath
            })
          );
      }
    });
    if (!files.length) {
      return;
    }
    if (_jestUtil().isInteractive) {
      process.stderr.write(
        RUNNING_TEST_COLOR('Running coverage on untested files...')
      );
    }
    let worker;
    if (this._globalConfig.maxWorkers <= 1) {
      worker = require('./CoverageWorker');
    } else {
      worker = new (_jestWorker().Worker)(require.resolve('./CoverageWorker'), {
        exposedMethods: ['worker'],
        forkOptions: {
          serialization: 'json'
        },
        maxRetries: 2,
        numWorkers: this._globalConfig.maxWorkers
      });
    }
    const instrumentation = files.map(async fileObj => {
      const filename = fileObj.path;
      const config = fileObj.config;
      const hasCoverageData = this._v8CoverageResults.some(v8Res =>
        v8Res.some(innerRes => innerRes.result.url === filename)
      );
      if (
        !hasCoverageData &&
        !this._coverageMap.data[filename] &&
        'worker' in worker
      ) {
        try {
          const result = await worker.worker({
            config,
            context: {
              changedFiles:
                this._context.changedFiles &&
                Array.from(this._context.changedFiles),
              sourcesRelatedToTestsInChangedFiles:
                this._context.sourcesRelatedToTestsInChangedFiles &&
                Array.from(this._context.sourcesRelatedToTestsInChangedFiles)
            },
            globalConfig: this._globalConfig,
            path: filename
          });
          if (result) {
            if (result.kind === 'V8Coverage') {
              this._v8CoverageResults.push([
                {
                  codeTransformResult: undefined,
                  result: result.result
                }
              ]);
            } else {
              this._coverageMap.addFileCoverage(result.coverage);
            }
          }
        } catch (error) {
          console.error(
            _chalk().default.red(
              [
                `Failed to collect coverage from ${filename}`,
                `ERROR: ${error.message}`,
                `STACK: ${error.stack}`
              ].join('\n')
            )
          );
        }
      }
    });
    try {
      await Promise.all(instrumentation);
    } catch {
      // Do nothing; errors were reported earlier to the console.
    }
    if (_jestUtil().isInteractive) {
      (0, _jestUtil().clearLine)(process.stderr);
    }
    if (worker && 'end' in worker && typeof worker.end === 'function') {
      await worker.end();
    }
  }
  _checkThreshold(map) {
    const {coverageThreshold} = this._globalConfig;
    if (coverageThreshold) {
      function check(name, thresholds, actuals) {
        return ['statements', 'branches', 'lines', 'functions'].reduce(
          (errors, key) => {
            const actual = actuals[key].pct;
            const actualUncovered = actuals[key].total - actuals[key].covered;
            const threshold = thresholds[key];
            if (threshold !== undefined) {
              if (threshold < 0) {
                if (threshold * -1 < actualUncovered) {
                  errors.push(
                    `Jest: Uncovered count for ${key} (${actualUncovered}) ` +
                      `exceeds ${name} threshold (${-1 * threshold})`
                  );
                }
              } else if (actual < threshold) {
                errors.push(
                  `Jest: "${name}" coverage threshold for ${key} (${threshold}%) not met: ${actual}%`
                );
              }
            }
            return errors;
          },
          []
        );
      }
      const THRESHOLD_GROUP_TYPES = {
        GLOB: 'glob',
        GLOBAL: 'global',
        PATH: 'path'
      };
      const coveredFiles = map.files();
      const thresholdGroups = Object.keys(coverageThreshold);
      const groupTypeByThresholdGroup = {};
      const filesByGlob = {};
      const coveredFilesSortedIntoThresholdGroup = coveredFiles.reduce(
        (files, file) => {
          const pathOrGlobMatches = thresholdGroups.reduce(
            (agg, thresholdGroup) => {
              // Preserve trailing slash, but not required if root dir
              // See https://github.com/facebook/jest/issues/12703
              const resolvedThresholdGroup = path().resolve(thresholdGroup);
              const suffix =
                (thresholdGroup.endsWith(path().sep) ||
                  (process.platform === 'win32' &&
                    thresholdGroup.endsWith('/'))) &&
                !resolvedThresholdGroup.endsWith(path().sep)
                  ? path().sep
                  : '';
              const absoluteThresholdGroup = `${resolvedThresholdGroup}${suffix}`;

              // The threshold group might be a path:

              if (file.indexOf(absoluteThresholdGroup) === 0) {
                groupTypeByThresholdGroup[thresholdGroup] =
                  THRESHOLD_GROUP_TYPES.PATH;
                return agg.concat([[file, thresholdGroup]]);
              }

              // If the threshold group is not a path it might be a glob:

              // Note: glob.sync is slow. By memoizing the files matching each glob
              // (rather than recalculating it for each covered file) we save a tonne
              // of execution time.
              if (filesByGlob[absoluteThresholdGroup] === undefined) {
                filesByGlob[absoluteThresholdGroup] = _glob()
                  .default.sync(absoluteThresholdGroup)
                  .map(filePath => path().resolve(filePath));
              }
              if (filesByGlob[absoluteThresholdGroup].indexOf(file) > -1) {
                groupTypeByThresholdGroup[thresholdGroup] =
                  THRESHOLD_GROUP_TYPES.GLOB;
                return agg.concat([[file, thresholdGroup]]);
              }
              return agg;
            },
            []
          );
          if (pathOrGlobMatches.length > 0) {
            return files.concat(pathOrGlobMatches);
          }

          // Neither a glob or a path? Toss it in global if there's a global threshold:
          if (thresholdGroups.indexOf(THRESHOLD_GROUP_TYPES.GLOBAL) > -1) {
            groupTypeByThresholdGroup[THRESHOLD_GROUP_TYPES.GLOBAL] =
              THRESHOLD_GROUP_TYPES.GLOBAL;
            return files.concat([[file, THRESHOLD_GROUP_TYPES.GLOBAL]]);
          }

          // A covered file that doesn't have a threshold:
          return files.concat([[file, undefined]]);
        },
        []
      );
      const getFilesInThresholdGroup = thresholdGroup =>
        coveredFilesSortedIntoThresholdGroup
          .filter(fileAndGroup => fileAndGroup[1] === thresholdGroup)
          .map(fileAndGroup => fileAndGroup[0]);
      function combineCoverage(filePaths) {
        return filePaths
          .map(filePath => map.fileCoverageFor(filePath))
          .reduce((combinedCoverage, nextFileCoverage) => {
            if (combinedCoverage === undefined || combinedCoverage === null) {
              return nextFileCoverage.toSummary();
            }
            return combinedCoverage.merge(nextFileCoverage.toSummary());
          }, undefined);
      }
      let errors = [];
      thresholdGroups.forEach(thresholdGroup => {
        switch (groupTypeByThresholdGroup[thresholdGroup]) {
          case THRESHOLD_GROUP_TYPES.GLOBAL: {
            const coverage = combineCoverage(
              getFilesInThresholdGroup(THRESHOLD_GROUP_TYPES.GLOBAL)
            );
            if (coverage) {
              errors = errors.concat(
                check(
                  thresholdGroup,
                  coverageThreshold[thresholdGroup],
                  coverage
                )
              );
            }
            break;
          }
          case THRESHOLD_GROUP_TYPES.PATH: {
            const coverage = combineCoverage(
              getFilesInThresholdGroup(thresholdGroup)
            );
            if (coverage) {
              errors = errors.concat(
                check(
                  thresholdGroup,
                  coverageThreshold[thresholdGroup],
                  coverage
                )
              );
            }
            break;
          }
          case THRESHOLD_GROUP_TYPES.GLOB:
            getFilesInThresholdGroup(thresholdGroup).forEach(
              fileMatchingGlob => {
                errors = errors.concat(
                  check(
                    fileMatchingGlob,
                    coverageThreshold[thresholdGroup],
                    map.fileCoverageFor(fileMatchingGlob).toSummary()
                  )
                );
              }
            );
            break;
          default:
            // If the file specified by path is not found, error is returned.
            if (thresholdGroup !== THRESHOLD_GROUP_TYPES.GLOBAL) {
              errors = errors.concat(
                `Jest: Coverage data for ${thresholdGroup} was not found.`
              );
            }
          // Sometimes all files in the coverage data are matched by
          // PATH and GLOB threshold groups in which case, don't error when
          // the global threshold group doesn't match any files.
        }
      });

      errors = errors.filter(
        err => err !== undefined && err !== null && err.length > 0
      );
      if (errors.length > 0) {
        this.log(`${FAIL_COLOR(errors.join('\n'))}`);
        this._setError(new Error(errors.join('\n')));
      }
    }
  }
  async _getCoverageResult() {
    if (this._globalConfig.coverageProvider === 'v8') {
      const mergedCoverages = (0, _v8Coverage().mergeProcessCovs)(
        this._v8CoverageResults.map(cov => ({
          result: cov.map(r => r.result)
        }))
      );
      const fileTransforms = new Map();
      this._v8CoverageResults.forEach(res =>
        res.forEach(r => {
          if (r.codeTransformResult && !fileTransforms.has(r.result.url)) {
            fileTransforms.set(r.result.url, r.codeTransformResult);
          }
        })
      );
      const transformedCoverage = await Promise.all(
        mergedCoverages.result.map(async res => {
          const fileTransform = fileTransforms.get(res.url);
          let sourcemapContent = undefined;
          if (
            fileTransform?.sourceMapPath &&
            fs().existsSync(fileTransform.sourceMapPath)
          ) {
            sourcemapContent = JSON.parse(
              fs().readFileSync(fileTransform.sourceMapPath, 'utf8')
            );
          }
          const converter = (0, _v8ToIstanbul().default)(
            res.url,
            fileTransform?.wrapperLength ?? 0,
            fileTransform && sourcemapContent
              ? {
                  originalSource: fileTransform.originalCode,
                  source: fileTransform.code,
                  sourceMap: {
                    sourcemap: {
                      file: res.url,
                      ...sourcemapContent
                    }
                  }
                }
              : {
                  source: fs().readFileSync(res.url, 'utf8')
                }
          );
          await converter.load();
          converter.applyCoverage(res.functions);
          const istanbulData = converter.toIstanbul();
          return istanbulData;
        })
      );
      const map = _istanbulLibCoverage().default.createCoverageMap({});
      transformedCoverage.forEach(res => map.merge(res));
      const reportContext = _istanbulLibReport().default.createContext({
        coverageMap: map,
        dir: this._globalConfig.coverageDirectory,
        watermarks: (0, _getWatermarks.default)(this._globalConfig)
      });
      return {
        map,
        reportContext
      };
    }
    const map = await this._sourceMapStore.transformCoverage(this._coverageMap);
    const reportContext = _istanbulLibReport().default.createContext({
      coverageMap: map,
      dir: this._globalConfig.coverageDirectory,
      sourceFinder: this._sourceMapStore.sourceFinder,
      watermarks: (0, _getWatermarks.default)(this._globalConfig)
    });
    return {
      map,
      reportContext
    };
  }
}
exports.default = CoverageReporter;
