extern crate text_document_editing as document_editing;
use anyhow::Result;
use common::types::EntityId;
use test_harness::{
DocumentRelationshipField, RootRelationshipField, block_controller, document_controller,
export_text, frame_controller, get_block_ids, get_document_stats, get_first_block_element_ids,
inline_element_controller, root_controller, setup_with_text,
};
use document_editing::document_editing_controller;
use document_editing::*;
#[test]
fn test_insert_formatted_text() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Hello")?;
let result = document_editing_controller::insert_formatted_text(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&InsertFormattedTextDto {
position: 5,
anchor: 5,
text: " World".to_string(),
font_family: "Arial".to_string(),
font_point_size: 12,
font_bold: true,
font_italic: false,
font_underline: false,
font_strikeout: false,
},
)?;
assert_eq!(result.new_position, 11);
let text = export_text(&db_context, &event_hub)?;
assert_eq!(text, "Hello World");
let elem_ids = get_first_block_element_ids(&db_context)?;
let mut found_bold = false;
for elem_id in &elem_ids {
let elem =
inline_element_controller::get(&db_context, elem_id)?.expect("Element not found");
if elem.fmt_font_bold == Some(true) {
found_bold = true;
assert_eq!(elem.fmt_font_family, Some("Arial".to_string()));
assert_eq!(elem.fmt_font_point_size, Some(12));
}
}
assert!(found_bold, "Should find an element with bold formatting");
Ok(())
}
#[test]
fn test_insert_formatted_text_undo() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Hello")?;
document_editing_controller::insert_formatted_text(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&InsertFormattedTextDto {
position: 5,
anchor: 5,
text: " Bold".to_string(),
font_family: "Arial".to_string(),
font_point_size: 14,
font_bold: true,
font_italic: false,
font_underline: false,
font_strikeout: false,
},
)?;
let text = export_text(&db_context, &event_hub)?;
assert_eq!(text, "Hello Bold");
undo_redo_manager.undo(None)?;
let text = export_text(&db_context, &event_hub)?;
assert_eq!(text, "Hello");
Ok(())
}
#[test]
fn test_insert_image() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Hello")?;
let result = document_editing_controller::insert_image(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&InsertImageDto {
position: 5,
anchor: 5,
image_name: "test.png".to_string(),
width: 100,
height: 50,
},
)?;
assert_eq!(result.new_position, 6);
assert!(result.element_id > 0);
let stats = test_harness::get_document_stats(&db_context)?;
assert_eq!(stats.character_count, 6);
Ok(())
}
#[test]
fn test_insert_image_undo() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Hello")?;
document_editing_controller::insert_image(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&InsertImageDto {
position: 3,
anchor: 3,
image_name: "photo.jpg".to_string(),
width: 200,
height: 150,
},
)?;
let stats = test_harness::get_document_stats(&db_context)?;
assert_eq!(stats.character_count, 6);
undo_redo_manager.undo(None)?;
let stats = test_harness::get_document_stats(&db_context)?;
assert_eq!(stats.character_count, 5);
let text = export_text(&db_context, &event_hub)?;
assert_eq!(text, "Hello");
Ok(())
}
#[test]
fn test_create_list() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Item one")?;
let result = document_editing_controller::create_list(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&CreateListDto {
position: 0,
anchor: 8,
style: ListStyle::Disc,
},
)?;
assert!(result.list_id > 0);
let block_ids = get_block_ids(&db_context)?;
let block = block_controller::get(&db_context, &block_ids[0])?.expect("Block not found");
assert!(block.list.is_some(), "Block should have a list reference");
assert_eq!(block.list.unwrap() as i64, result.list_id);
Ok(())
}
#[test]
fn test_create_list_undo() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Item one")?;
document_editing_controller::create_list(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&CreateListDto {
position: 0,
anchor: 8,
style: ListStyle::Decimal,
},
)?;
let block_ids = get_block_ids(&db_context)?;
let block = block_controller::get(&db_context, &block_ids[0])?.expect("Block not found");
assert!(block.list.is_some());
undo_redo_manager.undo(None)?;
let block_ids = get_block_ids(&db_context)?;
let block = block_controller::get(&db_context, &block_ids[0])?.expect("Block not found");
assert!(
block.list.is_none(),
"Block should not have a list after undo"
);
Ok(())
}
#[test]
fn test_insert_list() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Hello")?;
let result = document_editing_controller::insert_list(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&InsertListDto {
position: 5,
anchor: 5,
style: ListStyle::Disc,
},
)?;
assert!(result.list_id > 0);
let stats = test_harness::get_document_stats(&db_context)?;
assert_eq!(stats.block_count, 2);
Ok(())
}
#[test]
fn test_insert_frame() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Hello")?;
let result = document_editing_controller::insert_frame(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&InsertFrameDto {
position: 0,
anchor: 0,
},
)?;
assert!(result.frame_id > 0);
let root = root_controller::get(&db_context, &1)?.expect("Root not found");
let doc_ids =
root_controller::get_relationship(&db_context, &root.id, &RootRelationshipField::Document)?;
let frame_ids = document_controller::get_relationship(
&db_context,
&doc_ids[0],
&DocumentRelationshipField::Frames,
)?;
assert_eq!(frame_ids.len(), 2);
let new_frame = frame_controller::get(&db_context, &(result.frame_id as EntityId))?
.expect("New frame not found");
let root_frame_id = frame_ids[0]; assert_eq!(
new_frame.parent_frame,
Some(root_frame_id),
"New frame should have the root frame as parent"
);
let parent_frame =
frame_controller::get(&db_context, &root_frame_id)?.expect("Parent frame not found");
assert!(
parent_frame.child_order.contains(&-(result.frame_id)),
"Parent frame's child_order should contain -(new_frame_id)"
);
Ok(())
}
#[test]
fn test_insert_frame_undo() -> Result<()> {
let (db_context, event_hub, mut undo_redo_manager) = setup_with_text("Hello")?;
document_editing_controller::insert_frame(
&db_context,
&event_hub,
&mut undo_redo_manager,
None,
&InsertFrameDto {
position: 0,
anchor: 0,
},
)?;
let root = root_controller::get(&db_context, &1)?.expect("Root not found");
let doc_ids =
root_controller::get_relationship(&db_context, &root.id, &RootRelationshipField::Document)?;
let frame_ids = document_controller::get_relationship(
&db_context,
&doc_ids[0],
&DocumentRelationshipField::Frames,
)?;
assert_eq!(frame_ids.len(), 2);
undo_redo_manager.undo(None)?;
let frame_ids = document_controller::get_relationship(
&db_context,
&doc_ids[0],
&DocumentRelationshipField::Frames,
)?;
assert_eq!(frame_ids.len(), 1, "Should be back to 1 frame after undo");
let text = export_text(&db_context, &event_hub)?;
assert_eq!(text, "Hello");
Ok(())
}
#[test]
fn test_insert_formatted_text_replaces_selection() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Hello World")?;
let result = document_editing_controller::insert_formatted_text(
&db,
&hub,
&mut urm,
None,
&InsertFormattedTextDto {
position: 6,
anchor: 11,
text: "Earth".to_string(),
font_family: "Arial".to_string(),
font_point_size: 12,
font_bold: true,
font_italic: false,
font_underline: false,
font_strikeout: false,
},
)?;
assert_eq!(result.new_position, 11); let text = export_text(&db, &hub)?;
assert_eq!(text, "Hello Earth");
let elem_ids = get_first_block_element_ids(&db)?;
let mut found_bold_earth = false;
for elem_id in &elem_ids {
let elem = inline_element_controller::get(&db, elem_id)?.unwrap();
if let common::entities::InlineContent::Text(ref t) = elem.content
&& t.contains("Earth")
&& elem.fmt_font_bold == Some(true)
{
found_bold_earth = true;
}
}
assert!(found_bold_earth, "Should find bold 'Earth' element");
Ok(())
}
#[test]
fn test_insert_formatted_text_replaces_selection_undo() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Hello World")?;
document_editing_controller::insert_formatted_text(
&db,
&hub,
&mut urm,
None,
&InsertFormattedTextDto {
position: 6,
anchor: 11,
text: "Earth".to_string(),
font_family: "".to_string(),
font_point_size: 0,
font_bold: false,
font_italic: false,
font_underline: false,
font_strikeout: false,
},
)?;
assert_eq!(export_text(&db, &hub)?, "Hello Earth");
urm.undo(None)?;
assert_eq!(export_text(&db, &hub)?, "Hello World");
urm.redo(None)?;
assert_eq!(export_text(&db, &hub)?, "Hello Earth");
Ok(())
}
#[test]
fn test_insert_formatted_text_cross_block_selection_errors() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Hello\nWorld")?;
let result = document_editing_controller::insert_formatted_text(
&db,
&hub,
&mut urm,
None,
&InsertFormattedTextDto {
position: 3,
anchor: 9,
text: "XY".to_string(),
font_family: "".to_string(),
font_point_size: 0,
font_bold: true,
font_italic: false,
font_underline: false,
font_strikeout: false,
},
);
assert!(result.is_err(), "Cross-block selection should fail");
assert_eq!(export_text(&db, &hub)?, "Hello\nWorld");
Ok(())
}
#[test]
fn test_insert_image_at_position_zero() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Hello")?;
let result = document_editing_controller::insert_image(
&db,
&hub,
&mut urm,
None,
&InsertImageDto {
position: 0,
anchor: 0,
image_name: "start.png".to_string(),
width: 50,
height: 50,
},
)?;
assert_eq!(result.new_position, 1);
let stats = get_document_stats(&db)?;
assert_eq!(stats.character_count, 6);
let elem_ids = get_first_block_element_ids(&db)?;
let has_image = elem_ids.iter().any(|id| {
let elem = inline_element_controller::get(&db, id).unwrap().unwrap();
matches!(elem.content, common::entities::InlineContent::Image { .. })
});
assert!(has_image, "Block should contain an image element");
Ok(())
}
#[test]
fn test_insert_image_with_selection_errors() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Hello World")?;
let result = document_editing_controller::insert_image(
&db,
&hub,
&mut urm,
None,
&InsertImageDto {
position: 6,
anchor: 11,
image_name: "replaced.png".to_string(),
width: 100,
height: 100,
},
);
assert!(result.is_err(), "Image insert with selection should fail");
assert_eq!(export_text(&db, &hub)?, "Hello World");
Ok(())
}
#[test]
fn test_insert_frame_at_end_of_document() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Hello")?;
let result = document_editing_controller::insert_frame(
&db,
&hub,
&mut urm,
None,
&InsertFrameDto {
position: 5,
anchor: 5,
},
)?;
assert!(result.frame_id > 0);
let root = root_controller::get(&db, &1)?.unwrap();
let doc_ids =
root_controller::get_relationship(&db, &root.id, &RootRelationshipField::Document)?;
let frame_ids = document_controller::get_relationship(
&db,
&doc_ids[0],
&DocumentRelationshipField::Frames,
)?;
assert_eq!(frame_ids.len(), 2);
urm.undo(None)?;
let frame_ids = document_controller::get_relationship(
&db,
&doc_ids[0],
&DocumentRelationshipField::Frames,
)?;
assert_eq!(frame_ids.len(), 1);
urm.redo(None)?;
let frame_ids = document_controller::get_relationship(
&db,
&doc_ids[0],
&DocumentRelationshipField::Frames,
)?;
assert_eq!(frame_ids.len(), 2);
Ok(())
}
#[test]
fn test_insert_frame_in_middle_of_text() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Hello World")?;
let result = document_editing_controller::insert_frame(
&db,
&hub,
&mut urm,
None,
&InsertFrameDto {
position: 5,
anchor: 5,
},
)?;
assert!(result.frame_id > 0);
let text = export_text(&db, &hub)?;
assert!(
text.contains("Hello"),
"Should still contain 'Hello', got: {}",
text
);
assert!(
text.contains("World"),
"Should still contain 'World', got: {}",
text
);
Ok(())
}
#[test]
fn test_insert_list_at_end_of_multiblock_document() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Line one\nLine two")?;
let result = document_editing_controller::insert_list(
&db,
&hub,
&mut urm,
None,
&InsertListDto {
position: 17,
anchor: 17,
style: ListStyle::Decimal,
},
)?;
assert!(result.list_id > 0);
let stats = get_document_stats(&db)?;
assert_eq!(stats.block_count, 3);
urm.undo(None)?;
let stats = get_document_stats(&db)?;
assert_eq!(stats.block_count, 2);
assert_eq!(export_text(&db, &hub)?, "Line one\nLine two");
Ok(())
}
#[test]
fn test_create_list_spanning_multiple_blocks() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("First\nSecond\nThird")?;
let result = document_editing_controller::create_list(
&db,
&hub,
&mut urm,
None,
&CreateListDto {
position: 0,
anchor: 18,
style: ListStyle::Disc,
},
)?;
assert!(result.list_id > 0);
let block_ids = get_block_ids(&db)?;
assert_eq!(block_ids.len(), 3);
for block_id in &block_ids {
let block = block_controller::get(&db, block_id)?.unwrap();
assert!(
block.list.is_some(),
"Block {} should have a list reference",
block_id
);
assert_eq!(block.list.unwrap() as i64, result.list_id);
}
urm.undo(None)?;
for block_id in &block_ids {
let block = block_controller::get(&db, block_id)?.unwrap();
assert!(
block.list.is_none(),
"Block {} should have no list after undo",
block_id
);
}
Ok(())
}
#[test]
fn test_insert_formatted_text_into_empty_document() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("")?;
let result = document_editing_controller::insert_formatted_text(
&db,
&hub,
&mut urm,
None,
&InsertFormattedTextDto {
position: 0,
anchor: 0,
text: "Bold start".to_string(),
font_family: "Courier".to_string(),
font_point_size: 10,
font_bold: true,
font_italic: false,
font_underline: false,
font_strikeout: false,
},
)?;
assert_eq!(result.new_position, 10);
assert_eq!(export_text(&db, &hub)?, "Bold start");
let stats = get_document_stats(&db)?;
assert_eq!(stats.character_count, 10);
assert_eq!(stats.block_count, 1);
Ok(())
}
#[test]
fn test_insert_image_cross_block_selection_errors() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Hello\nWorld")?;
let result = document_editing_controller::insert_image(
&db,
&hub,
&mut urm,
None,
&InsertImageDto {
position: 3,
anchor: 9,
image_name: "bridge.png".to_string(),
width: 200,
height: 100,
},
);
assert!(result.is_err(), "Image insert with selection should fail");
assert_eq!(export_text(&db, &hub)?, "Hello\nWorld");
Ok(())
}