huskies: merge 588_bug_wizard_generated_script_test_misses_frontend_tests_for_projects_with_a_frontend
This commit is contained in:
@@ -199,33 +199,69 @@ pub fn detect_components_toml(root: &Path) -> String {
|
|||||||
sections.join("\n")
|
sections.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Detect the appropriate Node.js test command for a directory containing `package.json`.
|
||||||
|
///
|
||||||
|
/// Reads the `package.json` content to identify known test runners (vitest, jest).
|
||||||
|
/// Falls back to `npm test` or `pnpm test` based on which lock file is present.
|
||||||
|
fn detect_node_test_cmd(pkg_dir: &Path) -> String {
|
||||||
|
let has_pnpm = pkg_dir.join("pnpm-lock.yaml").exists();
|
||||||
|
let content = std::fs::read_to_string(pkg_dir.join("package.json")).unwrap_or_default();
|
||||||
|
|
||||||
|
if content.contains("\"vitest\"") {
|
||||||
|
let pm = if has_pnpm { "pnpm" } else { "npx" };
|
||||||
|
return format!("{} vitest run", pm);
|
||||||
|
}
|
||||||
|
if content.contains("\"jest\"") {
|
||||||
|
let pm = if has_pnpm { "pnpm" } else { "npx" };
|
||||||
|
return format!("{} jest", pm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_pnpm {
|
||||||
|
"pnpm test".to_string()
|
||||||
|
} else {
|
||||||
|
"npm test".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate `script/test` content for a new project at `root`.
|
/// Generate `script/test` content for a new project at `root`.
|
||||||
///
|
///
|
||||||
/// Inspects well-known marker files to identify which tech stacks are present
|
/// Inspects well-known marker files to identify which tech stacks are present
|
||||||
/// and emits the appropriate test commands. Multi-stack projects get combined
|
/// and emits the appropriate test commands. Multi-stack projects get combined
|
||||||
/// commands run sequentially. Falls back to the generic stub when no markers
|
/// commands run sequentially. Falls back to the generic stub when no markers
|
||||||
/// are found so the scaffold is always valid.
|
/// are found so the scaffold is always valid.
|
||||||
|
///
|
||||||
|
/// For projects with a frontend in a known subdirectory (`frontend/`, `client/`),
|
||||||
|
/// the test runner is detected from the `package.json` (vitest, jest, npm, pnpm).
|
||||||
pub fn detect_script_test(root: &Path) -> String {
|
pub fn detect_script_test(root: &Path) -> String {
|
||||||
let mut commands: Vec<&str> = Vec::new();
|
let mut commands: Vec<String> = Vec::new();
|
||||||
|
|
||||||
if root.join("Cargo.toml").exists() {
|
if root.join("Cargo.toml").exists() {
|
||||||
commands.push("cargo test");
|
commands.push("cargo test".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if root.join("package.json").exists() {
|
if root.join("package.json").exists() {
|
||||||
if root.join("pnpm-lock.yaml").exists() {
|
if root.join("pnpm-lock.yaml").exists() {
|
||||||
commands.push("pnpm test");
|
commands.push("pnpm test".to_string());
|
||||||
} else {
|
} else {
|
||||||
commands.push("npm test");
|
commands.push("npm test".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect frontend in known subdirectories (e.g. frontend/, client/)
|
||||||
|
for subdir in &["frontend", "client"] {
|
||||||
|
let sub_path = root.join(subdir);
|
||||||
|
if sub_path.join("package.json").exists() {
|
||||||
|
let cmd = detect_node_test_cmd(&sub_path);
|
||||||
|
commands.push(format!("(cd {} && {})", subdir, cmd));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if root.join("pyproject.toml").exists() || root.join("requirements.txt").exists() {
|
if root.join("pyproject.toml").exists() || root.join("requirements.txt").exists() {
|
||||||
commands.push("pytest");
|
commands.push("pytest".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if root.join("go.mod").exists() {
|
if root.join("go.mod").exists() {
|
||||||
commands.push("go test ./...");
|
commands.push("go test ./...".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if commands.is_empty() {
|
if commands.is_empty() {
|
||||||
@@ -234,7 +270,7 @@ pub fn detect_script_test(root: &Path) -> String {
|
|||||||
|
|
||||||
let mut script = "#!/usr/bin/env bash\nset -euo pipefail\n\n".to_string();
|
let mut script = "#!/usr/bin/env bash\nset -euo pipefail\n\n".to_string();
|
||||||
for cmd in commands {
|
for cmd in commands {
|
||||||
script.push_str(cmd);
|
script.push_str(&cmd);
|
||||||
script.push('\n');
|
script.push('\n');
|
||||||
}
|
}
|
||||||
script
|
script
|
||||||
@@ -1170,6 +1206,141 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detect_script_test_frontend_subdir_with_vitest_uses_npx_vitest() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let frontend = dir.path().join("frontend");
|
||||||
|
fs::create_dir_all(&frontend).unwrap();
|
||||||
|
fs::write(
|
||||||
|
frontend.join("package.json"),
|
||||||
|
r#"{"devDependencies":{"vitest":"^1.0.0"},"scripts":{"test":"vitest run"}}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let script = detect_script_test(dir.path());
|
||||||
|
assert!(
|
||||||
|
script.contains("vitest run"),
|
||||||
|
"frontend with vitest should emit vitest run"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
script.contains("cd frontend"),
|
||||||
|
"should cd into the frontend directory"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!script.contains("No tests configured"),
|
||||||
|
"should not use stub when frontend is detected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detect_script_test_frontend_subdir_with_jest_uses_npx_jest() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let frontend = dir.path().join("frontend");
|
||||||
|
fs::create_dir_all(&frontend).unwrap();
|
||||||
|
fs::write(
|
||||||
|
frontend.join("package.json"),
|
||||||
|
r#"{"devDependencies":{"jest":"^29.0.0"},"scripts":{"test":"jest"}}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let script = detect_script_test(dir.path());
|
||||||
|
assert!(
|
||||||
|
script.contains("jest"),
|
||||||
|
"frontend with jest should emit jest"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
script.contains("cd frontend"),
|
||||||
|
"should cd into the frontend directory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detect_script_test_frontend_subdir_no_known_runner_uses_npm_test() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let frontend = dir.path().join("frontend");
|
||||||
|
fs::create_dir_all(&frontend).unwrap();
|
||||||
|
fs::write(
|
||||||
|
frontend.join("package.json"),
|
||||||
|
r#"{"scripts":{"test":"mocha"}}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let script = detect_script_test(dir.path());
|
||||||
|
assert!(
|
||||||
|
script.contains("npm test"),
|
||||||
|
"frontend without known runner should fall back to npm test"
|
||||||
|
);
|
||||||
|
assert!(script.contains("cd frontend"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detect_script_test_frontend_subdir_pnpm_uses_pnpm_vitest() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let frontend = dir.path().join("frontend");
|
||||||
|
fs::create_dir_all(&frontend).unwrap();
|
||||||
|
fs::write(
|
||||||
|
frontend.join("package.json"),
|
||||||
|
r#"{"devDependencies":{"vitest":"^1.0.0"}}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
fs::write(frontend.join("pnpm-lock.yaml"), "").unwrap();
|
||||||
|
|
||||||
|
let script = detect_script_test(dir.path());
|
||||||
|
assert!(
|
||||||
|
script.contains("pnpm vitest run"),
|
||||||
|
"pnpm frontend with vitest should use pnpm vitest run"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detect_script_test_rust_plus_frontend_subdir_both_included() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
fs::write(
|
||||||
|
dir.path().join("Cargo.toml"),
|
||||||
|
"[package]\nname = \"server\"\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let frontend = dir.path().join("frontend");
|
||||||
|
fs::create_dir_all(&frontend).unwrap();
|
||||||
|
fs::write(
|
||||||
|
frontend.join("package.json"),
|
||||||
|
r#"{"devDependencies":{"vitest":"^1.0.0"}}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let script = detect_script_test(dir.path());
|
||||||
|
assert!(
|
||||||
|
script.contains("cargo test"),
|
||||||
|
"Rust + frontend should include cargo test"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
script.contains("vitest run"),
|
||||||
|
"Rust + frontend should include vitest run"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
script.contains("cd frontend"),
|
||||||
|
"Rust + frontend should cd into frontend"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detect_script_test_client_subdir_detected() {
|
||||||
|
let dir = tempdir().unwrap();
|
||||||
|
let client = dir.path().join("client");
|
||||||
|
fs::create_dir_all(&client).unwrap();
|
||||||
|
fs::write(
|
||||||
|
client.join("package.json"),
|
||||||
|
r#"{"scripts":{"test":"jest"}}"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let script = detect_script_test(dir.path());
|
||||||
|
assert!(
|
||||||
|
script.contains("cd client"),
|
||||||
|
"client/ subdir should also be detected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn detect_script_test_output_starts_with_shebang() {
|
fn detect_script_test_output_starts_with_shebang() {
|
||||||
let dir = tempdir().unwrap();
|
let dir = tempdir().unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user