Input tests

This commit is contained in:
veeso
2021-03-06 15:46:11 +01:00
parent c1780230e9
commit 5b832cea8b
3 changed files with 198 additions and 20 deletions

View File

@@ -333,6 +333,8 @@ mod tests {
// Verify states // Verify states
assert_eq!(component.states.list_index, 0); assert_eq!(component.states.list_index, 0);
assert_eq!(component.states.list_len, 3); assert_eq!(component.states.list_len, 3);
// get value
assert_eq!(component.get_value(), Payload::Unsigned(0));
// Render // Render
assert_eq!(component.render().unwrap().cursor, 0); assert_eq!(component.render().unwrap().cursor, 0);
// Handle inputs // Handle inputs

View File

@@ -59,22 +59,25 @@ impl OwnStates {
/// ### append /// ### append
/// ///
/// Append, if possible according to input type, the character to the input vec /// Append, if possible according to input type, the character to the input vec
pub fn append(&mut self, ch: char, itype: InputType) { pub fn append(&mut self, ch: char, itype: InputType, max_len: Option<usize>) {
match itype { // Check if max length has been reached
InputType::Signed => { if self.input.len() < max_len.unwrap_or(usize::MAX) {
if ch.is_digit(10) { match itype {
// Must be digit InputType::Number => {
self.input.push(ch); if ch.is_digit(10) {
// Must be digit
self.input.insert(self.cursor, ch);
// Increment cursor
self.cursor += 1;
}
}
_ => {
// No rule
self.input.insert(self.cursor, ch);
// Increment cursor // Increment cursor
self.cursor += 1; self.cursor += 1;
} }
} }
_ => {
// No rule
self.input.push(ch);
// Increment cursor
self.cursor += 1;
}
} }
} }
@@ -93,7 +96,7 @@ impl OwnStates {
/// ///
/// Delete element at cursor /// Delete element at cursor
pub fn delete(&mut self) { pub fn delete(&mut self) {
if self.cursor + 1 < self.input.len() { if self.cursor < self.input.len() {
self.input.remove(self.cursor); self.input.remove(self.cursor);
} }
} }
@@ -102,7 +105,7 @@ impl OwnStates {
/// ///
/// Increment cursor value by one if possible /// Increment cursor value by one if possible
pub fn incr_cursor(&mut self) { pub fn incr_cursor(&mut self) {
if self.cursor + 1 < self.input.len() { if self.cursor + 1 <= self.input.len() {
self.cursor += 1; self.cursor += 1;
} }
} }
@@ -155,7 +158,7 @@ impl Input {
// Set state value from props // Set state value from props
if let PropValue::Str(val) = props.value.clone() { if let PropValue::Str(val) = props.value.clone() {
for ch in val.chars() { for ch in val.chars() {
states.append(ch, props.input_type); states.append(ch, props.input_type, props.input_len);
} }
} }
Input { props, states } Input { props, states }
@@ -248,7 +251,8 @@ impl Component for Input {
&& !key.modifiers.intersects(KeyModifiers::ALT) && !key.modifiers.intersects(KeyModifiers::ALT)
{ {
// Push char to input // Push char to input
self.states.append(ch, self.props.input_type); self.states
.append(ch, self.props.input_type, self.props.input_len);
// Message none // Message none
Msg::None Msg::None
} else { } else {
@@ -269,7 +273,7 @@ impl Component for Input {
/// Returns the value as string or as a number based on the input value /// Returns the value as string or as a number based on the input value
fn get_value(&self) -> Payload { fn get_value(&self) -> Payload {
match self.props.input_type { match self.props.input_type {
InputType::Signed => { InputType::Number => {
Payload::Unsigned(self.states.get_value().parse::<usize>().ok().unwrap_or(0)) Payload::Unsigned(self.states.get_value().parse::<usize>().ok().unwrap_or(0))
} }
_ => Payload::Text(self.states.get_value()), _ => Payload::Text(self.states.get_value()),
@@ -306,10 +310,182 @@ impl Component for Input {
mod tests { mod tests {
use super::*; use super::*;
use crate::ui::layout::props::TextParts;
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
#[test] #[test]
fn test_ui_layout_components_input_text() {} fn test_ui_layout_components_input_text() {
// Instantiate Input with value
let mut component: Input = Input::new(
PropsBuilder::default()
.with_input(InputType::Text)
.with_input_len(5)
.with_value(PropValue::Str(String::from("home")))
.build(),
);
// Verify initial state
assert_eq!(component.states.cursor, 4);
assert_eq!(component.states.input.len(), 4);
// Focus
assert_eq!(component.states.focus, false);
component.active();
assert_eq!(component.states.focus, true);
component.blur();
assert_eq!(component.states.focus, false);
// Should umount
assert_eq!(component.should_umount(), false);
// Get value
assert_eq!(component.get_value(), Payload::Text(String::from("home")));
// Render
assert_eq!(component.render().unwrap().cursor, 4);
// Handle events
// Try key with ctrl
assert_eq!(
component.on(InputEvent::Key(KeyEvent::new(
KeyCode::Char('a'),
KeyModifiers::CONTROL
))),
Msg::OnKey(KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)),
);
// String shouldn't have changed
assert_eq!(component.get_value(), Payload::Text(String::from("home")));
assert_eq!(component.render().unwrap().cursor, 4);
// Character
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Char('/')))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("home/")));
assert_eq!(component.render().unwrap().cursor, 5);
// Verify max length (shouldn't push any character)
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Char('a')))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("home/")));
assert_eq!(component.render().unwrap().cursor, 5);
// Enter
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Enter))),
Msg::OnSubmit(Payload::Text(String::from("home/")))
);
// Backspace
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Backspace))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("home")));
assert_eq!(component.render().unwrap().cursor, 4);
// Check backspace at 0
component.states.input = vec!['h'];
component.states.cursor = 1;
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Backspace))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("")));
assert_eq!(component.render().unwrap().cursor, 0);
// Another one...
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Backspace))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("")));
assert_eq!(component.render().unwrap().cursor, 0);
// See del behaviour here
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Delete))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("")));
assert_eq!(component.render().unwrap().cursor, 0);
// Check del behaviour
component.states.input = vec!['h', 'e'];
component.states.cursor = 1;
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Delete))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("h")));
assert_eq!(component.render().unwrap().cursor, 1); // Shouldn't move
// Another one (should do nothing)
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Delete))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("h")));
assert_eq!(component.render().unwrap().cursor, 1); // Shouldn't move
// Move cursor right
component.states.input = vec!['h', 'e', 'l', 'l', 'o'];
component.states.cursor = 1;
component.props.input_len = Some(16); // Let's change length
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Right))), // between 'e' and 'l'
Msg::None
);
assert_eq!(component.render().unwrap().cursor, 2); // Should increment
// Put a character here
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Char('a')))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Text(String::from("heallo")));
assert_eq!(component.render().unwrap().cursor, 3);
// Move left
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Left))),
Msg::None
);
assert_eq!(component.render().unwrap().cursor, 2); // Should decrement
// Go at the end
component.states.cursor = 6;
// Move right
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Right))),
Msg::None
);
assert_eq!(component.render().unwrap().cursor, 6); // Should stay
// Move left
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Left))),
Msg::None
);
assert_eq!(component.render().unwrap().cursor, 5); // Should decrement
// Go at the beginning
component.states.cursor = 0;
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Left))),
Msg::None
);
assert_eq!(component.render().unwrap().cursor, 0); // Should stay
}
#[test]
fn test_ui_layout_components_input_number() {
// Instantiate Input with value
let mut component: Input = Input::new(
PropsBuilder::default()
.with_input(InputType::Number)
.with_input_len(5)
.with_value(PropValue::Str(String::from("3000")))
.build(),
);
// Verify initial state
assert_eq!(component.states.cursor, 4);
assert_eq!(component.states.input.len(), 4);
// Push a non numeric value
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Char('a')))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Unsigned(3000));
assert_eq!(component.render().unwrap().cursor, 4);
// Push a number
assert_eq!(
component.on(InputEvent::Key(KeyEvent::from(KeyCode::Char('1')))),
Msg::None
);
assert_eq!(component.get_value(), Payload::Unsigned(30001));
assert_eq!(component.render().unwrap().cursor, 5);
}
} }

View File

@@ -282,7 +282,7 @@ pub enum PropValue {
#[derive(Clone, Copy, PartialEq, std::fmt::Debug)] #[derive(Clone, Copy, PartialEq, std::fmt::Debug)]
pub enum InputType { pub enum InputType {
Text, Text,
Signed, Number,
Password, Password,
} }