extern crate text_document_formatting as document_formatting;
use anyhow::Result;
use common::entities::InlineContent;
use document_formatting::document_formatting_controller;
use document_formatting::{
Alignment, CellVerticalAlignment, SetBlockFormatDto, SetFrameFormatDto, SetListFormatDto,
SetTableCellFormatDto, SetTableFormatDto, SetTextFormatDto,
};
use test_harness::{
BlockRelationshipField, block_controller, create_list, export_text, frame_controller,
get_all_block_ids, get_block_ids, get_frame_id, get_sorted_cells, inline_element_controller,
insert_frame, insert_image, insert_table, list_controller, setup_with_text,
table_cell_controller, table_controller,
};
#[test]
fn test_mixed_list_block_and_text_formatting() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Heading\nItem A\nItem B\nItem C")?;
let list_result = create_list(
&db,
&hub,
&mut urm,
8,
28,
common::entities::ListStyle::Decimal,
)?;
document_formatting_controller::set_block_format(
&db,
&hub,
&mut urm,
None,
&SetBlockFormatDto {
position: 0,
anchor: 7,
heading_level: Some(1),
alignment: Some(Alignment::Center),
..Default::default()
},
)?;
document_formatting_controller::set_list_format(
&db,
&hub,
&mut urm,
None,
&SetListFormatDto {
list_id: list_result.list_id as i64,
style: Some(common::entities::ListStyle::UpperAlpha),
indent: Some(2),
prefix: None,
suffix: Some(".".into()),
},
)?;
document_formatting_controller::set_text_format(
&db,
&hub,
&mut urm,
None,
&SetTextFormatDto {
position: 8,
anchor: 28,
font_bold: Some(true),
..Default::default()
},
)?;
let block_ids = get_block_ids(&db)?;
let heading_block = block_controller::get(&db, &block_ids[0])?.unwrap();
assert_eq!(heading_block.fmt_heading_level, Some(1));
assert_eq!(
heading_block.fmt_alignment,
Some(common::entities::Alignment::Center)
);
let list = list_controller::get(&db, &list_result.list_id)?.unwrap();
assert_eq!(list.style, common::entities::ListStyle::UpperAlpha);
assert_eq!(list.indent, 2);
assert_eq!(list.suffix, ".");
for (i, bid) in block_ids.iter().enumerate().take(4).skip(1) {
let elem_ids =
block_controller::get_relationship(&db, bid, &BlockRelationshipField::Elements)?;
for eid in &elem_ids {
let elem = inline_element_controller::get(&db, eid)?.unwrap();
if let InlineContent::Text(ref t) = elem.content
&& !t.is_empty()
{
assert_eq!(
elem.fmt_font_bold,
Some(true),
"Text '{}' in block {} should be bold",
t,
i
);
}
}
}
urm.undo(None)?;
urm.undo(None)?;
urm.undo(None)?;
let heading_block = block_controller::get(&db, &block_ids[0])?.unwrap();
assert_eq!(heading_block.fmt_heading_level, None);
assert_eq!(heading_block.fmt_alignment, None);
Ok(())
}
#[test]
fn test_mixed_table_with_formatted_cells() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Report")?;
let table_result = insert_table(&db, &hub, &mut urm, 6, 2, 3)?;
let table_id = table_result.table_id;
document_formatting_controller::set_table_format(
&db,
&hub,
&mut urm,
None,
&SetTableFormatDto {
table_id: table_id as i64,
border: Some(1),
cell_padding: Some(5),
width: Some(500),
alignment: Some(Alignment::Center),
..Default::default()
},
)?;
let cells = get_sorted_cells(&db, &table_id)?;
for c in cells.iter().take(3) {
document_formatting_controller::set_table_cell_format(
&db,
&hub,
&mut urm,
None,
&SetTableCellFormatDto {
cell_id: c.id as i64,
background_color: Some("#333333".into()),
vertical_alignment: Some(CellVerticalAlignment::Middle),
..Default::default()
},
)?;
}
for c in cells.iter().take(3) {
let cell = table_cell_controller::get(&db, &c.id)?.unwrap();
assert_eq!(cell.fmt_background_color, Some("#333333".into()));
assert_eq!(
cell.fmt_vertical_alignment,
Some(common::entities::CellVerticalAlignment::Middle)
);
}
for c in cells.iter().take(6).skip(3) {
let cell = table_cell_controller::get(&db, &c.id)?.unwrap();
assert_eq!(cell.fmt_background_color, None);
}
let table = table_controller::get(&db, &table_id)?.unwrap();
assert_eq!(table.fmt_border, Some(1));
assert_eq!(table.fmt_cell_padding, Some(5));
Ok(())
}
#[test]
fn test_mixed_sub_frame_with_list_and_formatting() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Main text")?;
let frame_result = insert_frame(&db, &hub, &mut urm, 9)?;
let sub_frame_id = frame_result.frame_id;
document_formatting_controller::set_frame_format(
&db,
&hub,
&mut urm,
None,
&SetFrameFormatDto {
position: 0,
anchor: 0,
frame_id: sub_frame_id as i64,
is_blockquote: Some(true),
left_margin: Some(20),
padding: Some(10),
border: Some(1),
..Default::default()
},
)?;
let sub_frame = frame_controller::get(&db, &sub_frame_id)?.unwrap();
assert_eq!(sub_frame.fmt_is_blockquote, Some(true));
assert_eq!(sub_frame.fmt_left_margin, Some(20));
assert_eq!(sub_frame.fmt_padding, Some(10));
assert_eq!(sub_frame.fmt_border, Some(1));
let main_frame_id = get_frame_id(&db)?;
let main_frame = frame_controller::get(&db, &main_frame_id)?.unwrap();
assert_eq!(main_frame.fmt_is_blockquote, None);
Ok(())
}
#[test]
fn test_mixed_full_document_formatting_undo_chain() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Title\nParagraph one\nParagraph two")?;
document_formatting_controller::set_block_format(
&db,
&hub,
&mut urm,
None,
&SetBlockFormatDto {
position: 0,
anchor: 5,
heading_level: Some(1),
..Default::default()
},
)?;
document_formatting_controller::set_text_format(
&db,
&hub,
&mut urm,
None,
&SetTextFormatDto {
position: 0,
anchor: 5,
font_bold: Some(true),
font_family: Some("Helvetica".into()),
..Default::default()
},
)?;
document_formatting_controller::set_text_format(
&db,
&hub,
&mut urm,
None,
&SetTextFormatDto {
position: 16,
anchor: 19,
font_italic: Some(true),
..Default::default()
},
)?;
let frame_id = get_frame_id(&db)?;
document_formatting_controller::set_frame_format(
&db,
&hub,
&mut urm,
None,
&SetFrameFormatDto {
position: 0,
anchor: 0,
frame_id: frame_id as i64,
padding: Some(20),
..Default::default()
},
)?;
let block_ids = get_block_ids(&db)?;
assert_eq!(
block_controller::get(&db, &block_ids[0])?
.unwrap()
.fmt_heading_level,
Some(1)
);
assert_eq!(
frame_controller::get(&db, &frame_id)?.unwrap().fmt_padding,
Some(20)
);
urm.undo(None)?;
assert_eq!(
frame_controller::get(&db, &frame_id)?.unwrap().fmt_padding,
None
);
urm.undo(None)?;
urm.undo(None)?;
let title_elems =
block_controller::get_relationship(&db, &block_ids[0], &BlockRelationshipField::Elements)?;
assert_eq!(
inline_element_controller::get(&db, &title_elems[0])?
.unwrap()
.fmt_font_bold,
None
);
urm.undo(None)?;
assert_eq!(
block_controller::get(&db, &block_ids[0])?
.unwrap()
.fmt_heading_level,
None
);
urm.redo(None)?;
urm.redo(None)?;
urm.redo(None)?;
urm.redo(None)?;
assert_eq!(
block_controller::get(&db, &block_ids[0])?
.unwrap()
.fmt_heading_level,
Some(1)
);
assert_eq!(
frame_controller::get(&db, &frame_id)?.unwrap().fmt_padding,
Some(20)
);
Ok(())
}
#[test]
fn test_mixed_table_and_list_same_document_formatting() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Header\nBullet A\nBullet B")?;
let list_result = create_list(
&db,
&hub,
&mut urm,
7,
24,
common::entities::ListStyle::Disc,
)?;
let table_result = insert_table(&db, &hub, &mut urm, 24, 2, 2)?;
let table_id = table_result.table_id;
let list_id = list_result.list_id;
document_formatting_controller::set_list_format(
&db,
&hub,
&mut urm,
None,
&SetListFormatDto {
list_id: list_id as i64,
style: Some(common::entities::ListStyle::Circle),
indent: Some(1),
prefix: Some("- ".into()),
suffix: None,
},
)?;
document_formatting_controller::set_table_format(
&db,
&hub,
&mut urm,
None,
&SetTableFormatDto {
table_id: table_id as i64,
border: Some(2),
alignment: Some(Alignment::Left),
..Default::default()
},
)?;
document_formatting_controller::set_block_format(
&db,
&hub,
&mut urm,
None,
&SetBlockFormatDto {
position: 0,
anchor: 6,
heading_level: Some(2),
alignment: Some(Alignment::Center),
..Default::default()
},
)?;
assert_eq!(
list_controller::get(&db, &list_id)?.unwrap().style,
common::entities::ListStyle::Circle
);
assert_eq!(list_controller::get(&db, &list_id)?.unwrap().prefix, "- ");
assert_eq!(
table_controller::get(&db, &table_id)?.unwrap().fmt_border,
Some(2)
);
assert_eq!(
block_controller::get(&db, &get_block_ids(&db)?[0])?
.unwrap()
.fmt_heading_level,
Some(2)
);
for _ in 0..3 {
urm.undo(None)?;
}
assert_eq!(
table_controller::get(&db, &table_id)?.unwrap().fmt_border,
None,
"Table format should be undone"
);
assert_eq!(
list_controller::get(&db, &list_id)?.unwrap().style,
common::entities::ListStyle::Disc,
"List style should be reverted"
);
Ok(())
}
#[test]
fn test_mixed_text_format_with_image_and_list() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Start\nList item")?;
insert_image(&db, &hub, &mut urm, 3, "icon.png", 16, 16)?;
create_list(
&db,
&hub,
&mut urm,
7,
16,
common::entities::ListStyle::Disc,
)?;
document_formatting_controller::set_text_format(
&db,
&hub,
&mut urm,
None,
&SetTextFormatDto {
position: 0,
anchor: 6,
font_bold: Some(true),
..Default::default()
},
)?;
let block_ids = get_block_ids(&db)?;
let elem_ids =
block_controller::get_relationship(&db, &block_ids[0], &BlockRelationshipField::Elements)?;
let mut text_bold_count = 0;
let mut image_bold = false;
for eid in &elem_ids {
let elem = inline_element_controller::get(&db, eid)?.unwrap();
if elem.fmt_font_bold == Some(true) {
match &elem.content {
InlineContent::Text(_) => text_bold_count += 1,
InlineContent::Image { .. } => image_bold = true,
_ => {}
}
}
}
assert!(
text_bold_count > 0,
"At least one text element should be bold"
);
assert!(image_bold, "Image should be bold when fully in range");
let text = export_text(&db, &hub)?;
assert!(text.starts_with("Sta"), "Text should start with 'Sta'");
Ok(())
}
#[test]
fn test_format_blocks_across_multiple_frames() -> Result<()> {
let (db, hub, mut urm) = setup_with_text("Frame 1 content")?;
insert_frame(&db, &hub, &mut urm, 15)?;
let all_block_ids = get_all_block_ids(&db)?;
assert!(
all_block_ids.len() >= 2,
"Should have blocks in both frames"
);
let mut min_pos = i64::MAX;
let mut max_end = 0;
for bid in &all_block_ids {
let b = block_controller::get(&db, bid)?.unwrap();
min_pos = min_pos.min(b.document_position);
max_end = max_end.max(b.document_position + b.text_length);
}
document_formatting_controller::set_block_format(
&db,
&hub,
&mut urm,
None,
&SetBlockFormatDto {
position: min_pos,
anchor: max_end,
direction: Some(common::entities::TextDirection::RightToLeft),
..Default::default()
},
)?;
for bid in &all_block_ids {
let b = block_controller::get(&db, bid)?.unwrap();
assert_eq!(
b.fmt_direction,
Some(common::entities::TextDirection::RightToLeft),
"Block at position {} should have RTL direction",
b.document_position
);
}
Ok(())
}