import { d as TestContext, o as Group, s as Refiner, t as colors, u as Test } from "./helpers-C1bD1eod.js";
import { i as spec, n as github, r as ndjson, t as dot } from "./main-CjKU6911.js";
import { pathToFileURL } from "node:url";
import { debuglog } from "node:util";
import { join } from "node:path";
import string from "@poppinss/string";
import { glob } from "node:fs/promises";
import Hooks from "@poppinss/hooks";
import getopts from "getopts";
import { isRunningInAIAgent } from "@poppinss/utils";
var debug_default = debuglog("japa:runner");
var Validator = class {
	ensureIsConfigured(config) {
		if (!config) throw new Error(`Cannot run tests. Make sure to call "configure" method before the "run" method`);
	}
	ensureIsInPlanningPhase(phase) {
		if (phase !== "planning") throw new Error(`Cannot import japa test file directly. It must be imported by calling the "japa.run" method`);
	}
	validateSuitesFilter(config) {
		if (!config.filters.suites || !config.filters.suites.length) return;
		if (!("suites" in config) || !config.suites.length) throw new Error(`Cannot apply suites filter. You have not configured any test suites`);
		const suites = config.suites.map(({ name }) => name);
		const unknownSuites = config.filters.suites.filter((suite) => !suites.includes(suite));
		if (unknownSuites.length) throw new Error(`Cannot apply suites filter. "${unknownSuites[0]}" suite is not configured`);
	}
	validateSuitesForUniqueness(config) {
		if (!("suites" in config)) return;
		const suites = /* @__PURE__ */ new Set();
		config.suites.forEach(({ name }) => {
			if (suites.has(name)) throw new Error(`Duplicate suite "${name}"`);
			suites.add(name);
		});
		suites.clear();
	}
	validateActivatedReporters(config) {
		const reportersList = config.reporters.list.map(({ name }) => name);
		const unknownReporters = config.reporters.activated.filter((name) => !reportersList.includes(name));
		if (unknownReporters.length) throw new Error(`Invalid reporter "${unknownReporters[0]}". Make sure to register it first inside the "reporters.list" array`);
	}
};
var validator_default = new Validator();
const FILE_SUFFIX_EXPRESSION = /(\.spec|\.test)?\.[js|ts|jsx|tsx|mjs|mts|cjs|cts]+$/;
var FilesManager = class {
	async getFiles(cwd, files, excludes) {
		if (Array.isArray(files) || typeof files === "string") {
			const matchingFiles = glob(files, {
				withFileTypes: false,
				cwd,
				exclude: excludes
			});
			return (await Array.fromAsync(matchingFiles)).sort((current, next) => {
				return current.localeCompare(next, void 0, {
					numeric: true,
					sensitivity: "base"
				});
			}).map((file) => pathToFileURL(join(cwd, file)));
		}
		return await files();
	}
	grep(files, filters) {
		return files.filter((file) => {
			const filename = string.toUnixSlash(file.pathname);
			const filenameWithoutTestSuffix = filename.replace(FILE_SUFFIX_EXPRESSION, "");
			return !!filters.find((filter) => {
				if (filename.endsWith(filter)) return true;
				const filterSegments = filter.split("/").reverse();
				const fileSegments = filenameWithoutTestSuffix.split("/").reverse();
				return filterSegments.every((segment, index) => {
					return fileSegments[index] && (segment === "*" || fileSegments[index].endsWith(segment));
				});
			});
		});
	}
};
var Planner = class {
	#config;
	#fileManager = new FilesManager();
	constructor(config) {
		validator_default.validateActivatedReporters(config);
		validator_default.validateSuitesFilter(config);
		validator_default.validateSuitesForUniqueness(config);
		this.#config = config;
	}
	#getActivatedReporters() {
		return this.#config.reporters.activated.map((activated) => {
			return this.#config.reporters.list.find(({ name }) => activated === name);
		});
	}
	async #collectFiles(files) {
		let filesURLs = await this.#fileManager.getFiles(this.#config.cwd, files, this.#config.exclude);
		if (this.#config.filters.files && this.#config.filters.files.length) filesURLs = this.#fileManager.grep(filesURLs, this.#config.filters.files);
		return filesURLs;
	}
	async #getSuites() {
		let suites = [];
		let suitesFilters = this.#config.filters.suites || [];
		if ("files" in this.#config) suites.push({
			name: "default",
			files: this.#config.files,
			timeout: this.#config.timeout,
			retries: this.#config.retries,
			filesURLs: await this.#collectFiles(this.#config.files)
		});
		if ("suites" in this.#config) {
			for (let suite of this.#config.suites) if (!suitesFilters.length || suitesFilters.includes(suite.name)) suites.push({
				...suite,
				filesURLs: await this.#collectFiles(suite.files)
			});
		}
		return suites;
	}
	#getRefinerFilters() {
		return Object.keys(this.#config.filters).reduce((result, layer) => {
			if (layer === "tests" || layer === "tags" || layer === "groups") result.push({
				layer,
				filters: this.#config.filters[layer]
			});
			return result;
		}, []);
	}
	async plan() {
		const suites = await this.#getSuites();
		return {
			reporters: this.#getActivatedReporters(),
			suites,
			refinerFilters: this.#getRefinerFilters(),
			config: this.#config
		};
	}
};
var GlobalHooks = class {
	#hooks = new Hooks();
	#setupRunner;
	#teardownRunner;
	apply(config) {
		config.setup.forEach((hook) => this.#hooks.add("setup", hook));
		config.teardown.forEach((hook) => this.#hooks.add("teardown", hook));
	}
	async setup(runner) {
		this.#setupRunner = this.#hooks.runner("setup");
		this.#teardownRunner = this.#hooks.runner("teardown");
		await this.#setupRunner.run(runner);
	}
	async teardown(error, runner) {
		if (this.#setupRunner) await this.#setupRunner.cleanup(error, runner);
		if (this.#teardownRunner) {
			if (!error) await this.#teardownRunner.run(runner);
			await this.#teardownRunner.cleanup(error, runner);
		}
	}
};
const OPTIONS = {
	string: [
		"tests",
		"groups",
		"tags",
		"files",
		"timeout",
		"retries",
		"reporters",
		"bailLayer"
	],
	boolean: [
		"help",
		"matchAll",
		"failed",
		"bail",
		"listPinned"
	],
	alias: {
		forceExit: "force-exit",
		matchAll: "match-all",
		listPinned: "list-pinned",
		bailLayer: "bail-layer",
		help: "h"
	}
};
const GET_HELP = () => `
${colors.yellow("@japa/runner v2.3.0")}

${colors.green("--tests")}                     ${colors.dim("Filter tests by the test title")}
${colors.green("--groups")}                    ${colors.dim("Filter tests by the group title")}
${colors.green("--tags")}                      ${colors.dim("Filter tests by tags")}
${colors.green("--match-all")}                 ${colors.dim("Run tests that matches all the supplied tags")}
${colors.green("--list-pinned")}               ${colors.dim("List pinned tests")}
${colors.green("--files")}                     ${colors.dim("Filter tests by the file name")}
${colors.green("--force-exit")}                ${colors.dim("Forcefully exit the process")}
${colors.green("--timeout")}                   ${colors.dim("Define default timeout for all tests")}
${colors.green("--retries")}                   ${colors.dim("Define default retries for all tests")}
${colors.green("--reporters")}                 ${colors.dim("Activate one or more test reporters")}
${colors.green("--failed")}                    ${colors.dim("Run tests failed during the last run")}
${colors.green("--bail")}                      ${colors.dim("Exit early when a test fails")}
${colors.green("--bail-layer")}                ${colors.dim("Specify at which layer to enable the bail mode. Can be \"group\" or \"suite\"")}
${colors.green("-h, --help")}                  ${colors.dim("View help")}

${colors.yellow("Examples:")}
${colors.dim("node bin/test.js --tags=\"@github\"")}
${colors.dim("node bin/test.js --tags=\"~@github\"")}
${colors.dim("node bin/test.js --tags=\"@github,@slow,@integration\" --match-all")}
${colors.dim("node bin/test.js --force-exit")}
${colors.dim("node bin/test.js --files=\"user\"")}
${colors.dim("node bin/test.js --files=\"functional/user\"")}
${colors.dim("node bin/test.js --files=\"unit/user\"")}
${colors.dim("node bin/test.js --failed")}
${colors.dim("node bin/test.js --bail")}
${colors.dim("node bin/test.js --bail=group")}

${colors.yellow("Notes:")}
- When groups and tests filters are applied together. We will first filter the
  tests by group title and then apply the tests filter.
- The timeout defined on test object takes precedence over the ${colors.green("--timeout")} flag.
- The retries defined on test object takes precedence over the ${colors.green("--retries")} flag.
- The ${colors.green("--files")} flag checks for the file names ending with the filter substring.
- The ${colors.green("--tags")} filter runs tests that has one or more of the supplied tags.
- You can use the ${colors.green("--match-all")} flag to run tests that has all the supplied tags.
`;
var CliParser = class {
	parse(argv) {
		return getopts(argv, OPTIONS);
	}
	getHelp() {
		return GET_HELP();
	}
};
const NOOP = () => {};
const DEFAULTS = {
	files: [],
	timeout: 2e3,
	retries: 0,
	forceExit: false,
	plugins: [],
	reporters: {
		activated: isRunningInAIAgent() ? ["dot"] : ["spec"].concat(process.env.GITHUB_ACTIONS === "true" ? ["github"] : []),
		list: [
			spec(),
			ndjson(),
			dot(),
			github()
		]
	},
	importer: (filePath) => import(filePath.href),
	configureSuite: () => {}
};
var ConfigManager = class {
	#config;
	#cliArgs;
	constructor(config, cliArgs) {
		this.#config = config;
		this.#cliArgs = cliArgs;
	}
	#processAsArray(value, splitByComma) {
		return Array.isArray(value) ? value : splitByComma ? value.split(",").map((item) => item.trim()) : [value];
	}
	#getCLIFilters() {
		const filters = {};
		if (this.#cliArgs.tags) filters.tags = this.#processAsArray(this.#cliArgs.tags, true);
		if (this.#cliArgs.tests) filters.tests = this.#processAsArray(this.#cliArgs.tests, false);
		if (this.#cliArgs.files) filters.files = this.#processAsArray(this.#cliArgs.files, true);
		if (this.#cliArgs.groups) filters.groups = this.#processAsArray(this.#cliArgs.groups, false);
		if (this.#cliArgs._ && this.#cliArgs._.length) filters.suites = this.#processAsArray(this.#cliArgs._, true);
		return filters;
	}
	#getCLITimeout() {
		if (this.#cliArgs.timeout) {
			const value = Number(this.#cliArgs.timeout);
			if (!Number.isNaN(value)) return value;
		}
	}
	#getCLIRetries() {
		if (this.#cliArgs.retries) {
			const value = Number(this.#cliArgs.retries);
			if (!Number.isNaN(value)) return value;
		}
	}
	#getCLIForceExit() {
		if (this.#cliArgs.forceExit) return true;
	}
	#getCLIReporters() {
		if (this.#cliArgs.reporters) return this.#processAsArray(this.#cliArgs.reporters, true);
	}
	hydrate() {
		const cliFilters = this.#getCLIFilters();
		const cliRetries = this.#getCLIRetries();
		const cliTimeout = this.#getCLITimeout();
		const cliReporters = this.#getCLIReporters();
		const cliForceExit = this.#getCLIForceExit();
		debug_default("filters applied using CLI flags %O", cliFilters);
		const baseConfig = {
			cwd: this.#config.cwd ?? process.cwd(),
			exclude: this.#config.exclude || [
				"node_modules/**",
				".git/**",
				"coverage/**"
			],
			filters: Object.assign({}, this.#config.filters ?? {}, cliFilters),
			importer: this.#config.importer ?? DEFAULTS.importer,
			refiner: this.#config.refiner ?? new Refiner(),
			retries: cliRetries ?? this.#config.retries ?? DEFAULTS.retries,
			timeout: cliTimeout ?? this.#config.timeout ?? DEFAULTS.timeout,
			plugins: this.#config.plugins ?? DEFAULTS.plugins,
			forceExit: cliForceExit ?? this.#config.forceExit ?? DEFAULTS.forceExit,
			reporters: this.#config.reporters ? {
				activated: this.#config.reporters.activated,
				list: this.#config.reporters.list || DEFAULTS.reporters.list
			} : DEFAULTS.reporters,
			configureSuite: this.#config.configureSuite ?? DEFAULTS.configureSuite,
			setup: this.#config.setup || [],
			teardown: this.#config.teardown || []
		};
		if (cliReporters) baseConfig.reporters.activated = cliReporters;
		if ("files" in this.#config) return {
			files: this.#config.files,
			...baseConfig
		};
		return {
			suites: this.#config.suites.map((suite) => {
				return {
					name: suite.name,
					files: suite.files,
					timeout: cliTimeout ?? suite.timeout ?? baseConfig.timeout,
					retries: cliRetries ?? suite.retries ?? baseConfig.retries,
					configure: suite.configure || NOOP
				};
			}),
			...baseConfig
		};
	}
};
const contextBuilder = (testInstance) => new TestContext(testInstance);
function createTest(title, emitter, refiner, debuggingError, options) {
	const testInstance = new Test(title, contextBuilder, emitter, refiner, options.group);
	testInstance.options.meta.suite = options.suite;
	testInstance.options.meta.group = options.group;
	testInstance.options.meta.fileName = options.file;
	testInstance.options.meta.abort = (message) => {
		debuggingError.message = message;
		throw debuggingError;
	};
	if (options.timeout !== void 0) testInstance.timeout(options.timeout);
	if (options.retries !== void 0) testInstance.retry(options.retries);
	if (options.group) options.group.add(testInstance);
	else if (options.suite) options.suite.add(testInstance);
	return testInstance;
}
function createTestGroup(title, emitter, refiner, options) {
	if (options.group) throw new Error("Nested groups are not supported by Japa");
	const group = new Group(title, emitter, refiner);
	group.options.meta.suite = options.suite;
	group.options.meta.fileName = options.file;
	if (options.suite) options.suite.add(group);
	return group;
}
export { GlobalHooks as a, debug_default as c, CliParser as i, createTestGroup as n, Planner as o, ConfigManager as r, validator_default as s, createTest as t };
