Skip to main content

xacli_testing/testing/
test.rs

1use xacli_core::InputEvent;
2
3use crate::{spec, Asserter, Result};
4
5/// A test case for CLI application testing
6///
7/// TestCase is a pure data structure that declares what should be tested.
8/// It contains the test name, command path, arguments, input events, and assertions.
9#[derive(Default)]
10pub struct TestCase {
11    /// Name of the test case
12    pub name: String,
13
14    /// Command path (subcommands) to execute
15    pub commands: Vec<String>,
16
17    /// Command line arguments to pass to the application
18    pub args: Vec<String>,
19
20    /// Input events to simulate (for interactive testing)
21    pub input_events: Vec<InputEvent>,
22
23    /// Assertions to perform after execution
24    pub assertions: Vec<Box<dyn Asserter>>,
25}
26
27impl TestCase {
28    /// Create a new test case with the given name
29    pub fn new(name: impl Into<String>) -> Self {
30        Self {
31            name: name.into(),
32            ..Default::default()
33        }
34    }
35
36    /// Set the command path (subcommands) to execute
37    pub fn commands(mut self, commands: Vec<String>) -> Self {
38        self.commands = commands;
39        self
40    }
41
42    /// Set the command line arguments
43    pub fn args(mut self, args: Vec<String>) -> Self {
44        self.args = args;
45        self
46    }
47
48    /// Set the input events for interactive testing
49    pub fn input_events(mut self, events: Vec<InputEvent>) -> Self {
50        self.input_events = events;
51        self
52    }
53
54    /// Set the assertions to perform
55    pub fn assertions(mut self, assertions: Vec<Box<dyn Asserter>>) -> Self {
56        self.assertions = assertions;
57        self
58    }
59}
60
61impl std::fmt::Debug for TestCase {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        f.debug_struct("TestCase")
64            .field("name", &self.name)
65            .field("commands", &self.commands)
66            .field("args", &self.args)
67            .field("input_events", &self.input_events)
68            .field(
69                "assertions",
70                &format!("[{} asserters]", self.assertions.len()),
71            )
72            .finish()
73    }
74}
75
76/// Convert spec TestCase to testing TestCase
77///
78/// This conversion combines commands and args into a single args vector,
79/// converts input events to core events (handling repeat),
80/// and converts assertion configs to asserters.
81impl TryFrom<(&str, &spec::TestCase)> for TestCase {
82    type Error = crate::TestingError;
83
84    fn try_from((name, spec_case): (&str, &spec::TestCase)) -> Result<Self> {
85        // Convert input events
86        let mut events = Vec::new();
87        for input_event in &spec_case.input_events {
88            events.extend(input_event.to_core_events()?);
89        }
90
91        // Convert assertions
92        let mut assertions = Vec::new();
93        for assertion in &spec_case.assertions {
94            assertions.push(assertion.to_asserter()?);
95        }
96
97        Ok(TestCase::new(name)
98            .commands(spec_case.commands.clone())
99            .args(spec_case.args.clone())
100            .input_events(events)
101            .assertions(assertions))
102    }
103}