"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var testRunner_exports = {};
__export(testRunner_exports, {
  TestRunner: () => TestRunner,
  TestRunnerEvent: () => TestRunnerEvent,
  runAllTestsWithConfig: () => runAllTestsWithConfig
});
module.exports = __toCommonJS(testRunner_exports);
var import_events = __toESM(require("events"));
var import_fs = __toESM(require("fs"));
var import_path = __toESM(require("path"));
var import_server = require("playwright-core/lib/server");
var import_utils = require("playwright-core/lib/utils");
var import_configLoader = require("../common/configLoader");
var import_fsWatcher = require("../fsWatcher");
var import_teleReceiver = require("../isomorphic/teleReceiver");
var import_gitCommitInfoPlugin = require("../plugins/gitCommitInfoPlugin");
var import_webServerPlugin = require("../plugins/webServerPlugin");
var import_base = require("../reporters/base");
var import_internalReporter = require("../reporters/internalReporter");
var import_compilationCache = require("../transform/compilationCache");
var import_util = require("../util");
var import_reporters = require("./reporters");
var import_tasks = require("./tasks");
var import_lastRun = require("./lastRun");
const TestRunnerEvent = {
  TestFilesChanged: "testFilesChanged",
  RecoverFromStepError: "recoverFromStepError"
};
class TestRunner extends import_events.default {
  constructor(configLocation, configCLIOverrides) {
    super();
    this._watchedProjectDirs = /* @__PURE__ */ new Set();
    this._ignoredProjectOutputs = /* @__PURE__ */ new Set();
    this._watchedTestDependencies = /* @__PURE__ */ new Set();
    this._queue = Promise.resolve();
    this._watchTestDirs = false;
    this._populateDependenciesOnList = false;
    this._recoverFromStepErrors = false;
    this._resumeAfterStepErrors = /* @__PURE__ */ new Map();
    this.configLocation = configLocation;
    this._configCLIOverrides = configCLIOverrides;
    this._watcher = new import_fsWatcher.Watcher((events) => {
      const collector = /* @__PURE__ */ new Set();
      events.forEach((f) => (0, import_compilationCache.collectAffectedTestFiles)(f.file, collector));
      this.emit(TestRunnerEvent.TestFilesChanged, [...collector]);
    });
  }
  async initialize(params) {
    this._watchTestDirs = !!params.watchTestDirs;
    this._populateDependenciesOnList = !!params.populateDependenciesOnList;
    this._recoverFromStepErrors = !!params.recoverFromStepErrors;
  }
  resizeTerminal(params) {
    process.stdout.columns = params.cols;
    process.stdout.rows = params.rows;
    process.stderr.columns = params.cols;
    process.stderr.rows = params.rows;
  }
  hasSomeBrowsers() {
    for (const browserName of ["chromium", "webkit", "firefox"]) {
      try {
        import_server.registry.findExecutable(browserName).executablePathOrDie("javascript");
        return true;
      } catch {
      }
    }
    return false;
  }
  async installBrowsers() {
    const executables = import_server.registry.defaultExecutables();
    await import_server.registry.install(executables, false);
  }
  async runGlobalSetup(userReporters) {
    await this.runGlobalTeardown();
    const reporter = new import_internalReporter.InternalReporter(userReporters);
    const config = await this._loadConfigOrReportError(reporter, this._configCLIOverrides);
    if (!config)
      return { status: "failed" };
    const { status, cleanup } = await (0, import_tasks.runTasksDeferCleanup)(new import_tasks.TestRun(config, reporter), [
      ...(0, import_tasks.createGlobalSetupTasks)(config)
    ]);
    if (status !== "passed")
      await cleanup();
    else
      this._globalSetup = { cleanup };
    return { status };
  }
  async runGlobalTeardown() {
    const globalSetup = this._globalSetup;
    const status = await globalSetup?.cleanup();
    this._globalSetup = void 0;
    return { status };
  }
  async startDevServer(userReporter, mode) {
    await this.stopDevServer();
    const reporter = new import_internalReporter.InternalReporter([userReporter]);
    const config = await this._loadConfigOrReportError(reporter);
    if (!config)
      return { status: "failed" };
    const { status, cleanup } = await (0, import_tasks.runTasksDeferCleanup)(new import_tasks.TestRun(config, reporter), [
      ...(0, import_tasks.createPluginSetupTasks)(config),
      (0, import_tasks.createLoadTask)(mode, { failOnLoadErrors: true, filterOnly: false }),
      (0, import_tasks.createStartDevServerTask)()
    ]);
    if (status !== "passed")
      await cleanup();
    else
      this._devServer = { cleanup };
    return { status };
  }
  async stopDevServer() {
    const devServer = this._devServer;
    const status = await devServer?.cleanup();
    this._devServer = void 0;
    return { status };
  }
  async clearCache(userReporter) {
    const reporter = new import_internalReporter.InternalReporter(userReporter ? [userReporter] : []);
    const config = await this._loadConfigOrReportError(reporter);
    if (!config)
      return { status: "failed" };
    const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [
      ...(0, import_tasks.createPluginSetupTasks)(config),
      (0, import_tasks.createClearCacheTask)(config)
    ]);
    return { status };
  }
  async listFiles(userReporter, projects) {
    const reporter = new import_internalReporter.InternalReporter([userReporter]);
    const config = await this._loadConfigOrReportError(reporter);
    if (!config)
      return { status: "failed" };
    config.cliProjectFilter = projects?.length ? projects : void 0;
    const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [
      (0, import_tasks.createListFilesTask)(),
      (0, import_tasks.createReportBeginTask)()
    ]);
    return { status };
  }
  async listTests(userReporter, params) {
    let result;
    this._queue = this._queue.then(async () => {
      const { config, status } = await this._innerListTests(userReporter, params);
      if (config)
        await this._updateWatchedDirs(config);
      result = { status };
    }).catch(printInternalError);
    await this._queue;
    return result;
  }
  async _innerListTests(userReporter, params) {
    const overrides = {
      ...this._configCLIOverrides,
      repeatEach: 1,
      retries: 0
    };
    const reporter = new import_internalReporter.InternalReporter([userReporter]);
    const config = await this._loadConfigOrReportError(reporter, overrides);
    if (!config)
      return { status: "failed" };
    config.cliArgs = params.locations || [];
    config.cliGrep = params.grep;
    config.cliGrepInvert = params.grepInvert;
    config.cliProjectFilter = params.projects?.length ? params.projects : void 0;
    config.cliListOnly = true;
    const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [
      (0, import_tasks.createLoadTask)("out-of-process", { failOnLoadErrors: false, filterOnly: false, populateDependencies: this._populateDependenciesOnList }),
      (0, import_tasks.createReportBeginTask)()
    ]);
    return { config, status };
  }
  async _updateWatchedDirs(config) {
    this._watchedProjectDirs = /* @__PURE__ */ new Set();
    this._ignoredProjectOutputs = /* @__PURE__ */ new Set();
    for (const p of config.projects) {
      this._watchedProjectDirs.add(p.project.testDir);
      this._ignoredProjectOutputs.add(p.project.outputDir);
    }
    const result = await resolveCtDirs(config);
    if (result) {
      this._watchedProjectDirs.add(result.templateDir);
      this._ignoredProjectOutputs.add(result.outDir);
    }
    if (this._watchTestDirs)
      await this._updateWatcher(false);
  }
  async _updateWatcher(reportPending) {
    await this._watcher.update([...this._watchedProjectDirs, ...this._watchedTestDependencies], [...this._ignoredProjectOutputs], reportPending);
  }
  async runTests(userReporter, params) {
    let result = { status: "passed" };
    this._queue = this._queue.then(async () => {
      result = await this._innerRunTests(userReporter, params).catch((e) => {
        printInternalError(e);
        return { status: "failed" };
      });
    });
    await this._queue;
    return result;
  }
  async _innerRunTests(userReporter, params) {
    await this.stopTests();
    const overrides = {
      ...this._configCLIOverrides,
      repeatEach: 1,
      retries: 0,
      preserveOutputDir: true,
      reporter: params.reporters ? params.reporters.map((r) => [r]) : void 0,
      use: {
        ...this._configCLIOverrides.use,
        ...params.trace === "on" ? { trace: { mode: "on", sources: false, _live: true } } : {},
        ...params.trace === "off" ? { trace: "off" } : {},
        ...params.video === "on" || params.video === "off" ? { video: params.video } : {},
        ...params.headed !== void 0 ? { headless: !params.headed } : {},
        _optionContextReuseMode: params.reuseContext ? "when-possible" : void 0,
        _optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : void 0
      },
      ...params.updateSnapshots ? { updateSnapshots: params.updateSnapshots } : {},
      ...params.updateSourceMethod ? { updateSourceMethod: params.updateSourceMethod } : {},
      ...params.workers ? { workers: params.workers } : {}
    };
    if (params.trace === "on")
      process.env.PW_LIVE_TRACE_STACKS = "1";
    else
      process.env.PW_LIVE_TRACE_STACKS = void 0;
    const config = await this._loadConfigOrReportError(new import_internalReporter.InternalReporter([userReporter]), overrides);
    if (!config)
      return { status: "failed" };
    config.cliListOnly = false;
    config.cliPassWithNoTests = true;
    config.cliArgs = params.locations || [];
    config.cliGrep = params.grep;
    config.cliGrepInvert = params.grepInvert;
    config.cliProjectFilter = params.projects?.length ? params.projects : void 0;
    config.preOnlyTestFilters = [];
    if (params.testIds) {
      const testIdSet = new Set(params.testIds);
      config.preOnlyTestFilters.push((test) => testIdSet.has(test.id));
    }
    const configReporters = await (0, import_reporters.createReporters)(config, "test", true);
    const reporter = new import_internalReporter.InternalReporter([...configReporters, userReporter]);
    const stop = new import_utils.ManualPromise();
    const tasks = [
      (0, import_tasks.createApplyRebaselinesTask)(),
      (0, import_tasks.createLoadTask)("out-of-process", { filterOnly: true, failOnLoadErrors: false, doNotRunDepsOutsideProjectFilter: true }),
      ...(0, import_tasks.createRunTestsTasks)(config)
    ];
    const testRun = new import_tasks.TestRun(config, reporter);
    testRun.failureTracker.setRecoverFromStepErrorHandler(this._recoverFromStepError.bind(this));
    const run = (0, import_tasks.runTasks)(testRun, tasks, 0, stop).then(async (status) => {
      this._testRun = void 0;
      return status;
    });
    this._testRun = { run, stop };
    return { status: await run };
  }
  async _recoverFromStepError(stepId, error) {
    if (!this._recoverFromStepErrors)
      return { stepId, status: "failed" };
    const recoveryPromise = new import_utils.ManualPromise();
    this._resumeAfterStepErrors.set(stepId, recoveryPromise);
    if (!error?.message || !error?.location)
      return { stepId, status: "failed" };
    this.emit(TestRunnerEvent.RecoverFromStepError, stepId, error.message, error.location);
    const recoveredResult = await recoveryPromise;
    if (recoveredResult.stepId !== stepId)
      return { stepId, status: "failed" };
    return recoveredResult;
  }
  async resumeAfterStepError(params) {
    const recoveryPromise = this._resumeAfterStepErrors.get(params.stepId);
    if (recoveryPromise)
      recoveryPromise.resolve(params);
  }
  async watch(fileNames) {
    this._watchedTestDependencies = /* @__PURE__ */ new Set();
    for (const fileName of fileNames) {
      this._watchedTestDependencies.add(fileName);
      (0, import_compilationCache.dependenciesForTestFile)(fileName).forEach((file) => this._watchedTestDependencies.add(file));
    }
    await this._updateWatcher(true);
  }
  async findRelatedTestFiles(files, userReporter) {
    const errorReporter = (0, import_reporters.createErrorCollectingReporter)(import_base.internalScreen);
    const reporter = new import_internalReporter.InternalReporter(userReporter ? [userReporter, errorReporter] : [errorReporter]);
    const config = await this._loadConfigOrReportError(reporter);
    if (!config)
      return { errors: errorReporter.errors(), testFiles: [] };
    const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [
      ...(0, import_tasks.createPluginSetupTasks)(config),
      (0, import_tasks.createLoadTask)("out-of-process", { failOnLoadErrors: true, filterOnly: false, populateDependencies: true })
    ]);
    if (status !== "passed")
      return { errors: errorReporter.errors(), testFiles: [] };
    return { testFiles: (0, import_compilationCache.affectedTestFiles)(files) };
  }
  async stopTests() {
    this._testRun?.stop?.resolve();
    await this._testRun?.run;
    this._resumeAfterStepErrors.clear();
  }
  async closeGracefully() {
    (0, import_utils.gracefullyProcessExitDoNotHang)(0);
  }
  async _loadConfig(overrides) {
    try {
      const config = await (0, import_configLoader.loadConfig)(this.configLocation, overrides);
      if (!this._plugins) {
        (0, import_webServerPlugin.webServerPluginsForConfig)(config).forEach((p) => config.plugins.push({ factory: p }));
        (0, import_gitCommitInfoPlugin.addGitCommitInfoPlugin)(config);
        this._plugins = config.plugins || [];
      } else {
        config.plugins.splice(0, config.plugins.length, ...this._plugins);
      }
      return { config };
    } catch (e) {
      return { config: null, error: (0, import_util.serializeError)(e) };
    }
  }
  async _loadConfigOrReportError(reporter, overrides) {
    const { config, error } = await this._loadConfig(overrides);
    if (config)
      return config;
    reporter.onConfigure(import_teleReceiver.baseFullConfig);
    reporter.onError(error);
    await reporter.onEnd({ status: "failed" });
    await reporter.onExit();
    return null;
  }
}
function printInternalError(e) {
  console.error("Internal error:", e);
}
async function resolveCtDirs(config) {
  const use = config.config.projects[0].use;
  const relativeTemplateDir = use.ctTemplateDir || "playwright";
  const templateDir = await import_fs.default.promises.realpath(import_path.default.normalize(import_path.default.join(config.configDir, relativeTemplateDir))).catch(() => void 0);
  if (!templateDir)
    return null;
  const outDir = use.ctCacheDir ? import_path.default.resolve(config.configDir, use.ctCacheDir) : import_path.default.resolve(templateDir, ".cache");
  return {
    outDir,
    templateDir
  };
}
async function runAllTestsWithConfig(config) {
  const listOnly = config.cliListOnly;
  (0, import_gitCommitInfoPlugin.addGitCommitInfoPlugin)(config);
  (0, import_webServerPlugin.webServerPluginsForConfig)(config).forEach((p) => config.plugins.push({ factory: p }));
  const reporters = await (0, import_reporters.createReporters)(config, listOnly ? "list" : "test", false);
  const lastRun = new import_lastRun.LastRunReporter(config);
  if (config.cliLastFailed)
    await lastRun.filterLastFailed();
  const reporter = new import_internalReporter.InternalReporter([...reporters, lastRun]);
  const tasks = listOnly ? [
    (0, import_tasks.createLoadTask)("in-process", { failOnLoadErrors: true, filterOnly: false }),
    (0, import_tasks.createReportBeginTask)()
  ] : [
    (0, import_tasks.createApplyRebaselinesTask)(),
    ...(0, import_tasks.createGlobalSetupTasks)(config),
    (0, import_tasks.createLoadTask)("in-process", { filterOnly: true, failOnLoadErrors: true }),
    ...(0, import_tasks.createRunTestsTasks)(config)
  ];
  const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), tasks, config.config.globalTimeout);
  await new Promise((resolve) => process.stdout.write("", () => resolve()));
  await new Promise((resolve) => process.stderr.write("", () => resolve()));
  return status;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  TestRunner,
  TestRunnerEvent,
  runAllTestsWithConfig
});
