UPDATE: Adds 'user model' API (#27)

* bump version for documentation
* Fixes wrong doc comment
* renames old APIs to be consistent
This commit is contained in:
Nicolás Hatcher Andrés
2024-04-03 22:41:15 +02:00
committed by GitHub
parent e9fc41541b
commit d445553d85
45 changed files with 3233 additions and 268 deletions

View File

@@ -0,0 +1,9 @@
mod test_add_delete_sheets;
mod test_clear_cells;
mod test_diff_queue;
mod test_evaluation;
mod test_general;
mod test_rename_sheet;
mod test_row_column;
mod test_styles;
mod test_undo_redo;

View File

@@ -0,0 +1,58 @@
#![allow(clippy::unwrap_used)]
use crate::{constants::DEFAULT_COLUMN_WIDTH, UserModel};
#[test]
fn add_undo_redo() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model.new_sheet();
model.set_user_input(1, 1, 1, "=1 + 1").unwrap();
model.set_user_input(1, 1, 2, "=A1*3").unwrap();
model
.set_column_width(1, 5, 5.0 * DEFAULT_COLUMN_WIDTH)
.unwrap();
model.new_sheet();
model.set_user_input(2, 1, 1, "=Sheet2!B1").unwrap();
model.undo().unwrap();
model.undo().unwrap();
assert!(model.get_formatted_cell_value(2, 1, 1).is_err());
model.redo().unwrap();
model.redo().unwrap();
assert_eq!(model.get_formatted_cell_value(2, 1, 1), Ok("6".to_string()));
model.delete_sheet(1).unwrap();
assert!(!model.can_undo());
assert!(!model.can_redo());
}
#[test]
fn new_sheet_propagates() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model.new_sheet();
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
let sheets_info = model2.get_sheets_info();
assert_eq!(sheets_info.len(), 2);
}
#[test]
fn delete_sheet_propagates() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model.new_sheet();
model.delete_sheet(0).unwrap();
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
let sheets_info = model2.get_sheets_info();
assert_eq!(sheets_info.len(), 1);
}

View File

@@ -0,0 +1,91 @@
#![allow(clippy::unwrap_used)]
use crate::{expressions::types::Area, UserModel};
#[test]
fn basic() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model.set_user_input(0, 1, 1, "100$").unwrap();
model
.range_clear_contents(&Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
})
.unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
model.undo().unwrap();
assert_eq!(
model.get_formatted_cell_value(0, 1, 1),
Ok("100$".to_string())
);
model.redo().unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
model.set_user_input(0, 1, 1, "300").unwrap();
// clear contents keeps the formatting
assert_eq!(
model.get_formatted_cell_value(0, 1, 1),
Ok("300$".to_string())
);
model
.range_clear_all(&Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
})
.unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
model.undo().unwrap();
assert_eq!(
model.get_formatted_cell_value(0, 1, 1),
Ok("300$".to_string())
);
model.redo().unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
model.set_user_input(0, 1, 1, "400").unwrap();
// clear contents keeps the formatting
assert_eq!(
model.get_formatted_cell_value(0, 1, 1),
Ok("400".to_string())
);
}
#[test]
fn clear_empty_cell() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model
.range_clear_contents(&Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
})
.unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
model.undo().unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
}
#[test]
fn clear_all_empty_cell() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model
.range_clear_all(&Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
})
.unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
model.undo().unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
}

View File

@@ -0,0 +1,159 @@
use crate::{
constants::{DEFAULT_COLUMN_WIDTH, DEFAULT_ROW_HEIGHT},
test::util::new_empty_model,
UserModel,
};
#[test]
fn send_queue() {
let mut model1 = UserModel::from_model(new_empty_model());
let width = model1.get_column_width(0, 3).unwrap() * 3.0;
model1.set_column_width(0, 3, width).unwrap();
model1.set_user_input(0, 1, 2, "Hello IronCalc!").unwrap();
let send_queue = model1.flush_send_queue();
let mut model2 = UserModel::from_model(new_empty_model());
model2.apply_external_diffs(&send_queue).unwrap();
assert_eq!(model2.get_column_width(0, 3), Ok(width));
assert_eq!(
model2.get_formatted_cell_value(0, 1, 2),
Ok("Hello IronCalc!".to_string())
);
}
#[test]
fn apply_external_diffs_wrong_str() {
let mut model1 = UserModel::from_model(new_empty_model());
assert!(model1.apply_external_diffs("invalid").is_err());
}
#[test]
fn queue_undo_redo() {
let mut model1 = UserModel::from_model(new_empty_model());
let width = model1.get_column_width(0, 3).unwrap() * 3.0;
model1.set_column_width(0, 3, width).unwrap();
model1.set_user_input(0, 1, 2, "Hello IronCalc!").unwrap();
assert!(model1.undo().is_ok());
assert!(model1.redo().is_ok());
let send_queue = model1.flush_send_queue();
let mut model2 = UserModel::from_model(new_empty_model());
model2.apply_external_diffs(&send_queue).unwrap();
assert_eq!(model2.get_column_width(0, 3), Ok(width));
assert_eq!(
model2.get_formatted_cell_value(0, 1, 2),
Ok("Hello IronCalc!".to_string())
);
}
#[test]
fn queue_undo_redo_multiple() {
let mut model1 = UserModel::from_model(new_empty_model());
// do a bunch of things
model1.set_frozen_columns_count(0, 5).unwrap();
model1.set_frozen_rows_count(0, 6).unwrap();
model1.set_column_width(0, 7, 300.0).unwrap();
model1.set_row_height(0, 23, 123.0).unwrap();
model1.set_user_input(0, 55, 55, "=42+8").unwrap();
for row in 1..5 {
model1.set_user_input(0, row, 17, "=ROW()").unwrap();
}
model1.insert_row(0, 3).unwrap();
model1.insert_row(0, 3).unwrap();
// undo al of them
while model1.can_undo() {
model1.undo().unwrap();
}
// check it is an empty model
assert_eq!(model1.get_frozen_columns_count(0), Ok(0));
assert_eq!(model1.get_frozen_rows_count(0), Ok(0));
assert_eq!(model1.get_column_width(0, 7), Ok(DEFAULT_COLUMN_WIDTH));
assert_eq!(
model1.get_formatted_cell_value(0, 55, 55),
Ok("".to_string())
);
assert_eq!(model1.get_row_height(0, 23), Ok(DEFAULT_ROW_HEIGHT));
assert_eq!(
model1.get_formatted_cell_value(0, 57, 55),
Ok("".to_string())
);
assert_eq!(model1.get_row_height(0, 25), Ok(DEFAULT_ROW_HEIGHT));
// redo all of them
while model1.can_redo() {
model1.redo().unwrap();
}
// now send all this to a new model
let send_queue = model1.flush_send_queue();
let mut model2 = UserModel::from_model(new_empty_model());
model2.apply_external_diffs(&send_queue).unwrap();
// Check everything is as expected
assert_eq!(model2.get_frozen_columns_count(0), Ok(5));
assert_eq!(model2.get_frozen_rows_count(0), Ok(6));
assert_eq!(model2.get_column_width(0, 7), Ok(300.0));
// I inserted two rows
assert_eq!(
model2.get_formatted_cell_value(0, 57, 55),
Ok("50".to_string())
);
assert_eq!(model2.get_row_height(0, 25), Ok(123.0));
assert_eq!(
model2.get_formatted_cell_value(0, 1, 17),
Ok("1".to_string())
);
assert_eq!(
model2.get_formatted_cell_value(0, 2, 17),
Ok("2".to_string())
);
assert_eq!(
model2.get_formatted_cell_value(0, 3, 17),
Ok("".to_string())
);
assert_eq!(
model2.get_formatted_cell_value(0, 4, 17),
Ok("".to_string())
);
assert_eq!(
model2.get_formatted_cell_value(0, 5, 17),
Ok("5".to_string())
);
assert_eq!(
model2.get_formatted_cell_value(0, 6, 17),
Ok("6".to_string())
);
}
#[test]
fn new_sheet() {
let mut model1 = UserModel::from_model(new_empty_model());
model1.new_sheet();
model1.set_user_input(0, 1, 1, "42").unwrap();
model1.set_user_input(1, 1, 1, "=Sheet1!A1*2").unwrap();
let send_queue = model1.flush_send_queue();
let mut model2 = UserModel::from_model(new_empty_model());
model2.apply_external_diffs(&send_queue).unwrap();
assert_eq!(
model2.get_formatted_cell_value(1, 1, 1),
Ok("84".to_string())
);
}
#[test]
fn wrong_diffs_handled() {
let mut model = UserModel::from_model(new_empty_model());
assert!(model.apply_external_diffs("Hello world").is_err());
}

View File

@@ -0,0 +1,31 @@
#![allow(clippy::unwrap_used)]
use crate::UserModel;
#[test]
fn model_evaluates_automatically() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model.set_user_input(0, 1, 1, "=1 + 1").unwrap();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("2".to_string()));
assert_eq!(model.get_cell_content(0, 1, 1), Ok("=1+1".to_string()));
}
#[test]
fn pause_resume_evaluation() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model.pause_evaluation();
model.set_user_input(0, 1, 1, "=1+1").unwrap();
assert_eq!(
model.get_formatted_cell_value(0, 1, 1),
Ok("#ERROR!".to_string())
);
model.evaluate();
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("2".to_string()));
assert_eq!(model.get_cell_content(0, 1, 1), Ok("=1+1".to_string()));
model.resume_evaluation();
model.set_user_input(0, 2, 1, "=1+4").unwrap();
assert_eq!(model.get_formatted_cell_value(0, 2, 1), Ok("5".to_string()));
}

View File

@@ -0,0 +1,103 @@
#![allow(clippy::unwrap_used)]
use crate::constants::{LAST_COLUMN, LAST_ROW};
use crate::test::util::new_empty_model;
use crate::UserModel;
#[test]
fn set_user_input_errors() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
// Wrong sheet
assert!(model.set_user_input(1, 1, 1, "1").is_err());
// Wrong row
assert!(model.set_user_input(0, 0, 1, "1").is_err());
// Wrong column
assert!(model.set_user_input(0, 1, 0, "1").is_err());
// row too large
assert!(model.set_user_input(0, LAST_ROW, 1, "1").is_ok());
assert!(model.set_user_input(0, LAST_ROW + 1, 1, "1").is_err());
// column too large
assert!(model.set_user_input(0, 1, LAST_COLUMN, "1").is_ok());
assert!(model.set_user_input(0, 1, LAST_COLUMN + 1, "1").is_err());
}
#[test]
fn insert_remove_rows() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
let height = model.get_row_height(0, 5).unwrap();
// Insert some data in row 5 (and change the style)
assert!(model.set_user_input(0, 5, 1, "100$").is_ok());
// Change the height of the column
assert!(model.set_row_height(0, 5, 3.0 * height).is_ok());
// remove the row
assert!(model.delete_row(0, 5).is_ok());
// Row 5 has now the normal height
assert_eq!(model.get_row_height(0, 5), Ok(height));
// There is no value in A5
assert_eq!(model.get_formatted_cell_value(0, 5, 1), Ok("".to_string()));
// Setting a value will not format it
assert!(model.set_user_input(0, 5, 1, "125").is_ok());
assert_eq!(
model.get_formatted_cell_value(0, 5, 1),
Ok("125".to_string())
);
// undo twice
assert!(model.undo().is_ok());
assert!(model.undo().is_ok());
assert_eq!(model.get_row_height(0, 5), Ok(3.0 * height));
assert_eq!(
model.get_formatted_cell_value(0, 5, 1),
Ok("100$".to_string())
);
}
#[test]
fn insert_remove_columns() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
// column E
let column_width = model.get_column_width(0, 5).unwrap();
println!("{column_width}");
// Insert some data in row 5 (and change the style) in E1
assert!(model.set_user_input(0, 1, 5, "100$").is_ok());
// Change the width of the column
assert!(model.set_column_width(0, 5, 3.0 * column_width).is_ok());
assert_eq!(model.get_column_width(0, 5).unwrap(), 3.0 * column_width);
// remove the column
assert!(model.delete_column(0, 5).is_ok());
// Column 5 has now the normal width
assert_eq!(model.get_column_width(0, 5), Ok(column_width));
// There is no value in E5
assert_eq!(model.get_formatted_cell_value(0, 1, 5), Ok("".to_string()));
// Setting a value will not format it
assert!(model.set_user_input(0, 1, 5, "125").is_ok());
assert_eq!(
model.get_formatted_cell_value(0, 1, 5),
Ok("125".to_string())
);
// undo twice (set_user_input and delete_column)
assert!(model.undo().is_ok());
assert!(model.undo().is_ok());
assert_eq!(model.get_column_width(0, 5), Ok(3.0 * column_width));
assert_eq!(
model.get_formatted_cell_value(0, 1, 5),
Ok("100$".to_string())
);
}
#[test]
fn delete_remove_cell() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let (sheet, row, column) = (0, 1, 1);
model.set_user_input(sheet, row, column, "100$").unwrap();
}

View File

@@ -0,0 +1,39 @@
#![allow(clippy::unwrap_used)]
use crate::UserModel;
#[test]
fn basic_rename() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model.rename_sheet(0, "NewSheet").unwrap();
assert_eq!(model.get_sheets_info()[0].name, "NewSheet");
}
#[test]
fn undo_redo() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
model.rename_sheet(0, "NewSheet").unwrap();
model.undo().unwrap();
assert_eq!(model.get_sheets_info()[0].name, "Sheet1");
model.redo().unwrap();
assert_eq!(model.get_sheets_info()[0].name, "NewSheet");
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
assert_eq!(model.get_sheets_info()[0].name, "NewSheet");
}
#[test]
fn errors() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
assert_eq!(
model.rename_sheet(0, ""),
Err("Invalid name for a sheet: ''.".to_string())
);
assert_eq!(
model.rename_sheet(1, "Hello"),
Err("Invalid sheet index".to_string())
);
}

View File

@@ -0,0 +1,156 @@
#![allow(clippy::unwrap_used)]
use crate::{
constants::{DEFAULT_COLUMN_WIDTH, DEFAULT_ROW_HEIGHT, LAST_COLUMN},
test::util::new_empty_model,
UserModel,
};
#[test]
fn simple_insert_row() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
let (sheet, column) = (0, 5);
for row in 1..5 {
assert!(model.set_user_input(sheet, row, column, "123").is_ok());
}
assert!(model.insert_row(sheet, 3).is_ok());
assert_eq!(
model.get_formatted_cell_value(sheet, 3, column).unwrap(),
""
);
assert!(model.undo().is_ok());
assert_eq!(
model.get_formatted_cell_value(sheet, 3, column).unwrap(),
"123"
);
assert!(model.redo().is_ok());
assert_eq!(
model.get_formatted_cell_value(sheet, 3, column).unwrap(),
""
);
}
#[test]
fn simple_insert_column() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
let (sheet, row) = (0, 5);
for column in 1..5 {
assert!(model.set_user_input(sheet, row, column, "123").is_ok());
}
assert!(model.insert_column(sheet, 3).is_ok());
assert_eq!(model.get_formatted_cell_value(sheet, row, 3).unwrap(), "");
assert!(model.undo().is_ok());
assert_eq!(
model.get_formatted_cell_value(sheet, row, 3).unwrap(),
"123"
);
assert!(model.redo().is_ok());
assert_eq!(model.get_formatted_cell_value(sheet, row, 3).unwrap(), "");
}
#[test]
fn simple_delete_column() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
model.set_user_input(0, 1, 5, "3").unwrap();
model.set_user_input(0, 2, 5, "=E1*2").unwrap();
model
.set_column_width(0, 5, DEFAULT_COLUMN_WIDTH * 3.0)
.unwrap();
model.delete_column(0, 5).unwrap();
assert_eq!(model.get_formatted_cell_value(0, 2, 5), Ok("".to_string()));
assert_eq!(model.get_column_width(0, 5), Ok(DEFAULT_COLUMN_WIDTH));
model.undo().unwrap();
assert_eq!(model.get_formatted_cell_value(0, 2, 5), Ok("6".to_string()));
assert_eq!(model.get_column_width(0, 5), Ok(DEFAULT_COLUMN_WIDTH * 3.0));
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
assert_eq!(
model2.get_formatted_cell_value(0, 2, 5),
Ok("6".to_string())
);
assert_eq!(
model2.get_column_width(0, 5),
Ok(DEFAULT_COLUMN_WIDTH * 3.0)
);
}
#[test]
fn delete_column_errors() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
assert_eq!(
model.delete_column(1, 1),
Err("Invalid sheet index".to_string())
);
assert_eq!(
model.delete_column(0, 0),
Err("Column number '0' is not valid.".to_string())
);
assert_eq!(
model.delete_column(0, LAST_COLUMN + 1),
Err("Column number '16385' is not valid.".to_string())
);
assert_eq!(model.delete_column(0, LAST_COLUMN), Ok(()));
}
#[test]
fn simple_delete_row() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
model.set_user_input(0, 15, 4, "3").unwrap();
model.set_user_input(0, 15, 6, "=D15*2").unwrap();
model
.set_row_height(0, 15, DEFAULT_ROW_HEIGHT * 3.0)
.unwrap();
model.delete_row(0, 15).unwrap();
assert_eq!(model.get_formatted_cell_value(0, 15, 6), Ok("".to_string()));
assert_eq!(model.get_row_height(0, 15), Ok(DEFAULT_ROW_HEIGHT));
model.undo().unwrap();
assert_eq!(
model.get_formatted_cell_value(0, 15, 6),
Ok("6".to_string())
);
assert_eq!(model.get_row_height(0, 15), Ok(DEFAULT_ROW_HEIGHT * 3.0));
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
assert_eq!(
model2.get_formatted_cell_value(0, 15, 6),
Ok("6".to_string())
);
assert_eq!(model2.get_row_height(0, 15), Ok(DEFAULT_ROW_HEIGHT * 3.0));
}
#[test]
fn simple_delete_row_no_style() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
model.set_user_input(0, 15, 4, "3").unwrap();
model.set_user_input(0, 15, 6, "=D15*2").unwrap();
model.delete_row(0, 15).unwrap();
assert_eq!(model.get_formatted_cell_value(0, 15, 6), Ok("".to_string()));
}

View File

@@ -0,0 +1,711 @@
#![allow(clippy::unwrap_used)]
use crate::{
expressions::types::Area,
types::{Alignment, BorderItem, BorderStyle, HorizontalAlignment, VerticalAlignment},
UserModel,
};
#[test]
fn basic_fonts() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(!style.font.i);
assert!(!style.font.b);
assert!(!style.font.u);
assert!(!style.font.strike);
assert_eq!(style.font.color, Some("#000000".to_owned()));
// bold
model.update_range_style(&range, "font.b", "true").unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(style.font.b);
// italics
model.update_range_style(&range, "font.i", "true").unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(style.font.i);
// underline
model.update_range_style(&range, "font.u", "true").unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(style.font.u);
// strike
model
.update_range_style(&range, "font.strike", "true")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(style.font.strike);
// color
model
.update_range_style(&range, "font.color", "#F1F1F1")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.font.color, Some("#F1F1F1".to_owned()));
while model.can_undo() {
model.undo().unwrap();
}
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(!style.font.i);
assert!(!style.font.b);
assert!(!style.font.u);
assert!(!style.font.strike);
assert_eq!(style.font.color, Some("#000000".to_owned()));
while model.can_redo() {
model.redo().unwrap();
}
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(style.font.i);
assert!(style.font.b);
assert!(style.font.u);
assert!(style.font.strike);
assert_eq!(style.font.color, Some("#F1F1F1".to_owned()));
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
let style = model2.get_cell_style(0, 1, 1).unwrap();
assert!(style.font.i);
assert!(style.font.b);
assert!(style.font.u);
assert!(style.font.strike);
assert_eq!(style.font.color, Some("#F1F1F1".to_owned()));
}
#[test]
fn font_errors() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
assert_eq!(
model.update_range_style(&range, "font.b", "True"),
Err("Invalid value for boolean: 'True'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "font.i", "FALSE"),
Err("Invalid value for boolean: 'FALSE'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "font.bold", "true"),
Err("Invalid style path: 'font.bold'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "font.strike", ""),
Err("Invalid value for boolean: ''.".to_string())
);
// There is no cast for booleans
assert_eq!(
model.update_range_style(&range, "font.b", "1"),
Err("Invalid value for boolean: '1'.".to_string())
);
// colors don't work by name
assert_eq!(
model.update_range_style(&range, "font.color", "blue"),
Err("Invalid color: 'blue'.".to_string())
);
// No short form
assert_eq!(
model.update_range_style(&range, "font.color", "#FFF"),
Err("Invalid color: '#FFF'.".to_string())
);
}
#[test]
fn basic_fill() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.fill.bg_color, None);
// bg_color
model
.update_range_style(&range, "fill.bg_color", "#F2F2F2")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.fill.bg_color, Some("#F2F2F2".to_owned()));
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
let style = model2.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.fill.bg_color, Some("#F2F2F2".to_owned()));
}
#[test]
fn fill_errors() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
assert!(model
.update_range_style(&range, "fill.bg_color", "#FFF")
.is_err());
}
#[test]
fn basic_format() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.num_fmt, "general");
model
.update_range_style(&range, "num_fmt", "$#,##0.0000")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.num_fmt, "$#,##0.0000");
model.undo().unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.num_fmt, "general");
model.redo().unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.num_fmt, "$#,##0.0000");
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
let style = model2.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.num_fmt, "$#,##0.0000");
}
#[test]
fn basic_borders() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
model
.update_range_style(&range, "border.left", "thin,#F1F1F1")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::Thin,
color: Some("#F1F1F1".to_owned()),
})
);
model
.update_range_style(&range, "border.left", "thin,")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::Thin,
color: None,
})
);
model
.update_range_style(&range, "border.right", "dotted,#F1F1F2")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.right,
Some(BorderItem {
style: BorderStyle::Dotted,
color: Some("#F1F1F2".to_owned()),
})
);
model
.update_range_style(&range, "border.top", "double,#F1F1F3")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.top,
Some(BorderItem {
style: BorderStyle::Double,
color: Some("#F1F1F3".to_owned()),
})
);
model
.update_range_style(&range, "border.bottom", "medium,#F1F1F4")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.bottom,
Some(BorderItem {
style: BorderStyle::Medium,
color: Some("#F1F1F4".to_owned()),
})
);
while model.can_undo() {
model.undo().unwrap();
}
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.border.left, None);
assert_eq!(style.border.top, None);
assert_eq!(style.border.right, None);
assert_eq!(style.border.bottom, None);
while model.can_redo() {
model.redo().unwrap();
}
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::Thin,
color: None,
})
);
assert_eq!(
style.border.right,
Some(BorderItem {
style: BorderStyle::Dotted,
color: Some("#F1F1F2".to_owned()),
})
);
assert_eq!(
style.border.top,
Some(BorderItem {
style: BorderStyle::Double,
color: Some("#F1F1F3".to_owned()),
})
);
assert_eq!(
style.border.bottom,
Some(BorderItem {
style: BorderStyle::Medium,
color: Some("#F1F1F4".to_owned()),
})
);
let send_queue = model.flush_send_queue();
let mut model2 = UserModel::new_empty("model", "en", "UTC").unwrap();
model2.apply_external_diffs(&send_queue).unwrap();
let style = model2.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::Thin,
color: None,
})
);
assert_eq!(
style.border.right,
Some(BorderItem {
style: BorderStyle::Dotted,
color: Some("#F1F1F2".to_owned()),
})
);
assert_eq!(
style.border.top,
Some(BorderItem {
style: BorderStyle::Double,
color: Some("#F1F1F3".to_owned()),
})
);
assert_eq!(
style.border.bottom,
Some(BorderItem {
style: BorderStyle::Medium,
color: Some("#F1F1F4".to_owned()),
})
);
}
#[test]
fn basic_alignment() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
assert_eq!(alignment, None);
model
.update_range_style(&range, "alignment.horizontal", "center")
.unwrap();
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
assert_eq!(
alignment,
Some(Alignment {
horizontal: HorizontalAlignment::Center,
vertical: VerticalAlignment::Bottom,
wrap_text: false
})
);
model
.update_range_style(&range, "alignment.horizontal", "centerContinuous")
.unwrap();
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
assert_eq!(
alignment,
Some(Alignment {
horizontal: HorizontalAlignment::CenterContinuous,
vertical: VerticalAlignment::Bottom,
wrap_text: false
})
);
let range = Area {
sheet: 0,
row: 2,
column: 2,
width: 1,
height: 1,
};
model
.update_range_style(&range, "alignment.vertical", "distributed")
.unwrap();
let alignment = model.get_cell_style(0, 2, 2).unwrap().alignment;
assert_eq!(
alignment,
Some(Alignment {
horizontal: HorizontalAlignment::General,
vertical: VerticalAlignment::Distributed,
wrap_text: false
})
);
model
.update_range_style(&range, "alignment.vertical", "justify")
.unwrap();
let alignment = model.get_cell_style(0, 2, 2).unwrap().alignment;
assert_eq!(
alignment,
Some(Alignment {
horizontal: HorizontalAlignment::General,
vertical: VerticalAlignment::Justify,
wrap_text: false
})
);
model.update_range_style(&range, "alignment", "").unwrap();
let alignment = model.get_cell_style(0, 2, 2).unwrap().alignment;
assert_eq!(alignment, None);
model.undo().unwrap();
let alignment = model.get_cell_style(0, 2, 2).unwrap().alignment;
assert_eq!(
alignment,
Some(Alignment {
horizontal: HorizontalAlignment::General,
vertical: VerticalAlignment::Justify,
wrap_text: false
})
);
}
#[test]
fn alignment_errors() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
assert_eq!(
model.update_range_style(&range, "alignment", "some"),
Err("Alignment must be empty, but found: 'some'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "alignment.vertical", "justified"),
Err("Invalid value for vertical alignment: 'justified'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "alignment.horizontal", "unjustified"),
Err("Invalid value for horizontal alignment: 'unjustified'.".to_string())
);
model
.update_range_style(&range, "alignment.vertical", "justify")
.unwrap();
// Also fail if there is an alignment
assert_eq!(
model.update_range_style(&range, "alignment", "some"),
Err("Alignment must be empty, but found: 'some'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "alignment.vertical", "justified"),
Err("Invalid value for vertical alignment: 'justified'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "alignment.horizontal", "unjustified"),
Err("Invalid value for horizontal alignment: 'unjustified'.".to_string())
);
}
#[test]
fn basic_wrap_text() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
assert_eq!(
model.update_range_style(&range, "alignment.wrap_text", "T"),
Err("Invalid value for boolean: 'T'.".to_string())
);
model
.update_range_style(&range, "alignment.wrap_text", "true")
.unwrap();
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
assert_eq!(
alignment,
Some(Alignment {
horizontal: HorizontalAlignment::General,
vertical: VerticalAlignment::Bottom,
wrap_text: true
})
);
model.undo().unwrap();
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
assert_eq!(alignment, None);
model.redo().unwrap();
let alignment = model.get_cell_style(0, 1, 1).unwrap().alignment;
assert_eq!(
alignment,
Some(Alignment {
horizontal: HorizontalAlignment::General,
vertical: VerticalAlignment::Bottom,
wrap_text: true
})
);
assert_eq!(
model.update_range_style(&range, "alignment.wrap_text", "True"),
Err("Invalid value for boolean: 'True'.".to_string())
);
}
#[test]
fn more_basic_borders() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
model
.update_range_style(&range, "border.left", "thick,#F1F1F1")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::Thick,
color: Some("#F1F1F1".to_owned()),
})
);
model
.update_range_style(&range, "border.left", "slantDashDot,#F1F1F1")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::SlantDashDot,
color: Some("#F1F1F1".to_owned()),
})
);
model
.update_range_style(&range, "border.left", "mediumDashDot,#F1F1F1")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::MediumDashDot,
color: Some("#F1F1F1".to_owned()),
})
);
model
.update_range_style(&range, "border.left", "mediumDashDotDot,#F1F1F1")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::MediumDashDotDot,
color: Some("#F1F1F1".to_owned()),
})
);
model
.update_range_style(&range, "border.left", "mediumDashed,#F1F1F1")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::MediumDashed,
color: Some("#F1F1F1".to_owned()),
})
);
}
#[test]
fn border_errors() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
assert_eq!(
model.update_range_style(&range, "border.lef", "thick,#F1F1F1"),
Err("Invalid style path: 'border.lef'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "border.left", "thic,#F1F1F1"),
Err("Invalid border style: 'thic'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "border.left", "thick,#F1F1F"),
Err("Invalid color: '#F1F1F'.".to_string())
);
assert_eq!(
model.update_range_style(&range, "border.left", " "),
Err("Invalid border value: ' '.".to_string())
);
assert_eq!(
model.update_range_style(&range, "border.left", "thick,#F1F1F1,thin"),
Err("Invalid border value: 'thick,#F1F1F1,thin'.".to_string())
);
}
#[test]
fn empty_removes_border() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
model
.update_range_style(&range, "border.left", "mediumDashDotDot,#F1F1F1")
.unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(
style.border.left,
Some(BorderItem {
style: BorderStyle::MediumDashDotDot,
color: Some("#F1F1F1".to_owned()),
})
);
model.update_range_style(&range, "border.left", "").unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert_eq!(style.border.left, None);
}
#[test]
fn false_removes_value() {
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
let range = Area {
sheet: 0,
row: 1,
column: 1,
width: 1,
height: 1,
};
// bold
model.update_range_style(&range, "font.b", "true").unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(style.font.b);
model.update_range_style(&range, "font.b", "false").unwrap();
let style = model.get_cell_style(0, 1, 1).unwrap();
assert!(!style.font.b);
}

View File

@@ -0,0 +1,66 @@
#![allow(clippy::unwrap_used)]
use crate::{test::util::new_empty_model, UserModel};
#[test]
fn simple_undo_redo() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
// at the beginning I cannot undo or redo
assert!(!model.can_undo());
assert!(!model.can_redo());
assert!(model.set_user_input(0, 1, 1, "=1+2").is_ok());
// Once I enter a value I can undo but not redo
assert!(model.can_undo());
assert!(!model.can_redo());
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("3".to_string()));
// If I undo, I can't undo anymore, but I can redo
assert!(model.undo().is_ok());
assert!(!model.can_undo());
assert!(model.can_redo());
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("".to_string()));
// If I redo, I have the old value and formula
assert!(model.redo().is_ok());
assert_eq!(model.get_formatted_cell_value(0, 1, 1), Ok("3".to_string()));
assert_eq!(model.get_cell_content(0, 1, 1), Ok("=1+2".to_string()));
assert!(model.can_undo());
assert!(!model.can_redo());
}
#[test]
fn undo_redo_respect_styles() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
assert!(model.set_user_input(0, 1, 1, "100").is_ok());
assert!(model.set_user_input(0, 1, 1, "125$").is_ok());
// The content of the cell is just the number 125
assert_eq!(model.get_cell_content(0, 1, 1), Ok("125".to_string()));
assert!(model.undo().is_ok());
// The cell has no currency number formatting
assert_eq!(
model.get_formatted_cell_value(0, 1, 1),
Ok("100".to_string())
);
assert_eq!(model.get_cell_content(0, 1, 1), Ok("100".to_string()));
assert!(model.redo().is_ok());
// The cell has the number 125 formatted as '125$'
assert_eq!(
model.get_formatted_cell_value(0, 1, 1),
Ok("125$".to_string())
);
assert_eq!(model.get_cell_content(0, 1, 1), Ok("125".to_string()));
}
#[test]
fn can_undo_can_redo() {
let model = new_empty_model();
let mut model = UserModel::from_model(model);
assert!(!model.can_undo());
assert!(!model.can_redo());
assert!(model.undo().is_ok());
assert!(model.redo().is_ok());
}