use std::fs;
use tempfile::TempDir;
#[derive(Debug, Clone, Copy)]
pub enum LockfileType {
Npm,
Yarn,
Pnpm,
Bun,
}
impl LockfileType {
pub fn filename(&self) -> &'static str {
match self {
LockfileType::Npm => "package-lock.json",
LockfileType::Yarn => "yarn.lock",
LockfileType::Pnpm => "pnpm-lock.yaml",
LockfileType::Bun => "bun.lockb",
}
}
pub fn sample_content(&self) -> &'static str {
match self {
LockfileType::Npm => r#"{"lockfileVersion": 3, "packages": {}}"#,
LockfileType::Yarn => "# THIS IS AN AUTOGENERATED FILE\n__metadata:\n version: 6\n",
LockfileType::Pnpm => "lockfileVersion: '9.0'\n",
LockfileType::Bun => "binary lockfile content",
}
}
}
pub fn create_project(scripts: &[(&str, &str)]) -> TempDir {
let temp = TempDir::new().expect("Failed to create temp directory");
let scripts_json = scripts
.iter()
.map(|(name, cmd)| format!(r#" "{}": "{}""#, name, cmd))
.collect::<Vec<_>>()
.join(",\n");
let package_json = format!(
r#"{{
"name": "test-project",
"version": "1.0.0",
"scripts": {{
{}
}}
}}"#,
scripts_json
);
fs::write(temp.path().join("package.json"), package_json)
.expect("Failed to write package.json");
temp
}
pub fn create_project_with_descriptions(scripts: &[(&str, &str, &str)]) -> TempDir {
let temp = TempDir::new().expect("Failed to create temp directory");
let scripts_json = scripts
.iter()
.map(|(name, cmd, _)| format!(r#" "{}": "{}""#, name, cmd))
.collect::<Vec<_>>()
.join(",\n");
let scripts_info_json = scripts
.iter()
.filter(|(_, _, desc)| !desc.is_empty())
.map(|(name, _, desc)| format!(r#" "{}": "{}""#, name, desc))
.collect::<Vec<_>>()
.join(",\n");
let package_json = format!(
r#"{{
"name": "test-project",
"version": "1.0.0",
"scripts": {{
{}
}},
"scripts-info": {{
{}
}}
}}"#,
scripts_json, scripts_info_json
);
fs::write(temp.path().join("package.json"), package_json)
.expect("Failed to write package.json");
temp
}
pub fn create_project_with_config(scripts: &[(&str, &str)], config_toml: &str) -> TempDir {
let temp = create_project(scripts);
fs::write(temp.path().join(".nrsrc.toml"), config_toml).expect("Failed to write config");
temp
}
pub fn create_project_with_lockfile(
scripts: &[(&str, &str)],
lockfile_type: LockfileType,
) -> TempDir {
let temp = create_project(scripts);
fs::write(
temp.path().join(lockfile_type.filename()),
lockfile_type.sample_content(),
)
.expect("Failed to write lockfile");
temp
}
pub fn create_project_with_package_manager(
scripts: &[(&str, &str)],
package_manager: &str,
) -> TempDir {
let temp = TempDir::new().expect("Failed to create temp directory");
let scripts_json = scripts
.iter()
.map(|(name, cmd)| format!(r#" "{}": "{}""#, name, cmd))
.collect::<Vec<_>>()
.join(",\n");
let package_json = format!(
r#"{{
"name": "test-project",
"version": "1.0.0",
"packageManager": "{}",
"scripts": {{
{}
}}
}}"#,
package_manager, scripts_json
);
fs::write(temp.path().join("package.json"), package_json)
.expect("Failed to write package.json");
temp
}
pub fn create_project_with_pm_and_lockfile(
scripts: &[(&str, &str)],
package_manager: &str,
lockfile_type: LockfileType,
) -> TempDir {
let temp = create_project_with_package_manager(scripts, package_manager);
fs::write(
temp.path().join(lockfile_type.filename()),
lockfile_type.sample_content(),
)
.expect("Failed to write lockfile");
temp
}
pub fn create_empty_project() -> TempDir {
let temp = TempDir::new().expect("Failed to create temp directory");
let package_json = r#"{
"name": "empty-project",
"version": "1.0.0",
"scripts": {}
}"#;
fs::write(temp.path().join("package.json"), package_json)
.expect("Failed to write package.json");
temp
}
pub fn create_project_no_scripts() -> TempDir {
let temp = TempDir::new().expect("Failed to create temp directory");
let package_json = r#"{
"name": "no-scripts-project",
"version": "1.0.0"
}"#;
fs::write(temp.path().join("package.json"), package_json)
.expect("Failed to write package.json");
temp
}
pub fn create_project_invalid_json() -> TempDir {
let temp = TempDir::new().expect("Failed to create temp directory");
fs::write(temp.path().join("package.json"), "{ invalid json }")
.expect("Failed to write package.json");
temp
}
pub fn create_project_with_lifecycle_scripts() -> TempDir {
create_project(&[
("dev", "vite"),
("build", "vite build"),
("test", "vitest"),
("preinstall", "echo preinstall"),
("postinstall", "husky install"),
("prepare", "husky"),
("prepublishOnly", "npm test"),
])
}
pub fn create_large_project(script_count: usize) -> TempDir {
let scripts: Vec<(String, String)> = (0..script_count)
.map(|i| (format!("script_{}", i), format!("echo Script {}", i)))
.collect();
let scripts_ref: Vec<(&str, &str)> = scripts
.iter()
.map(|(name, cmd)| (name.as_str(), cmd.as_str()))
.collect();
create_project(&scripts_ref)
}
pub fn standard_scripts() -> Vec<(&'static str, &'static str)> {
vec![
("dev", "vite"),
("build", "vite build"),
("test", "vitest"),
("lint", "eslint ."),
("format", "prettier --write ."),
]
}
pub fn scripts_with_special_chars() -> Vec<(&'static str, &'static str)> {
vec![
("build:dev", "vite build --mode development"),
("build:prod", "vite build --mode production"),
("test:unit", "vitest --dir tests/unit"),
("test:e2e", "playwright test"),
("db:migrate", "prisma migrate dev"),
]
}
pub fn unicode_scripts() -> Vec<(&'static str, &'static str)> {
vec![
("build", "echo 构建"),
("test", "echo テスト"),
("lint", "echo проверка"),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_project() {
let temp = create_project(&[("dev", "vite"), ("build", "vite build")]);
let package_json = temp.path().join("package.json");
assert!(package_json.exists());
let content = fs::read_to_string(package_json).unwrap();
assert!(content.contains("\"dev\": \"vite\""));
assert!(content.contains("\"build\": \"vite build\""));
}
#[test]
fn test_create_project_with_lockfile() {
let temp = create_project_with_lockfile(&[("dev", "vite")], LockfileType::Pnpm);
assert!(temp.path().join("package.json").exists());
assert!(temp.path().join("pnpm-lock.yaml").exists());
}
#[test]
fn test_create_project_with_config() {
let config = r#"
[appearance]
icons = false
"#;
let temp = create_project_with_config(&[("dev", "vite")], config);
assert!(temp.path().join("package.json").exists());
assert!(temp.path().join(".nrsrc.toml").exists());
}
#[test]
fn test_lockfile_types() {
assert_eq!(LockfileType::Npm.filename(), "package-lock.json");
assert_eq!(LockfileType::Yarn.filename(), "yarn.lock");
assert_eq!(LockfileType::Pnpm.filename(), "pnpm-lock.yaml");
assert_eq!(LockfileType::Bun.filename(), "bun.lockb");
}
}