automerge/automerge-frontend/tests/test_cursor.rs
Andrew Jeffery fc1b8f87fb
Use SmolStr in place of String (#182)
Most of the strings are small and so fit nicely in a SmolStr. When they
don't it just reverts to using a normal String.
2021-06-19 16:28:51 +01:00

291 lines
11 KiB
Rust

use std::convert::TryInto;
use amp::RootDiff;
use automerge_backend::Backend;
use automerge_frontend::{Frontend, InvalidChangeRequest, LocalChange, Path, Primitive, Value};
use automerge_protocol as amp;
use maplit::hashmap;
use unicode_segmentation::UnicodeSegmentation;
#[test]
fn test_allow_cursor_on_list_element() {
let _ = env_logger::builder().is_test(true).try_init().unwrap();
let mut frontend = Frontend::new();
let change = frontend
.change::<_, _, InvalidChangeRequest>(None, |d| {
d.add_change(LocalChange::set(Path::root().key("list"), vec![1, 2, 3]))?;
let cursor = d
.cursor_to_path(&Path::root().key("list").index(1))
.unwrap();
d.add_change(LocalChange::set(Path::root().key("cursor"), cursor))?;
Ok(())
})
.unwrap()
.1
.unwrap();
let mut backend = Backend::new();
backend
.apply_changes(vec![change.try_into().unwrap()])
.unwrap();
let mut backend2 = Backend::new();
backend2
.apply_changes(backend.get_changes(&[]).into_iter().cloned().collect())
.unwrap();
let mut frontend2 = Frontend::new();
frontend2
.apply_patch(backend2.get_patch().unwrap())
.unwrap();
let index_value = frontend2.get_value(&Path::root().key("cursor")).unwrap();
if let Value::Primitive(Primitive::Cursor(c)) = index_value {
assert_eq!(c.index, 1)
} else {
panic!("value was not a cursor");
}
}
#[test]
fn test_allow_cursor_on_text_element() {
let mut frontend = Frontend::new();
let change = frontend
.change::<_, _, InvalidChangeRequest>(None, |d| {
d.add_change(LocalChange::set(
Path::root().key("list"),
Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
))?;
let cursor = d
.cursor_to_path(&Path::root().key("list").index(1))
.unwrap();
d.add_change(LocalChange::set(Path::root().key("cursor"), cursor))?;
Ok(())
})
.unwrap()
.1
.unwrap();
let mut backend = Backend::new();
backend
.apply_changes(vec![change.try_into().unwrap()])
.unwrap();
let mut backend2 = Backend::new();
backend2
.apply_changes(backend.get_changes(&[]).into_iter().cloned().collect())
.unwrap();
let mut frontend2 = Frontend::new();
frontend2
.apply_patch(backend2.get_patch().unwrap())
.unwrap();
let index_value = frontend2.get_value(&Path::root().key("cursor")).unwrap();
if let Value::Primitive(Primitive::Cursor(c)) = index_value {
assert_eq!(c.index, 1)
} else {
panic!("value was not a cursor");
}
}
#[test]
fn test_do_not_allow_index_past_end_of_list() {
let mut frontend = Frontend::new();
frontend
.change::<_, _, InvalidChangeRequest>(None, |d| {
d.add_change(LocalChange::set(
Path::root().key("list"),
Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
))?;
let cursor = d.cursor_to_path(&Path::root().key("list").index(10));
assert_eq!(cursor, None);
Ok(())
})
.unwrap();
}
// #[test]
// fn test_updates_cursor_during_change_function() {
// let mut frontend = Frontend::new();
// frontend
// .change::<_, _, InvalidChangeRequest>(None, |d| {
// d.add_change(LocalChange::set(
// Path::root().key("list"),
// Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
// ))?;
// let cursor = d
// .cursor_to_path(&Path::root().key("list").index(1))
// .unwrap();
// d.add_change(LocalChange::set(Path::root().key("cursor"), cursor))?;
// let cursor_the_second = d.value_at_path(&Path::root().key("cursor"));
// if let Some(Value::Primitive(Primitive::Cursor(c))) = cursor_the_second {
// assert_eq!(c.index, 1);
// } else {
// panic!("Cursor the second not found");
// }
// d.add_change(LocalChange::insert(
// Path::root().key("list").index(0),
// Value::Primitive(Primitive::Str("0".into())),
// ))?;
// let cursor_the_third = d.value_at_path(&Path::root().key("cursor"));
// if let Some(Value::Primitive(Primitive::Cursor(c))) = cursor_the_third {
// assert_eq!(c.index, 2);
// } else {
// panic!("Cursor the third not found");
// }
// Ok(())
// })
// .unwrap();
// }
#[test]
fn test_set_cursor_to_new_element_in_diff() {
let mut frontend = Frontend::new();
let actor = frontend.actor_id.clone();
let patch1 = amp::Patch {
actor: Some(actor.clone()),
deps: Vec::new(),
seq: Some(1),
clock: hashmap! {actor.clone() => 1},
max_op: 3,
pending_changes: 0,
diffs: RootDiff {
props: hashmap! {
"list".into() => hashmap!{
actor.op_id_at(1) => amp::Diff::List(amp::ListDiff{
object_id: actor.op_id_at(1).into(),
edits: vec![
amp::DiffEdit::SingleElementInsert{
index: 0,
elem_id: actor.op_id_at(2).into(),
op_id: actor.op_id_at(2),
value: amp::Diff::Value("one".into()),
},
amp::DiffEdit::SingleElementInsert{
index: 1,
elem_id: actor.op_id_at(3).into(),
op_id: actor.op_id_at(3),
value: amp::Diff::Value("two".into()),
},
],
}),
},
"cursor".into() => hashmap!{
actor.op_id_at(4) => amp::Diff::Cursor(amp::CursorDiff{
elem_id: actor.op_id_at(3),
index: 1,
object_id: actor.op_id_at(1).into(),
})
},
},
},
};
frontend.apply_patch(patch1).unwrap();
let patch2 = amp::Patch {
actor: Some(actor.clone()),
deps: Vec::new(),
seq: Some(2),
clock: hashmap! {actor.clone() => 2},
max_op: 5,
pending_changes: 0,
diffs: RootDiff {
props: hashmap! {
"cursor".into() => hashmap!{
actor.op_id_at(4) => amp::Diff::Cursor(amp::CursorDiff{
elem_id: actor.op_id_at(2),
index: 0,
object_id: actor.op_id_at(1).into(),
})
}
},
},
};
frontend.apply_patch(patch2).unwrap();
frontend
.change::<_, _, InvalidChangeRequest>(None, |doc| {
doc.add_change(LocalChange::insert(
Path::root().key("list").index(1),
"three".into(),
))?;
let cursor = doc.value_at_path(&Path::root().key("cursor")).unwrap();
match cursor {
Value::Primitive(Primitive::Cursor(c)) => assert_eq!(c.index, 0),
_ => panic!("Cursor value was not a cursor"),
}
Ok(())
})
.unwrap();
}
// #[test]
// fn test_set_cursor_to_new_element_in_local_change() {
// let mut frontend = Frontend::new();
// frontend
// .change::<_, _, InvalidChangeRequest>(None, |d| {
// d.add_change(LocalChange::set(
// Path::root().key("list"),
// Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
// ))?;
// let cursor = d
// .cursor_to_path(&Path::root().key("list").index(1))
// .unwrap();
// d.add_change(LocalChange::set(Path::root().key("cursor"), cursor))?;
// let cursor_the_second = d.value_at_path(&Path::root().key("cursor"));
// if let Some(Value::Primitive(Primitive::Cursor(c))) = cursor_the_second {
// assert_eq!(c.index, 1);
// } else {
// panic!("Cursor the second not found");
// }
// d.add_change(LocalChange::insert(
// Path::root().key("list").index(0),
// Value::Primitive(Primitive::Str("0".into())),
// ))?;
// d.add_change(LocalChange::insert(
// Path::root().key("list").index(0),
// Value::Primitive(Primitive::Str("1".into())),
// ))?;
// let cursor = d
// .cursor_to_path(&Path::root().key("list").index(2))
// .unwrap();
// d.add_change(LocalChange::set(Path::root().key("cursor"), cursor))?;
// d.add_change(LocalChange::insert(
// Path::root().key("list").index(4),
// "2".into(),
// ))?;
// let cursor_the_third = d.value_at_path(&Path::root().key("cursor"));
// if let Some(Value::Primitive(Primitive::Cursor(c))) = cursor_the_third {
// assert_eq!(c.index, 3);
// } else {
// panic!("Cursor the third not found");
// }
// Ok(())
// })
// .unwrap();
// }
#[test]
fn test_delete_cursor_and_adding_again() {
let mut frontend = Frontend::new();
frontend
.change::<_, _, InvalidChangeRequest>(None, |d| {
d.add_change(LocalChange::set(
Path::root().key("list"),
Value::Text("123".graphemes(true).map(|s| s.into()).collect()),
))?;
let cursor = d
.cursor_to_path(&Path::root().key("list").index(1))
.unwrap();
d.add_change(LocalChange::set(Path::root().key("cursor"), cursor.clone()))?;
d.add_change(LocalChange::delete(Path::root().key("cursor")))?;
d.add_change(LocalChange::set(Path::root().key("cursor"), cursor))?;
let cursor_value = d.value_at_path(&Path::root().key("cursor"));
if let Some(Value::Primitive(Primitive::Cursor(c))) = cursor_value {
assert_eq!(c.index, 1);
} else {
panic!("Cursor the third not found");
}
Ok(())
})
.unwrap();
}
//TODO test removing a cursors