add undo test

This commit is contained in:
appflowy 2021-08-18 13:12:45 +08:00
parent 8272e2e8f6
commit bfb80d1184
13 changed files with 274 additions and 148 deletions

View File

@ -93,7 +93,8 @@ class EditorController extends ChangeNotifier {
toggledStyle = toggledStyle.put(attribute); toggledStyle = toggledStyle.put(attribute);
} }
final change = document.format(index, length, attribute); final change =
document.format(index, length, LinkAttribute("www.baidu.com"));
final adjustedSelection = selection.copyWith( final adjustedSelection = selection.copyWith(
baseOffset: change.transformPosition(selection.baseOffset), baseOffset: change.transformPosition(selection.baseOffset),
extentOffset: change.transformPosition(selection.extentOffset), extentOffset: change.transformPosition(selection.extentOffset),

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
client::{extensions::DeleteExt, util::is_newline}, client::{extensions::DeleteExt, util::is_newline},
core::{Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, Operation, NEW_LINE}, core::{Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE},
}; };
pub struct PreserveLineFormatOnMerge {} pub struct PreserveLineFormatOnMerge {}

View File

@ -1,47 +1,48 @@
use crate::{ // use crate::{
client::extensions::FormatExt, // client::extensions::FormatExt,
core::{Attribute, AttributeKey, Delta, DeltaBuilder, DeltaIter, Interval}, // core::{Attribute, AttributeKey, Delta, DeltaBuilder, DeltaIter,
}; // Interval}, };
//
pub struct FormatLinkAtCaretPositionExt {} // pub struct FormatLinkAtCaretPositionExt {}
//
impl FormatExt for FormatLinkAtCaretPositionExt { // impl FormatExt for FormatLinkAtCaretPositionExt {
fn ext_name(&self) -> &str { std::any::type_name::<FormatLinkAtCaretPositionExt>() } // fn ext_name(&self) -> &str {
// std::any::type_name::<FormatLinkAtCaretPositionExt>() }
fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> { //
if attribute.key != AttributeKey::Link || interval.size() != 0 { // fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute)
return None; // -> Option<Delta> { if attribute.key != AttributeKey::Link ||
} // interval.size() != 0 { return None;
// }
let mut iter = DeltaIter::from_offset(delta, interval.start); //
let (before, after) = (iter.next_op_with_len(interval.size()), iter.next_op()); // let mut iter = DeltaIter::from_offset(delta, interval.start);
let mut start = interval.end; // let (before, after) = (iter.next_op_with_len(interval.size()),
let mut retain = 0; // iter.next_op()); let mut start = interval.end;
// let mut retain = 0;
if let Some(before) = before { //
if before.contain_attribute(attribute) { // if let Some(before) = before {
start -= before.len(); // if before.contain_attribute(attribute) {
retain += before.len(); // start -= before.len();
} // retain += before.len();
} // }
// }
if let Some(after) = after { //
if after.contain_attribute(attribute) { // if let Some(after) = after {
if retain != 0 { // if after.contain_attribute(attribute) {
retain += after.len(); // if retain != 0 {
} // retain += after.len();
} // }
} // }
// }
if retain == 0 { //
return None; // if retain == 0 {
} // return None;
// }
Some( //
DeltaBuilder::new() // Some(
.retain(start) // DeltaBuilder::new()
.retain_with_attributes(retain, (attribute.clone()).into()) // .retain(start)
.build(), // .retain_with_attributes(retain, (attribute.clone()).into())
) // .build(),
} // )
} // }
// }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
client::{extensions::InsertExt, util::is_newline}, client::{extensions::InsertExt, util::is_newline},
core::{AttributeKey, Delta, DeltaBuilder, DeltaIter, Operation}, core::{AttributeKey, Delta, DeltaBuilder, DeltaIter},
}; };
use crate::core::{attributes_except_header, is_empty_line_at_index}; use crate::core::{attributes_except_header, is_empty_line_at_index};

View File

@ -50,7 +50,7 @@ impl InsertExt for AutoFormatExt {
} }
} }
use crate::core::{AttributeBuilder, Attributes, DeltaBuilder, Operation}; use crate::core::{AttributeBuilder, Attributes, DeltaBuilder};
use bytecount::num_chars; use bytecount::num_chars;
use std::cmp::min; use std::cmp::min;
use url::Url; use url::Url;

View File

@ -7,7 +7,6 @@ use crate::{
Delta, Delta,
DeltaBuilder, DeltaBuilder,
DeltaIter, DeltaIter,
Operation,
NEW_LINE, NEW_LINE,
}, },
}; };

View File

@ -94,7 +94,7 @@ fn construct_insert_exts() -> Vec<InsertExtension> {
fn construct_format_exts() -> Vec<FormatExtension> { fn construct_format_exts() -> Vec<FormatExtension> {
vec![ vec![
Box::new(FormatLinkAtCaretPositionExt {}), // Box::new(FormatLinkAtCaretPositionExt {}),
Box::new(ResolveBlockFormat {}), Box::new(ResolveBlockFormat {}),
Box::new(ResolveInlineFormat {}), Box::new(ResolveInlineFormat {}),
] ]

View File

@ -47,7 +47,7 @@ impl Attributes {
None => { None => {
self.inner self.inner
.iter_mut() .iter_mut()
.for_each(|(k, v)| v.0 = REMOVE_FLAG.into()); .for_each(|(_k, v)| v.0 = REMOVE_FLAG.into());
}, },
Some(attribute) => { Some(attribute) => {
self.inner.iter_mut().for_each(|(k, v)| { self.inner.iter_mut().for_each(|(k, v)| {

View File

@ -175,14 +175,17 @@ pub struct CharMetric {}
impl Metric for CharMetric { impl Metric for CharMetric {
fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult { fn seek(cursor: &mut OpCursor, index: usize) -> SeekResult {
let _ = check_bound(cursor.consume_count, index)?; if index > 0 {
let _ = cursor.next_with_len(Some(index)); let _ = check_bound(cursor.consume_count, index)?;
let _ = cursor.next_with_len(Some(index));
}
Ok(()) Ok(())
} }
} }
fn check_bound(current: usize, target: usize) -> Result<(), OTError> { fn check_bound(current: usize, target: usize) -> Result<(), OTError> {
debug_assert!(current <= target);
if current > target { if current > target {
let msg = format!("{} should be greater than current: {}", target, current); let msg = format!("{} should be greater than current: {}", target, current);
return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength) return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)

View File

@ -1,61 +1,10 @@
pub mod helper; pub mod helper;
use crate::helper::{TestOp::*, *}; use crate::helper::{TestOp::*, *};
use flowy_ot::core::Interval; use flowy_ot::core::{Interval, NEW_LINE, WHITESPACE};
use flowy_ot::core::{NEW_LINE, WHITESPACE};
#[test] #[test]
fn attributes_insert_text() { fn attributes_bold_added() {
let ops = vec![
Insert(0, "123", 0),
Insert(0, "456", 3),
AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
];
OpTester::new().run_script(ops);
}
#[test]
fn attributes_insert_text_at_head() {
let ops = vec![
Insert(0, "123", 0),
Insert(0, "456", 0),
AssertOpsJson(0, r#"[{"insert":"456123"}]"#),
];
OpTester::new().run_script(ops);
}
#[test]
fn attributes_insert_text_at_middle() {
let ops = vec![
Insert(0, "123", 0),
Insert(0, "456", 1),
AssertOpsJson(0, r#"[{"insert":"145623"}]"#),
];
OpTester::new().run_script(ops);
}
#[test]
fn attributes_insert_text_with_attr() {
let ops = vec![
Insert(0, "145", 0),
Insert(0, "23", 1),
Bold(0, Interval::new(0, 2), true),
AssertOpsJson(
0,
r#"[{"insert":"12","attributes":{"bold":"true"}},{"insert":"345"}]"#,
),
Insert(0, "abc", 1),
AssertOpsJson(
0,
r#"[{"insert":"1abc2","attributes":{"bold":"true"}},{"insert":"345"}]"#,
),
];
OpTester::new().run_script(ops);
}
#[test]
fn attributes_add_bold() {
let ops = vec![ let ops = vec![
Insert(0, "123456", 0), Insert(0, "123456", 0),
Bold(0, Interval::new(3, 5), true), Bold(0, Interval::new(3, 5), true),
@ -72,7 +21,7 @@ fn attributes_add_bold() {
} }
#[test] #[test]
fn attributes_add_bold_and_invert_all() { fn attributes_bold_added_and_invert_all() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true), Bold(0, Interval::new(0, 3), true),
@ -84,7 +33,7 @@ fn attributes_add_bold_and_invert_all() {
} }
#[test] #[test]
fn attributes_add_bold_and_invert_partial_suffix() { fn attributes_bold_added_and_invert_partial_suffix() {
let ops = vec![ let ops = vec![
Insert(0, "1234", 0), Insert(0, "1234", 0),
Bold(0, Interval::new(0, 4), true), Bold(0, Interval::new(0, 4), true),
@ -99,7 +48,7 @@ fn attributes_add_bold_and_invert_partial_suffix() {
} }
#[test] #[test]
fn attributes_add_bold_and_invert_partial_suffix2() { fn attributes_bold_added_and_invert_partial_suffix2() {
let ops = vec![ let ops = vec![
Insert(0, "1234", 0), Insert(0, "1234", 0),
Bold(0, Interval::new(0, 4), true), Bold(0, Interval::new(0, 4), true),
@ -116,7 +65,7 @@ fn attributes_add_bold_and_invert_partial_suffix2() {
} }
#[test] #[test]
fn attributes_add_bold_with_new_line() { fn attributes_bold_added_with_new_line() {
let ops = vec![ let ops = vec![
Insert(0, "123456", 0), Insert(0, "123456", 0),
Bold(0, Interval::new(0, 6), true), Bold(0, Interval::new(0, 6), true),
@ -144,7 +93,7 @@ fn attributes_add_bold_with_new_line() {
} }
#[test] #[test]
fn attributes_add_bold_and_invert_partial_prefix() { fn attributes_bold_added_and_invert_partial_prefix() {
let ops = vec![ let ops = vec![
Insert(0, "1234", 0), Insert(0, "1234", 0),
Bold(0, Interval::new(0, 4), true), Bold(0, Interval::new(0, 4), true),
@ -159,7 +108,7 @@ fn attributes_add_bold_and_invert_partial_prefix() {
} }
#[test] #[test]
fn attributes_add_bold_consecutive() { fn attributes_bold_added_consecutive() {
let ops = vec![ let ops = vec![
Insert(0, "1234", 0), Insert(0, "1234", 0),
Bold(0, Interval::new(0, 1), true), Bold(0, Interval::new(0, 1), true),
@ -177,7 +126,7 @@ fn attributes_add_bold_consecutive() {
} }
#[test] #[test]
fn attributes_add_bold_italic() { fn attributes_bold_added_italic() {
let ops = vec![ let ops = vec![
Insert(0, "1234", 0), Insert(0, "1234", 0),
Bold(0, Interval::new(0, 4), true), Bold(0, Interval::new(0, 4), true),
@ -196,7 +145,7 @@ fn attributes_add_bold_italic() {
} }
#[test] #[test]
fn attributes_add_bold_italic2() { fn attributes_bold_added_italic2() {
let ops = vec![ let ops = vec![
Insert(0, "123456", 0), Insert(0, "123456", 0),
Bold(0, Interval::new(0, 6), true), Bold(0, Interval::new(0, 6), true),
@ -224,7 +173,7 @@ fn attributes_add_bold_italic2() {
} }
#[test] #[test]
fn attributes_add_bold_italic3() { fn attributes_bold_added_italic3() {
let ops = vec![ let ops = vec![
Insert(0, "123456789", 0), Insert(0, "123456789", 0),
Bold(0, Interval::new(0, 5), true), Bold(0, Interval::new(0, 5), true),
@ -261,7 +210,7 @@ fn attributes_add_bold_italic3() {
} }
#[test] #[test]
fn attributes_add_bold_italic_delete() { fn attributes_bold_added_italic_delete() {
let ops = vec![ let ops = vec![
Insert(0, "123456789", 0), Insert(0, "123456789", 0),
Bold(0, Interval::new(0, 5), true), Bold(0, Interval::new(0, 5), true),
@ -467,7 +416,7 @@ fn attributes_header_insert_newline_at_middle() {
} }
#[test] #[test]
fn attributes_header_insert_newline_at_middle2() { fn attributes_header_insert_double_newline_at_middle() {
let ops = vec![ let ops = vec![
Insert(0, "123456", 0), Insert(0, "123456", 0),
Header(0, Interval::new(0, 6), 1, true), Header(0, Interval::new(0, 6), 1, true),
@ -523,7 +472,7 @@ fn attributes_header_insert_double_newline_at_trailing() {
} }
#[test] #[test]
fn attributes_add_link() { fn attributes_link_added() {
let ops = vec![ let ops = vec![
Insert(0, "123456", 0), Insert(0, "123456", 0),
Link(0, Interval::new(0, 6), "https://appflowy.io", true), Link(0, Interval::new(0, 6), "https://appflowy.io", true),
@ -536,6 +485,25 @@ fn attributes_add_link() {
OpTester::new().run_script_with_newline(ops); OpTester::new().run_script_with_newline(ops);
} }
#[test]
fn attributes_link_format_with_bold() {
let ops = vec![
Insert(0, "123456", 0),
Link(0, Interval::new(0, 6), "https://appflowy.io", true),
Bold(0, Interval::new(0, 3), true),
AssertOpsJson(
0,
r#"[
{"insert":"123","attributes":{"bold":"true","link":"https://appflowy.io"}},
{"insert":"456","attributes":{"link":"https://appflowy.io"}},
{"insert":"\n"}]
"#,
),
];
OpTester::new().run_script_with_newline(ops);
}
#[test] #[test]
fn attributes_link_insert_char_at_head() { fn attributes_link_insert_char_at_head() {
let ops = vec![ let ops = vec![
@ -605,7 +573,7 @@ fn attributes_link_insert_newline_at_middle() {
} }
#[test] #[test]
fn attributes_auto_format_link() { fn attributes_link_auto_format() {
let site = "https://appflowy.io"; let site = "https://appflowy.io";
let ops = vec![ let ops = vec![
Insert(0, site, 0), Insert(0, site, 0),
@ -621,7 +589,7 @@ fn attributes_auto_format_link() {
} }
#[test] #[test]
fn attributes_auto_format_exist_link() { fn attributes_link_auto_format_exist() {
let site = "https://appflowy.io"; let site = "https://appflowy.io";
let ops = vec![ let ops = vec![
Insert(0, site, 0), Insert(0, site, 0),
@ -637,7 +605,7 @@ fn attributes_auto_format_exist_link() {
} }
#[test] #[test]
fn attributes_auto_format_exist_link2() { fn attributes_link_auto_format_exist2() {
let site = "https://appflowy.io"; let site = "https://appflowy.io";
let ops = vec![ let ops = vec![
Insert(0, site, 0), Insert(0, site, 0),
@ -653,7 +621,7 @@ fn attributes_auto_format_exist_link2() {
} }
#[test] #[test]
fn attributes_add_bullet() { fn attributes_bullet_added() {
let ops = vec![ let ops = vec![
Insert(0, "12", 0), Insert(0, "12", 0),
Bullet(0, Interval::new(0, 1), true), Bullet(0, Interval::new(0, 1), true),
@ -667,7 +635,7 @@ fn attributes_add_bullet() {
} }
#[test] #[test]
fn attributes_add_bullet2() { fn attributes_bullet_added_2() {
let ops = vec![ let ops = vec![
Insert(0, "1", 0), Insert(0, "1", 0),
Bullet(0, Interval::new(0, 1), true), Bullet(0, Interval::new(0, 1), true),
@ -691,7 +659,7 @@ fn attributes_add_bullet2() {
} }
#[test] #[test]
fn attributes_un_bullet_one() { fn attributes_bullet_remove_partial() {
let ops = vec![ let ops = vec![
Insert(0, "1", 0), Insert(0, "1", 0),
Bullet(0, Interval::new(0, 1), true), Bullet(0, Interval::new(0, 1), true),
@ -708,7 +676,7 @@ fn attributes_un_bullet_one() {
} }
#[test] #[test]
fn attributes_auto_exit_block() { fn attributes_bullet_auto_exit() {
let ops = vec![ let ops = vec![
Insert(0, "1", 0), Insert(0, "1", 0),
Bullet(0, Interval::new(0, 1), true), Bullet(0, Interval::new(0, 1), true),
@ -764,7 +732,7 @@ fn attributes_preserve_block_when_insert_newline_inside() {
} }
#[test] #[test]
fn attributes_preserve_line_format_on_merge() { fn attributes_preserve_header_format_on_merge() {
let ops = vec![ let ops = vec![
Insert(0, "123456", 0), Insert(0, "123456", 0),
Header(0, Interval::new(0, 6), 1, true), Header(0, Interval::new(0, 6), 1, true),
@ -782,3 +750,23 @@ fn attributes_preserve_line_format_on_merge() {
OpTester::new().run_script_with_newline(ops); OpTester::new().run_script_with_newline(ops);
} }
#[test]
fn attributes_preserve_list_format_on_merge() {
let ops = vec![
Insert(0, "123456", 0),
Bullet(0, Interval::new(0, 6), true),
Insert(0, NEW_LINE, 3),
AssertOpsJson(
0,
r#"[{"insert":"123"},{"insert":"\n","attributes":{"bullet":"true"}},{"insert":"456"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
Delete(0, Interval::new(3, 4)),
AssertOpsJson(
0,
r#"[{"insert":"123456"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
];
OpTester::new().run_script_with_newline(ops);
}

View File

@ -3,7 +3,7 @@ use flowy_ot::{client::Document, core::*};
use rand::{prelude::*, Rng as WrappedRng}; use rand::{prelude::*, Rng as WrappedRng};
use std::{sync::Once, time::Duration}; use std::{sync::Once, time::Duration};
const LEVEL: &'static str = "debug"; const LEVEL: &'static str = "info";
#[derive(Clone, Debug, Display)] #[derive(Clone, Debug, Display)]
pub enum TestOp { pub enum TestOp {

View File

@ -5,6 +5,36 @@ use bytecount::num_chars;
use flowy_ot::core::*; use flowy_ot::core::*;
use helper::*; use helper::*;
#[test]
fn attributes_insert_text() {
let ops = vec![
Insert(0, "123", 0),
Insert(0, "456", 3),
AssertOpsJson(0, r#"[{"insert":"123456"}]"#),
];
OpTester::new().run_script(ops);
}
#[test]
fn attributes_insert_text_at_head() {
let ops = vec![
Insert(0, "123", 0),
Insert(0, "456", 0),
AssertOpsJson(0, r#"[{"insert":"456123"}]"#),
];
OpTester::new().run_script(ops);
}
#[test]
fn attributes_insert_text_at_middle() {
let ops = vec![
Insert(0, "123", 0),
Insert(0, "456", 1),
AssertOpsJson(0, r#"[{"insert":"145623"}]"#),
];
OpTester::new().run_script(ops);
}
#[test] #[test]
fn delta_get_ops_in_interval_1() { fn delta_get_ops_in_interval_1() {
let mut delta = Delta::default(); let mut delta = Delta::default();

View File

@ -1,10 +1,13 @@
pub mod helper; pub mod helper;
use crate::helper::{TestOp::*, *}; use crate::helper::{TestOp::*, *};
use flowy_ot::{client::RECORD_THRESHOLD, core::Interval}; use flowy_ot::{
client::RECORD_THRESHOLD,
core::{Interval, NEW_LINE, WHITESPACE},
};
#[test] #[test]
fn history_undo_insert() { fn history_insert_undo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Undo(0), Undo(0),
@ -14,7 +17,7 @@ fn history_undo_insert() {
} }
#[test] #[test]
fn history_undo_insert2() { fn history_insert_undo_with_lagging() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Wait(RECORD_THRESHOLD), Wait(RECORD_THRESHOLD),
@ -28,7 +31,7 @@ fn history_undo_insert2() {
} }
#[test] #[test]
fn history_redo_insert() { fn history_insert_redo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
AssertOpsJson(0, r#"[{"insert":"123\n"}]"#), AssertOpsJson(0, r#"[{"insert":"123\n"}]"#),
@ -41,7 +44,7 @@ fn history_redo_insert() {
} }
#[test] #[test]
fn history_redo_insert_with_lagging() { fn history_insert_redo_with_lagging() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Wait(RECORD_THRESHOLD), Wait(RECORD_THRESHOLD),
@ -60,7 +63,7 @@ fn history_redo_insert_with_lagging() {
} }
#[test] #[test]
fn history_undo_attributes() { fn history_bold_undo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true), Bold(0, Interval::new(0, 3), true),
@ -71,7 +74,7 @@ fn history_undo_attributes() {
} }
#[test] #[test]
fn history_undo_attributes_with_lagging() { fn history_bold_undo_with_lagging() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Wait(RECORD_THRESHOLD), Wait(RECORD_THRESHOLD),
@ -83,7 +86,7 @@ fn history_undo_attributes_with_lagging() {
} }
#[test] #[test]
fn history_redo_attributes() { fn history_bold_redo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true), Bold(0, Interval::new(0, 3), true),
@ -99,7 +102,7 @@ fn history_redo_attributes() {
} }
#[test] #[test]
fn history_redo_attributes_with_lagging() { fn history_bold_redo_with_lagging() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Wait(RECORD_THRESHOLD), Wait(RECORD_THRESHOLD),
@ -116,7 +119,7 @@ fn history_redo_attributes_with_lagging() {
} }
#[test] #[test]
fn history_undo_delete() { fn history_delete_undo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
AssertOpsJson(0, r#"[{"insert":"123"}]"#), AssertOpsJson(0, r#"[{"insert":"123"}]"#),
@ -129,7 +132,7 @@ fn history_undo_delete() {
} }
#[test] #[test]
fn history_undo_delete2() { fn history_delete_undo_2() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true), Bold(0, Interval::new(0, 3), true),
@ -148,7 +151,7 @@ fn history_undo_delete2() {
} }
#[test] #[test]
fn history_undo_delete2_with_lagging() { fn history_delete_undo_with_lagging() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Wait(RECORD_THRESHOLD), Wait(RECORD_THRESHOLD),
@ -175,7 +178,7 @@ fn history_undo_delete2_with_lagging() {
} }
#[test] #[test]
fn history_redo_delete() { fn history_delete_redo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Wait(RECORD_THRESHOLD), Wait(RECORD_THRESHOLD),
@ -189,7 +192,7 @@ fn history_redo_delete() {
} }
#[test] #[test]
fn history_undo_replace() { fn history_replace_undo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true), Bold(0, Interval::new(0, 3), true),
@ -208,7 +211,7 @@ fn history_undo_replace() {
} }
#[test] #[test]
fn history_undo_replace_with_lagging() { fn history_replace_undo_with_lagging() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Wait(RECORD_THRESHOLD), Wait(RECORD_THRESHOLD),
@ -232,7 +235,7 @@ fn history_undo_replace_with_lagging() {
} }
#[test] #[test]
fn history_redo_replace() { fn history_replace_redo() {
let ops = vec![ let ops = vec![
Insert(0, "123", 0), Insert(0, "123", 0),
Bold(0, Interval::new(0, 3), true), Bold(0, Interval::new(0, 3), true),
@ -251,13 +254,14 @@ fn history_redo_replace() {
} }
#[test] #[test]
fn history_undo_add_header() { fn history_header_added_undo() {
let ops = vec![ let ops = vec![
Insert(0, "123456", 0), Insert(0, "123456", 0),
Header(0, Interval::new(0, 6), 1, true), Header(0, Interval::new(0, 6), 1, true),
Insert(0, "\n", 3), Insert(0, "\n", 3),
Insert(0, "\n", 4), Insert(0, "\n", 4),
Undo(0), Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
Redo(0), Redo(0),
AssertOpsJson( AssertOpsJson(
0, 0,
@ -269,7 +273,7 @@ fn history_undo_add_header() {
} }
#[test] #[test]
fn history_undo_add_link() { fn history_link_added_undo() {
let site = "https://appflowy.io"; let site = "https://appflowy.io";
let ops = vec![ let ops = vec![
Insert(0, site, 0), Insert(0, site, 0),
@ -286,3 +290,103 @@ fn history_undo_add_link() {
OpTester::new().run_script_with_newline(ops); OpTester::new().run_script_with_newline(ops);
} }
#[test]
fn history_link_auto_format_undo_with_lagging() {
let site = "https://appflowy.io";
let ops = vec![
Insert(0, site, 0),
AssertOpsJson(0, r#"[{"insert":"https://appflowy.io\n"}]"#),
Wait(RECORD_THRESHOLD),
Insert(0, WHITESPACE, site.len()),
AssertOpsJson(
0,
r#"[{"insert":"https://appflowy.io","attributes":{"link":"https://appflowy.io/"}},{"insert":" \n"}]"#,
),
Undo(0),
AssertOpsJson(0, r#"[{"insert":"https://appflowy.io\n"}]"#),
];
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn history_bullet_undo() {
let ops = vec![
Insert(0, "1", 0),
Bullet(0, Interval::new(0, 1), true),
Insert(0, NEW_LINE, 1),
Insert(0, "2", 2),
AssertOpsJson(
0,
r#"[{"insert":"1"},{"insert":"\n","attributes":{"bullet":"true"}},{"insert":"2"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
Redo(0),
AssertOpsJson(
0,
r#"[{"insert":"1"},{"insert":"\n","attributes":{"bullet":"true"}},{"insert":"2"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
];
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn history_bullet_undo_with_lagging() {
let ops = vec![
Insert(0, "1", 0),
Bullet(0, Interval::new(0, 1), true),
Wait(RECORD_THRESHOLD),
Insert(0, NEW_LINE, 1),
Insert(0, "2", 2),
Wait(RECORD_THRESHOLD),
AssertOpsJson(
0,
r#"[{"insert":"1"},{"insert":"\n","attributes":{"bullet":"true"}},{"insert":"2"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
Undo(0),
AssertOpsJson(
0,
r#"[{"insert":"1"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
Undo(0),
AssertOpsJson(0, r#"[{"insert":"\n"}]"#),
Redo(0),
Redo(0),
AssertOpsJson(
0,
r#"[{"insert":"1"},{"insert":"\n","attributes":{"bullet":"true"}},{"insert":"2"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
];
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn history_undo_attribute_on_merge_between_line() {
let ops = vec![
Insert(0, "123456", 0),
Bullet(0, Interval::new(0, 6), true),
Wait(RECORD_THRESHOLD),
Insert(0, NEW_LINE, 3),
Wait(RECORD_THRESHOLD),
AssertOpsJson(
0,
r#"[{"insert":"123"},{"insert":"\n","attributes":{"bullet":"true"}},{"insert":"456"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
Delete(0, Interval::new(3, 4)), // delete the newline
AssertOpsJson(
0,
r#"[{"insert":"123456"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
Undo(0),
AssertOpsJson(
0,
r#"[{"insert":"123"},{"insert":"\n","attributes":{"bullet":"true"}},{"insert":"456"},{"insert":"\n","attributes":{"bullet":"true"}}]"#,
),
];
OpTester::new().run_script_with_newline(ops);
}