@rstest/core
Version:
The Rsbuild-based test tool.
1,145 lines (1,123 loc) • 33.5 kB
JavaScript
import 'module';
/*#__PURE__*/ import.meta.url;
import { __webpack_require__ } from "./rslib-runtime.js";
import "./1157.js";
import "./2672.js";
import { node_process } from "./3278.js";
import { Ie, Me, ye, M, dist_Y, Se, ve, pD, xe } from "./0~9348.js";
__webpack_require__.add({
"../../node_modules/.pnpm/@vercel+detect-agent@1.0.0/node_modules/@vercel/detect-agent/dist/index.js" (module, __unused_rspack_exports, __webpack_require__) {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 && "object" == typeof from || "function" == typeof from) {
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 __toCommonJS = (mod)=>__copyProps(__defProp({}, "__esModule", {
value: true
}), mod);
var src_exports = {};
__export(src_exports, {
KNOWN_AGENTS: ()=>KNOWN_AGENTS,
determineAgent: ()=>determineAgent
});
module.exports = __toCommonJS(src_exports);
var import_promises = __webpack_require__("node:fs/promises");
var import_node_fs = __webpack_require__("fs");
const DEVIN_LOCAL_PATH = "/opt/.devin";
const CURSOR = "cursor";
const CURSOR_CLI = "cursor-cli";
const CLAUDE = "claude";
const DEVIN = "devin";
const REPLIT = "replit";
const GEMINI = "gemini";
const CODEX = "codex";
const KNOWN_AGENTS = {
CURSOR,
CURSOR_CLI,
CLAUDE,
DEVIN,
REPLIT,
GEMINI,
CODEX
};
async function determineAgent() {
if (process.env.AI_AGENT) {
const name = process.env.AI_AGENT.trim();
if (name) return {
isAgent: true,
agent: {
name
}
};
}
if (process.env.CURSOR_TRACE_ID) return {
isAgent: true,
agent: {
name: CURSOR
}
};
if (process.env.CURSOR_AGENT) return {
isAgent: true,
agent: {
name: CURSOR_CLI
}
};
if (process.env.GEMINI_CLI) return {
isAgent: true,
agent: {
name: GEMINI
}
};
if (process.env.CODEX_SANDBOX) return {
isAgent: true,
agent: {
name: CODEX
}
};
if (process.env.CLAUDECODE || process.env.CLAUDE_CODE) return {
isAgent: true,
agent: {
name: CLAUDE
}
};
if (process.env.REPL_ID) return {
isAgent: true,
agent: {
name: REPLIT
}
};
try {
await (0, import_promises.access)(DEVIN_LOCAL_PATH, import_node_fs.constants.F_OK);
return {
isAgent: true,
agent: {
name: DEVIN
}
};
} catch (error) {}
return {
isAgent: false,
agent: void 0
};
}
}
});
const constants_AGENTS = [
"npm",
"yarn",
"yarn@berry",
"pnpm",
"pnpm@6",
"bun",
"deno"
];
const LOCKS = {
"bun.lock": "bun",
"bun.lockb": "bun",
"deno.lock": "deno",
"pnpm-lock.yaml": "pnpm",
"pnpm-workspace.yaml": "pnpm",
"yarn.lock": "yarn",
"package-lock.json": "npm",
"npm-shrinkwrap.json": "npm"
};
const INSTALL_METADATA = {
"node_modules/.deno/": "deno",
"node_modules/.pnpm/": "pnpm",
"node_modules/.yarn-state.yml": "yarn",
"node_modules/.yarn_integrity": "yarn",
"node_modules/.package-lock.json": "npm",
".pnp.cjs": "yarn",
".pnp.js": "yarn",
"bun.lock": "bun",
"bun.lockb": "bun"
};
const promises_ = __webpack_require__("node:fs/promises");
const external_node_path_ = __webpack_require__("node:path");
async function pathExists(path2, type) {
try {
const stat = await promises_["default"].stat(path2);
return "file" === type ? stat.isFile() : stat.isDirectory();
} catch {
return false;
}
}
function* lookup(cwd = node_process.cwd()) {
let directory = external_node_path_["default"].resolve(cwd);
const { root } = external_node_path_["default"].parse(directory);
while(directory && directory !== root){
yield directory;
directory = external_node_path_["default"].dirname(directory);
}
}
async function parsePackageJson(filepath, options) {
if (!filepath || !await pathExists(filepath, "file")) return null;
return await handlePackageManager(filepath, options);
}
async function detect(options = {}) {
const { cwd, strategies = [
"lockfile",
"packageManager-field",
"devEngines-field"
] } = options;
let stopDir;
if ("string" == typeof options.stopDir) {
const resolved = external_node_path_["default"].resolve(options.stopDir);
stopDir = (dir)=>dir === resolved;
} else stopDir = options.stopDir;
for (const directory of lookup(cwd)){
for (const strategy of strategies)switch(strategy){
case "lockfile":
for (const lock of Object.keys(LOCKS))if (await pathExists(external_node_path_["default"].join(directory, lock), "file")) {
const name = LOCKS[lock];
const result = await parsePackageJson(external_node_path_["default"].join(directory, "package.json"), options);
if (result) return result;
return {
name,
agent: name
};
}
break;
case "packageManager-field":
case "devEngines-field":
{
const result = await parsePackageJson(external_node_path_["default"].join(directory, "package.json"), options);
if (result) return result;
break;
}
case "install-metadata":
for (const metadata of Object.keys(INSTALL_METADATA)){
const fileOrDir = metadata.endsWith("/") ? "dir" : "file";
if (await pathExists(external_node_path_["default"].join(directory, metadata), fileOrDir)) {
const name = INSTALL_METADATA[metadata];
const agent = "yarn" === name ? isMetadataYarnClassic(metadata) ? "yarn" : "yarn@berry" : name;
return {
name,
agent
};
}
}
break;
}
if (stopDir?.(directory)) break;
}
return null;
}
function getNameAndVer(pkg) {
const handelVer = (version)=>version?.match(/\d+(\.\d+){0,2}/)?.[0] ?? version;
if ("string" == typeof pkg.packageManager) {
const [name, ver] = pkg.packageManager.replace(/^\^/, "").split("@");
return {
name,
ver: handelVer(ver)
};
}
if ("string" == typeof pkg.devEngines?.packageManager?.name) return {
name: pkg.devEngines.packageManager.name,
ver: handelVer(pkg.devEngines.packageManager.version)
};
}
async function handlePackageManager(filepath, options) {
try {
const content = await promises_["default"].readFile(filepath, "utf8");
const pkg = options.packageJsonParser ? await options.packageJsonParser(content, filepath) : JSON.parse(content);
let agent;
const nameAndVer = getNameAndVer(pkg);
if (nameAndVer) {
const name = nameAndVer.name;
const ver = nameAndVer.ver;
let version = ver;
if ("yarn" === name && ver && Number.parseInt(ver) > 1) {
agent = "yarn@berry";
version = "berry";
return {
name,
agent,
version
};
}
if ("pnpm" === name && ver && Number.parseInt(ver) < 7) {
agent = "pnpm@6";
return {
name,
agent,
version
};
}
if (!constants_AGENTS.includes(name)) return options.onUnknown?.(pkg.packageManager) ?? null;
agent = name;
return {
name,
agent,
version
};
}
} catch {}
return null;
}
function isMetadataYarnClassic(metadataPath) {
return metadataPath.endsWith(".yarn_integrity");
}
const external_node_fs_ = __webpack_require__("fs");
function getUniqueBaseName(dir, baseName, ext) {
const fullPath = external_node_path_["default"].join(dir, `${baseName}${ext}`);
if (!external_node_fs_["default"].existsSync(fullPath)) return baseName;
let suffix = 1;
while(external_node_fs_["default"].existsSync(external_node_path_["default"].join(dir, `${baseName}_${suffix}${ext}`)))suffix++;
return `${baseName}_${suffix}`;
}
function ensureDir(dir) {
if (!external_node_fs_["default"].existsSync(dir)) external_node_fs_["default"].mkdirSync(dir, {
recursive: true
});
}
function writeFile(filePath, content) {
external_node_fs_["default"].writeFileSync(filePath, content, 'utf-8');
}
function readPackageJson(cwd) {
const pkgPath = external_node_path_["default"].join(cwd, 'package.json');
if (!external_node_fs_["default"].existsSync(pkgPath)) return null;
try {
const content = external_node_fs_["default"].readFileSync(pkgPath, 'utf-8');
return JSON.parse(content);
} catch {
return null;
}
}
function updatePackageJsonScripts(cwd, scripts) {
const pkgPath = external_node_path_["default"].join(cwd, 'package.json');
let pkg;
if (external_node_fs_["default"].existsSync(pkgPath)) {
const content = external_node_fs_["default"].readFileSync(pkgPath, 'utf-8');
pkg = JSON.parse(content);
} else pkg = {};
const existingScripts = pkg.scripts ?? {};
pkg.scripts = {
...existingScripts,
...scripts
};
external_node_fs_["default"].writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf-8');
}
function updatePackageJsonDevDeps(cwd, deps) {
const pkgPath = external_node_path_["default"].join(cwd, 'package.json');
let pkg;
if (external_node_fs_["default"].existsSync(pkgPath)) {
const content = external_node_fs_["default"].readFileSync(pkgPath, 'utf-8');
pkg = JSON.parse(content);
} else pkg = {};
const existingDevDeps = pkg.devDependencies ?? {};
for (const [name, version] of Object.entries(deps))if (!existingDevDeps[name]) existingDevDeps[name] = version;
pkg.devDependencies = existingDevDeps;
external_node_fs_["default"].writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf-8');
}
async function detectPackageManagerAgent(cwd) {
const result = await detect({
cwd
});
return result?.agent ?? 'npm';
}
function detectTestDir(cwd) {
const candidates = [
'tests',
'test',
'__tests__',
'src/__tests__'
];
for (const dir of candidates){
const fullPath = external_node_path_["default"].join(cwd, dir);
if (external_node_fs_["default"].existsSync(fullPath) && external_node_fs_["default"].statSync(fullPath).isDirectory()) return dir;
}
return 'tests';
}
function detectReact(pkg) {
const deps = pkg.dependencies ?? {};
const devDeps = pkg.devDependencies ?? {};
const reactVersion = deps.react ?? devDeps.react ?? null;
if (reactVersion) {
const cleanVersion = reactVersion.replace(/^[\^~>=<]+/, '');
return {
detected: true,
version: cleanVersion
};
}
return {
detected: false,
version: null
};
}
function detectTypeScript(cwd) {
return external_node_fs_["default"].existsSync(external_node_path_["default"].join(cwd, 'tsconfig.json'));
}
async function detectProject(cwd) {
const pkg = readPackageJson(cwd);
const { detected: hasReact, version: reactVersion } = pkg ? detectReact(pkg) : {
detected: false,
version: null
};
const hasTypeScript = detectTypeScript(cwd);
const testDir = detectTestDir(cwd);
const agent = await detectPackageManagerAgent(cwd);
return {
framework: hasReact ? 'react' : null,
language: hasTypeScript ? 'ts' : 'js',
testDir,
agent,
reactVersion
};
}
function dashDashArg(agent, agentCommand) {
return (args)=>{
if (args.length > 1) return [
agent,
agentCommand,
args[0],
"--",
...args.slice(1)
];
return [
agent,
agentCommand,
args[0]
];
};
}
function denoExecute() {
return (args)=>[
"deno",
"run",
`npm:${args[0]}`,
...args.slice(1)
];
}
const npm = {
agent: [
"npm",
0
],
run: dashDashArg("npm", "run"),
install: [
"npm",
"i",
0
],
frozen: [
"npm",
"ci",
0
],
global: [
"npm",
"i",
"-g",
0
],
add: [
"npm",
"i",
0
],
upgrade: [
"npm",
"update",
0
],
"upgrade-interactive": null,
dedupe: [
"npm",
"dedupe",
0
],
execute: [
"npx",
0
],
"execute-local": [
"npx",
0
],
uninstall: [
"npm",
"uninstall",
0
],
global_uninstall: [
"npm",
"uninstall",
"-g",
0
]
};
const yarn = {
agent: [
"yarn",
0
],
run: [
"yarn",
"run",
0
],
install: [
"yarn",
"install",
0
],
frozen: [
"yarn",
"install",
"--frozen-lockfile",
0
],
global: [
"yarn",
"global",
"add",
0
],
add: [
"yarn",
"add",
0
],
upgrade: [
"yarn",
"upgrade",
0
],
"upgrade-interactive": [
"yarn",
"upgrade-interactive",
0
],
dedupe: null,
execute: [
"npx",
0
],
"execute-local": dashDashArg("yarn", "exec"),
uninstall: [
"yarn",
"remove",
0
],
global_uninstall: [
"yarn",
"global",
"remove",
0
]
};
const yarnBerry = {
...yarn,
frozen: [
"yarn",
"install",
"--immutable",
0
],
upgrade: [
"yarn",
"up",
0
],
"upgrade-interactive": [
"yarn",
"up",
"-i",
0
],
dedupe: [
"yarn",
"dedupe",
0
],
execute: [
"yarn",
"dlx",
0
],
"execute-local": [
"yarn",
"exec",
0
],
global: [
"npm",
"i",
"-g",
0
],
global_uninstall: [
"npm",
"uninstall",
"-g",
0
]
};
const pnpm = {
agent: [
"pnpm",
0
],
run: [
"pnpm",
"run",
0
],
install: [
"pnpm",
"i",
0
],
frozen: [
"pnpm",
"i",
"--frozen-lockfile",
0
],
global: [
"pnpm",
"add",
"-g",
0
],
add: [
"pnpm",
"add",
0
],
upgrade: [
"pnpm",
"update",
0
],
"upgrade-interactive": [
"pnpm",
"update",
"-i",
0
],
dedupe: [
"pnpm",
"dedupe",
0
],
execute: [
"pnpm",
"dlx",
0
],
"execute-local": [
"pnpm",
"exec",
0
],
uninstall: [
"pnpm",
"remove",
0
],
global_uninstall: [
"pnpm",
"remove",
"--global",
0
]
};
const bun = {
agent: [
"bun",
0
],
run: [
"bun",
"run",
0
],
install: [
"bun",
"install",
0
],
frozen: [
"bun",
"install",
"--frozen-lockfile",
0
],
global: [
"bun",
"add",
"-g",
0
],
add: [
"bun",
"add",
0
],
upgrade: [
"bun",
"update",
0
],
"upgrade-interactive": [
"bun",
"update",
"-i",
0
],
dedupe: null,
execute: [
"bun",
"x",
0
],
"execute-local": [
"bun",
"x",
0
],
uninstall: [
"bun",
"remove",
0
],
global_uninstall: [
"bun",
"remove",
"-g",
0
]
};
const deno = {
agent: [
"deno",
0
],
run: [
"deno",
"task",
0
],
install: [
"deno",
"install",
0
],
frozen: [
"deno",
"install",
"--frozen",
0
],
global: [
"deno",
"install",
"-g",
0
],
add: [
"deno",
"add",
0
],
upgrade: [
"deno",
"outdated",
"--update",
0
],
"upgrade-interactive": [
"deno",
"outdated",
"--update",
0
],
dedupe: null,
execute: denoExecute(),
"execute-local": [
"deno",
"task",
"--eval",
0
],
uninstall: [
"deno",
"remove",
0
],
global_uninstall: [
"deno",
"uninstall",
"-g",
0
]
};
const COMMANDS = {
npm: npm,
yarn: yarn,
"yarn@berry": yarnBerry,
pnpm: pnpm,
"pnpm@6": {
...pnpm,
run: dashDashArg("pnpm", "run")
},
bun: bun,
deno: deno
};
function resolveCommand(agent, command, args) {
const value = COMMANDS[agent][command];
return constructCommand(value, args);
}
function constructCommand(value, args) {
if (null == value) return null;
const list = "function" == typeof value ? value(args) : value.flatMap((v)=>{
if ("number" == typeof v) return args;
return [
v
];
});
return {
command: list[0],
args: list.slice(1)
};
}
function getConfigTemplate() {
return `import { defineConfig } from '@rstest/core';
export default defineConfig({
browser: {
enabled: true,
},
});
`;
}
function getReactComponentTemplate(lang) {
if ('ts' === lang) return `import { useState } from 'react';
export default function Counter({ initial = 0 }: { initial?: number }) {
const [count, setCount] = useState(initial);
return (
<div>
<p>Count: {count}</p>
<button type="button" onClick={() => setCount((c) => c + 1)}>
Increment
</button>
<button type="button" onClick={() => setCount((c) => c - 1)}>
Decrement
</button>
</div>
);
}
`;
return `import { useState } from 'react';
export default function Counter({ initial = 0 }) {
const [count, setCount] = useState(initial);
return (
<div>
<p>Count: {count}</p>
<button type="button" onClick={() => setCount((c) => c + 1)}>
Increment
</button>
<button type="button" onClick={() => setCount((c) => c - 1)}>
Decrement
</button>
</div>
);
}
`;
}
function getReactTestTemplate(lang) {
const componentExt = 'ts' === lang ? 'tsx' : 'jsx';
return `import { expect, test } from '@rstest/core';
import { render } from '@rstest/browser-react';
import Counter from './Counter.${componentExt}';
test('increments count on button click', async () => {
const screen = await render(<Counter initial={5} />);
await expect.element(screen.getByText('Count: 5')).toBeInTheDocument();
await screen.getByRole('button', { name: 'Increment' }).click();
await expect.element(screen.getByText('Count: 6')).toBeInTheDocument();
});
`;
}
function getVanillaComponentTemplate(lang) {
if ('ts' === lang) return `export function createCounter(initial = 0): HTMLElement {
let count = initial;
const container = document.createElement('div');
const display = document.createElement('p');
const incBtn = document.createElement('button');
const decBtn = document.createElement('button');
display.textContent = \`Count: \${count}\`;
incBtn.textContent = 'Increment';
decBtn.textContent = 'Decrement';
incBtn.addEventListener('click', () => {
count++;
display.textContent = \`Count: \${count}\`;
});
decBtn.addEventListener('click', () => {
count--;
display.textContent = \`Count: \${count}\`;
});
container.append(display, incBtn, decBtn);
return container;
}
`;
return `export function createCounter(initial = 0) {
let count = initial;
const container = document.createElement('div');
const display = document.createElement('p');
const incBtn = document.createElement('button');
const decBtn = document.createElement('button');
display.textContent = \`Count: \${count}\`;
incBtn.textContent = 'Increment';
decBtn.textContent = 'Decrement';
incBtn.addEventListener('click', () => {
count++;
display.textContent = \`Count: \${count}\`;
});
decBtn.addEventListener('click', () => {
count--;
display.textContent = \`Count: \${count}\`;
});
container.append(display, incBtn, decBtn);
return container;
}
`;
}
function getVanillaTestTemplate(lang) {
const ext = 'ts' === lang ? 'ts' : 'js';
return `import { expect, test } from '@rstest/core';
import { page } from '@rstest/browser';
import { createCounter } from './Counter.${ext}';
test('increments count on button click', async () => {
document.body.appendChild(createCounter(5));
await expect.element(page.getByText('Count: 5')).toBeInTheDocument();
await page.getByRole('button', { name: 'Increment' }).click();
await expect.element(page.getByText('Count: 6')).toBeInTheDocument();
});
`;
}
function getDependenciesWithVersions(framework, provider, rstestVersion) {
const deps = {
'@rstest/browser': `^${rstestVersion}`,
'@testing-library/dom': '^10.0.0'
};
if ('playwright' === provider) deps.playwright = "^1.49.1";
if ('react' === framework) deps['@rstest/browser-react'] = `^${rstestVersion}`;
return deps;
}
function getInstallCommand(agent) {
const resolved = resolveCommand(agent, 'install', []);
if (!resolved) return 'npm install';
return [
resolved.command,
...resolved.args
].join(' ');
}
function getPlaywrightInstallCommand(agent, _provider) {
const resolved = resolveCommand(agent, 'execute', [
'playwright',
'install',
'--with-deps'
]);
if (!resolved) return 'npx playwright install --with-deps';
return [
resolved.command,
...resolved.args
].join(' ');
}
function getRunCommand(agent) {
const resolved = resolveCommand(agent, 'run', [
'test:browser'
]);
if (!resolved) return 'npm run test:browser';
return [
resolved.command,
...resolved.args
].join(' ');
}
function getConfigFileName() {
return 'rstest.browser.config.ts';
}
const dist = __webpack_require__("../../node_modules/.pnpm/@vercel+detect-agent@1.0.0/node_modules/@vercel/detect-agent/dist/index.js");
const picocolors = __webpack_require__("../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js");
var picocolors_default = /*#__PURE__*/ __webpack_require__.n(picocolors);
async function create(options = {}) {
const cwd = process.cwd();
const { yes: nonInteractive } = options;
const projectInfo = await detectProject(cwd);
const { isAgent } = await (0, dist.determineAgent)();
if (nonInteractive) await createNonInteractive(cwd, projectInfo);
else await createInteractive(cwd, projectInfo, isAgent);
}
function computeFilePreview(cwd, projectInfo) {
const { language, testDir, framework } = projectInfo;
const effectiveFramework = 'react' === framework ? 'react' : 'vanilla';
const configFile = getConfigFileName();
let componentExt;
let testExt;
if ('react' === effectiveFramework) {
componentExt = 'ts' === language ? '.tsx' : '.jsx';
testExt = 'ts' === language ? '.test.tsx' : '.test.jsx';
} else {
componentExt = 'ts' === language ? '.ts' : '.js';
testExt = 'ts' === language ? '.test.ts' : '.test.js';
}
const testDirPath = external_node_path_["default"].join(cwd, testDir);
const baseName = getUniqueBaseName(testDirPath, 'Counter', componentExt);
return {
configFile,
componentFile: `${testDir}/${baseName}${componentExt}`,
testFile: `${testDir}/${baseName}${testExt}`,
framework: effectiveFramework
};
}
async function createNonInteractive(cwd, projectInfo) {
const { agent, testDir, framework, reactVersion } = projectInfo;
const provider = 'playwright';
console.log();
console.log(picocolors_default().cyan('◆'), picocolors_default().bold('rstest init browser --yes'));
console.log();
console.log(' Detecting project...');
if ('react' === framework && reactVersion) console.log(picocolors_default().green(' ✓'), `Found React ${reactVersion}`);
else if ('react' === framework) console.log(picocolors_default().green(' ✓'), 'Found React');
else console.log(picocolors_default().yellow(' ⚠'), 'Framework not detected, generating vanilla DOM example');
console.log(picocolors_default().green(' ✓'), 'Using playwright as browser provider');
console.log(picocolors_default().green(' ✓'), `Test directory: ${testDir}/`);
console.log();
const createdFiles = await generateFiles(cwd, projectInfo, provider);
console.log(' Created files:');
for (const file of createdFiles)console.log(` - ${file}`);
console.log(' - Updated package.json');
console.log();
console.log(' Next steps:');
console.log(` ${getInstallCommand(agent)}`);
console.log(` ${getPlaywrightInstallCommand(agent, provider)}`);
console.log(` ${getRunCommand(agent)}`);
console.log();
console.log(picocolors_default().green('└'), 'Done!');
}
async function createInteractive(cwd, projectInfo, isAgent) {
const { agent, language, testDir, framework, reactVersion } = projectInfo;
const effectiveFramework = 'react' === framework ? 'react' : 'vanilla';
Ie(picocolors_default().bgCyan(picocolors_default().black(' rstest init browser ')));
const detectionLines = [];
if ('react' === framework && reactVersion) detectionLines.push(`${picocolors_default().green('✓')} Found React ${reactVersion}`);
else if ('react' === framework) detectionLines.push(`${picocolors_default().green('✓')} Found React`);
else detectionLines.push(`${picocolors_default().yellow('⚠')} Framework not detected, will generate vanilla DOM example`);
detectionLines.push(`${picocolors_default().green('✓')} Found ${'ts' === language ? 'TypeScript' : 'JavaScript'}`);
detectionLines.push(`${picocolors_default().green('✓')} Test directory: ${testDir}/`);
Me(detectionLines.join('\n'), 'Detecting project...');
if (isAgent) M.info(`AI Agent detected. For non-interactive mode, run:\n ${picocolors_default().cyan('npx rstest init browser --yes')}`);
const providerSelection = await ve({
message: 'Choose a browser provider (so far, only Playwright)',
options: [
{
value: 'playwright',
label: 'Playwright',
hint: 'recommended'
}
]
});
if (pD(providerSelection)) {
xe('Operation cancelled.');
process.exit(0);
}
const provider = providerSelection;
const preview = computeFilePreview(cwd, projectInfo);
const deps = getDependenciesWithVersions(effectiveFramework, provider, "0.7.9");
const depsList = Object.entries(deps).map(([name, version])=>`${name}@${version}`).join(', ');
const previewLines = [
`${picocolors_default().cyan('+')} Create ${preview.configFile}`,
`${picocolors_default().cyan('+')} Create ${preview.componentFile}`,
`${picocolors_default().cyan('+')} Create ${preview.testFile}`,
`${picocolors_default().yellow('~')} Modify package.json`,
' - Add "test:browser" script',
` - Add devDependencies: ${picocolors_default().dim(depsList)}`
];
Me(previewLines.join('\n'), 'Changes to be made');
const confirmed = await ye({
message: 'Proceed with these changes?',
initialValue: true
});
if (pD(confirmed) || !confirmed) {
xe('Operation cancelled.');
process.exit(0);
}
const s = dist_Y();
s.start('Creating files...');
const createdFiles = await generateFiles(cwd, projectInfo, provider);
s.stop('Created files');
const fileLines = createdFiles.map((f)=>`${picocolors_default().green('✓')} Created ${f}`);
fileLines.push(`${picocolors_default().green('✓')} Updated package.json`);
Me(fileLines.join('\n'), 'Files');
const nextStepsLines = [
`${picocolors_default().bold('1.')} Install dependencies:`,
` ${picocolors_default().cyan(getInstallCommand(agent))}`,
'',
`${picocolors_default().bold('2.')} Install Playwright browsers:`,
` ${picocolors_default().cyan(getPlaywrightInstallCommand(agent, provider))}`,
'',
`${picocolors_default().bold('3.')} Run your tests:`,
` ${picocolors_default().cyan(getRunCommand(agent))}`
];
Me(nextStepsLines.join('\n'), 'Next steps');
Se(picocolors_default().green('Done! Happy testing with Rstest!'));
}
async function generateFiles(cwd, projectInfo, provider) {
const { language, testDir, framework } = projectInfo;
const effectiveFramework = 'react' === framework ? 'react' : 'vanilla';
const createdFiles = [];
const configFileName = getConfigFileName();
const configPath = external_node_path_["default"].join(cwd, configFileName);
writeFile(configPath, getConfigTemplate());
createdFiles.push(configFileName);
const testDirPath = external_node_path_["default"].join(cwd, testDir);
ensureDir(testDirPath);
let componentExt;
let testExt;
if ('react' === effectiveFramework) {
componentExt = 'ts' === language ? '.tsx' : '.jsx';
testExt = 'ts' === language ? '.test.tsx' : '.test.jsx';
} else {
componentExt = 'ts' === language ? '.ts' : '.js';
testExt = 'ts' === language ? '.test.ts' : '.test.js';
}
const baseName = getUniqueBaseName(testDirPath, 'Counter', componentExt);
const componentFileName = `${baseName}${componentExt}`;
const componentPath = external_node_path_["default"].join(testDirPath, componentFileName);
'react' === effectiveFramework ? writeFile(componentPath, getReactComponentTemplate(language)) : writeFile(componentPath, getVanillaComponentTemplate(language));
createdFiles.push(`${testDir}/${componentFileName}`);
const testFileName = `${baseName}${testExt}`;
const testPath = external_node_path_["default"].join(testDirPath, testFileName);
let testContent;
if ('react' === effectiveFramework) {
testContent = getReactTestTemplate(language);
if ('Counter' !== baseName) testContent = testContent.replace(/from '\.\/Counter\.(tsx|jsx)'/, `from './${baseName}.$1'`);
} else {
testContent = getVanillaTestTemplate(language);
if ('Counter' !== baseName) testContent = testContent.replace(/from '\.\/Counter\.(ts|js)'/, `from './${baseName}.$1'`);
}
writeFile(testPath, testContent);
createdFiles.push(`${testDir}/${testFileName}`);
updatePackageJsonScripts(cwd, {
'test:browser': 'rstest --config=rstest.browser.config.ts'
});
const deps = getDependenciesWithVersions(effectiveFramework, provider, "0.7.9");
updatePackageJsonDevDeps(cwd, deps);
return createdFiles;
}
export { create };