morph-cli 0.1.0

AST-based codebase migration and codemod tool for JavaScript and TypeScript projects.
Documentation
use std::path::PathBuf;

use crate::core::pipeline::context::{RecipeOrderer, validate_recipe_order};
use crate::core::recipe::Recipe;
use crate::recipes::commonjs_to_esm::CommonJsToEsmRecipe;

struct MockRecipe;

impl Recipe for MockRecipe {
    fn metadata(&self) -> &'static crate::core::recipe::RecipeMetadata {
        &crate::core::recipe::RecipeMetadata {
            name: "mock-recipe",
            description: "Mock recipe for testing",
            supported_extensions: &["js"],
            required_recipes: &[],
            incompatible_recipes: &[],
            should_run_before: &[],
            should_run_after: &[],
            maturity: crate::core::recipe::RecipeMaturity::Stable,
            category: crate::core::recipe::RecipeCategory::Migration,
            tags: &[],
        }
    }

    fn detect(
        &self,
        _root: &std::path::Path,
        _progress: &indicatif::ProgressBar,
    ) -> anyhow::Result<crate::core::recipe::DetectionReport> {
        Ok(crate::core::recipe::DetectionReport::default())
    }

    fn transform(
        &self,
        _report: &crate::core::recipe::DetectionReport,
        _options: crate::core::recipe::TransformOptions,
    ) -> anyhow::Result<crate::core::recipe::TransformReport> {
        Ok(crate::core::recipe::TransformReport::default())
    }
}

#[test]
fn test_recipe_orderer_respects_dependencies() {
    let orderer = RecipeOrderer::new().add_dependency("a", "c");
    let recipes: Vec<&dyn Recipe> = vec![&MockRecipe, &MockRecipe, &MockRecipe];
    let ordered = orderer.order(&recipes);
    assert_eq!(ordered.len(), 3);
}

#[test]
fn test_detect_incompatibilities_finds_duplicates() {
    let recipe = MockRecipe;
    let recipes: Vec<&dyn Recipe> = vec![&recipe, &recipe];
    let incompatibilities = validate_recipe_order(&recipes);
    assert!(!incompatibilities.is_empty());
}

#[test]
fn test_detect_incompatibilities_allows_different_recipes() {
    let recipes: Vec<&dyn Recipe> = vec![&MockRecipe, &CommonJsToEsmRecipe];
    let incompatibilities = validate_recipe_order(&recipes);
    assert!(incompatibilities.is_empty());
}

#[test]
fn test_pipeline_executor_single_recipe() {
    let executor = crate::core::pipeline::executor::PipelineExecutor::new(PathBuf::from("/tmp"))
        .add_recipe("commonjs-to-esm");
    assert!(executor.get_context().files.is_empty());
}

#[test]
fn test_pipeline_executor_multiple_recipes() {
    let executor = crate::core::pipeline::executor::PipelineExecutor::new(PathBuf::from("/tmp"))
        .add_recipe("commonjs-to-esm")
        .add_recipe("commonjs-to-esm");
    assert!(executor.get_events().is_empty());
}

#[test]
fn test_commonjs_to_esm_recipe_metadata() {
    let recipe = CommonJsToEsmRecipe;
    let metadata = recipe.metadata();
    assert_eq!(metadata.name, "commonjs-to-esm");
    assert!(!metadata.supported_extensions.is_empty());
}