mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-07-24 01:18:56 +00:00

* chore: enable calendar view * chore: update database group listener * refactor: remove board data controller * refactor: remove group backend service * refactor: remove calendar controller * chore: create default calendar setting * chore: send calednar setting notifications * refactor: rename the card in kanban board, prepare to reuse in calendar * refactor: support custom card cell * chore: return calendar events * refactor: remove groupId in card, make card more generic * chore: display cell * chore: display three cards in calendar * chore: create calendar card * refactor: create row with data * chore: support create event * ci: fix tauri build * chore: disable create calendar
426 lines
12 KiB
Rust
426 lines
12 KiB
Rust
use crate::database::block_test::script::RowScript::{AssertCell, CreateRow};
|
|
use crate::database::block_test::util::DatabaseRowTestBuilder;
|
|
use crate::database::database_editor::DatabaseEditorTest;
|
|
use database_model::{
|
|
DatabaseBlockMetaRevision, DatabaseBlockMetaRevisionChangeset, RowChangeset, RowRevision,
|
|
};
|
|
use flowy_database::entities::{CellIdParams, CreateRowParams, FieldType, RowPB};
|
|
use flowy_database::services::field::*;
|
|
use flowy_database::services::row::DatabaseBlockRow;
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use strum::IntoEnumIterator;
|
|
|
|
pub enum RowScript {
|
|
CreateEmptyRow,
|
|
CreateRow {
|
|
row_rev: RowRevision,
|
|
},
|
|
UpdateRow {
|
|
changeset: RowChangeset,
|
|
},
|
|
AssertRow {
|
|
expected_row: RowRevision,
|
|
},
|
|
DeleteRows {
|
|
row_ids: Vec<String>,
|
|
},
|
|
AssertCell {
|
|
row_id: String,
|
|
field_id: String,
|
|
field_type: FieldType,
|
|
expected: String,
|
|
},
|
|
AssertRowCount(usize),
|
|
CreateBlock {
|
|
block: DatabaseBlockMetaRevision,
|
|
},
|
|
UpdateBlock {
|
|
changeset: DatabaseBlockMetaRevisionChangeset,
|
|
},
|
|
AssertBlockCount(usize),
|
|
AssertBlock {
|
|
block_index: usize,
|
|
row_count: i32,
|
|
start_row_index: i32,
|
|
},
|
|
AssertBlockEqual {
|
|
block_index: usize,
|
|
block: DatabaseBlockMetaRevision,
|
|
},
|
|
}
|
|
|
|
pub struct DatabaseRowTest {
|
|
inner: DatabaseEditorTest,
|
|
}
|
|
|
|
impl DatabaseRowTest {
|
|
pub async fn new() -> Self {
|
|
let editor_test = DatabaseEditorTest::new_grid().await;
|
|
Self { inner: editor_test }
|
|
}
|
|
|
|
pub fn last_row(&self) -> Option<RowRevision> {
|
|
self.row_revs.last().map(|a| a.clone().as_ref().clone())
|
|
}
|
|
|
|
pub async fn run_scripts(&mut self, scripts: Vec<RowScript>) {
|
|
for script in scripts {
|
|
self.run_script(script).await;
|
|
}
|
|
}
|
|
|
|
pub fn row_builder(&self) -> DatabaseRowTestBuilder {
|
|
DatabaseRowTestBuilder::new(self.block_id().to_string(), self.field_revs.clone())
|
|
}
|
|
|
|
pub async fn run_script(&mut self, script: RowScript) {
|
|
match script {
|
|
RowScript::CreateEmptyRow => {
|
|
let params = CreateRowParams {
|
|
view_id: self.editor.database_id.clone(),
|
|
start_row_id: None,
|
|
group_id: None,
|
|
cell_data_by_field_id: None,
|
|
};
|
|
let row_order = self.editor.create_row(params).await.unwrap();
|
|
self
|
|
.row_by_row_id
|
|
.insert(row_order.row_id().to_owned(), row_order);
|
|
self.row_revs = self.get_row_revs().await;
|
|
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
|
|
},
|
|
RowScript::CreateRow { row_rev } => {
|
|
let row_orders = self.editor.insert_rows(vec![row_rev]).await.unwrap();
|
|
for row_order in row_orders {
|
|
self
|
|
.row_by_row_id
|
|
.insert(row_order.row_id().to_owned(), row_order);
|
|
}
|
|
self.row_revs = self.get_row_revs().await;
|
|
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
|
|
},
|
|
RowScript::UpdateRow { changeset: change } => self.editor.update_row(change).await.unwrap(),
|
|
RowScript::DeleteRows { row_ids } => {
|
|
let row_pbs = row_ids
|
|
.into_iter()
|
|
.map(|row_id| self.row_by_row_id.get(&row_id).unwrap().clone())
|
|
.collect::<Vec<RowPB>>();
|
|
|
|
let block_rows = block_from_row_pbs(row_pbs);
|
|
self.editor.delete_rows(block_rows).await.unwrap();
|
|
self.row_revs = self.get_row_revs().await;
|
|
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
|
|
},
|
|
RowScript::AssertCell {
|
|
row_id,
|
|
field_id,
|
|
field_type,
|
|
expected,
|
|
} => {
|
|
let id = CellIdParams {
|
|
view_id: self.view_id.clone(),
|
|
field_id,
|
|
row_id,
|
|
};
|
|
self.compare_cell_content(id, field_type, expected).await;
|
|
},
|
|
RowScript::AssertRow { expected_row } => {
|
|
let row = &*self
|
|
.row_revs
|
|
.iter()
|
|
.find(|row| row.id == expected_row.id)
|
|
.cloned()
|
|
.unwrap();
|
|
assert_eq!(&expected_row, row);
|
|
},
|
|
RowScript::AssertRowCount(expected_row_count) => {
|
|
assert_eq!(expected_row_count, self.row_revs.len());
|
|
},
|
|
RowScript::CreateBlock { block } => {
|
|
self.editor.create_block(block).await.unwrap();
|
|
self.block_meta_revs = self.editor.get_block_meta_revs().await.unwrap();
|
|
},
|
|
RowScript::UpdateBlock { changeset: change } => {
|
|
self.editor.update_block(change).await.unwrap();
|
|
},
|
|
RowScript::AssertBlockCount(count) => {
|
|
assert_eq!(
|
|
self.editor.get_block_meta_revs().await.unwrap().len(),
|
|
count
|
|
);
|
|
},
|
|
RowScript::AssertBlock {
|
|
block_index,
|
|
row_count,
|
|
start_row_index,
|
|
} => {
|
|
assert_eq!(self.block_meta_revs[block_index].row_count, row_count);
|
|
assert_eq!(
|
|
self.block_meta_revs[block_index].start_row_index,
|
|
start_row_index
|
|
);
|
|
},
|
|
RowScript::AssertBlockEqual { block_index, block } => {
|
|
let blocks = self.editor.get_block_meta_revs().await.unwrap();
|
|
let compared_block = blocks[block_index].clone();
|
|
assert_eq!(compared_block, Arc::new(block));
|
|
},
|
|
}
|
|
}
|
|
|
|
async fn compare_cell_content(
|
|
&self,
|
|
cell_id: CellIdParams,
|
|
field_type: FieldType,
|
|
expected: String,
|
|
) {
|
|
match field_type {
|
|
FieldType::RichText => {
|
|
let cell_data = self
|
|
.editor
|
|
.get_cell_protobuf(&cell_id)
|
|
.await
|
|
.unwrap()
|
|
.parser::<TextCellDataParser>()
|
|
.unwrap();
|
|
|
|
assert_eq!(cell_data.as_ref(), &expected);
|
|
},
|
|
FieldType::Number => {
|
|
let field_rev = self.editor.get_field_rev(&cell_id.field_id).await.unwrap();
|
|
let number_type_option = field_rev
|
|
.get_type_option::<NumberTypeOptionPB>(FieldType::Number.into())
|
|
.unwrap();
|
|
let cell_data = self
|
|
.editor
|
|
.get_cell_protobuf(&cell_id)
|
|
.await
|
|
.unwrap()
|
|
.custom_parser(NumberCellCustomDataParser(number_type_option.format))
|
|
.unwrap();
|
|
assert_eq!(cell_data.to_string(), expected);
|
|
},
|
|
FieldType::DateTime => {
|
|
let cell_data = self
|
|
.editor
|
|
.get_cell_protobuf(&cell_id)
|
|
.await
|
|
.unwrap()
|
|
.parser::<DateCellDataParser>()
|
|
.unwrap();
|
|
|
|
assert_eq!(cell_data.date, expected);
|
|
},
|
|
FieldType::SingleSelect => {
|
|
let cell_data = self
|
|
.editor
|
|
.get_cell_protobuf(&cell_id)
|
|
.await
|
|
.unwrap()
|
|
.parser::<SelectOptionCellDataParser>()
|
|
.unwrap();
|
|
let select_option = cell_data.select_options.first().unwrap();
|
|
assert_eq!(select_option.name, expected);
|
|
},
|
|
FieldType::MultiSelect => {
|
|
let cell_data = self
|
|
.editor
|
|
.get_cell_protobuf(&cell_id)
|
|
.await
|
|
.unwrap()
|
|
.parser::<SelectOptionCellDataParser>()
|
|
.unwrap();
|
|
|
|
let s = cell_data
|
|
.select_options
|
|
.into_iter()
|
|
.map(|option| option.name)
|
|
.collect::<Vec<String>>()
|
|
.join(SELECTION_IDS_SEPARATOR);
|
|
|
|
assert_eq!(s, expected);
|
|
},
|
|
|
|
FieldType::Checklist => {
|
|
let cell_data = self
|
|
.editor
|
|
.get_cell_protobuf(&cell_id)
|
|
.await
|
|
.unwrap()
|
|
.parser::<SelectOptionCellDataParser>()
|
|
.unwrap();
|
|
|
|
let s = cell_data
|
|
.select_options
|
|
.into_iter()
|
|
.map(|option| option.name)
|
|
.collect::<Vec<String>>()
|
|
.join(SELECTION_IDS_SEPARATOR);
|
|
|
|
assert_eq!(s, expected);
|
|
},
|
|
FieldType::Checkbox => {
|
|
let cell_data = self
|
|
.editor
|
|
.get_cell_protobuf(&cell_id)
|
|
.await
|
|
.unwrap()
|
|
.parser::<CheckboxCellDataParser>()
|
|
.unwrap();
|
|
assert_eq!(cell_data.to_string(), expected);
|
|
},
|
|
FieldType::URL => {
|
|
let cell_data = self
|
|
.editor
|
|
.get_cell_protobuf(&cell_id)
|
|
.await
|
|
.unwrap()
|
|
.parser::<URLCellDataParser>()
|
|
.unwrap();
|
|
|
|
assert_eq!(cell_data.content, expected);
|
|
// assert_eq!(cell_data.url, expected);
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn block_from_row_pbs(row_orders: Vec<RowPB>) -> Vec<DatabaseBlockRow> {
|
|
let mut map: HashMap<String, DatabaseBlockRow> = HashMap::new();
|
|
row_orders.into_iter().for_each(|row_pb| {
|
|
let block_id = row_pb.block_id().to_owned();
|
|
let cloned_block_id = block_id.clone();
|
|
map
|
|
.entry(block_id)
|
|
.or_insert_with(|| DatabaseBlockRow::new(cloned_block_id, vec![]))
|
|
.row_ids
|
|
.push(row_pb.id);
|
|
});
|
|
map.into_values().collect::<Vec<_>>()
|
|
}
|
|
|
|
impl std::ops::Deref for DatabaseRowTest {
|
|
type Target = DatabaseEditorTest;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.inner
|
|
}
|
|
}
|
|
|
|
impl std::ops::DerefMut for DatabaseRowTest {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.inner
|
|
}
|
|
}
|
|
|
|
pub struct CreateRowScriptBuilder {
|
|
builder: DatabaseRowTestBuilder,
|
|
data_by_field_type: HashMap<FieldType, CellTestData>,
|
|
output_by_field_type: HashMap<FieldType, CellTestOutput>,
|
|
}
|
|
|
|
impl CreateRowScriptBuilder {
|
|
pub fn new(test: &DatabaseRowTest) -> Self {
|
|
Self {
|
|
builder: test.row_builder(),
|
|
data_by_field_type: HashMap::new(),
|
|
output_by_field_type: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub fn insert(&mut self, field_type: FieldType, input: &str, expected: &str) {
|
|
self.data_by_field_type.insert(
|
|
field_type,
|
|
CellTestData {
|
|
input: input.to_string(),
|
|
expected: expected.to_owned(),
|
|
},
|
|
);
|
|
}
|
|
|
|
pub fn insert_single_select_cell<F>(&mut self, f: F, expected: &str)
|
|
where
|
|
F: Fn(Vec<SelectOptionPB>) -> SelectOptionPB,
|
|
{
|
|
let field_id = self.builder.insert_single_select_cell(f);
|
|
self.output_by_field_type.insert(
|
|
FieldType::SingleSelect,
|
|
CellTestOutput {
|
|
field_id,
|
|
expected: expected.to_owned(),
|
|
},
|
|
);
|
|
}
|
|
|
|
pub fn insert_multi_select_cell<F>(&mut self, f: F, expected: &str)
|
|
where
|
|
F: Fn(Vec<SelectOptionPB>) -> Vec<SelectOptionPB>,
|
|
{
|
|
let field_id = self.builder.insert_multi_select_cell(f);
|
|
self.output_by_field_type.insert(
|
|
FieldType::MultiSelect,
|
|
CellTestOutput {
|
|
field_id,
|
|
expected: expected.to_owned(),
|
|
},
|
|
);
|
|
}
|
|
|
|
pub fn build(mut self) -> Vec<RowScript> {
|
|
let mut scripts = vec![];
|
|
let output_by_field_type = &mut self.output_by_field_type;
|
|
|
|
for field_type in FieldType::iter() {
|
|
let field_type: FieldType = field_type;
|
|
if let Some(data) = self.data_by_field_type.get(&field_type) {
|
|
let field_id = match field_type {
|
|
FieldType::RichText => self.builder.insert_text_cell(&data.input),
|
|
FieldType::Number => self.builder.insert_number_cell(&data.input),
|
|
FieldType::DateTime => self.builder.insert_date_cell(&data.input),
|
|
FieldType::Checkbox => self.builder.insert_checkbox_cell(&data.input),
|
|
FieldType::URL => self.builder.insert_url_cell(&data.input),
|
|
_ => "".to_owned(),
|
|
};
|
|
|
|
if !field_id.is_empty() {
|
|
output_by_field_type.insert(
|
|
field_type,
|
|
CellTestOutput {
|
|
field_id,
|
|
expected: data.expected.clone(),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
let row_rev = self.builder.build();
|
|
let row_id = row_rev.id.clone();
|
|
scripts.push(CreateRow { row_rev });
|
|
|
|
for field_type in FieldType::iter() {
|
|
if let Some(data) = output_by_field_type.get(&field_type) {
|
|
let script = AssertCell {
|
|
row_id: row_id.clone(),
|
|
field_id: data.field_id.clone(),
|
|
field_type,
|
|
expected: data.expected.clone(),
|
|
};
|
|
scripts.push(script);
|
|
}
|
|
}
|
|
scripts
|
|
}
|
|
}
|
|
|
|
pub struct CellTestData {
|
|
pub input: String,
|
|
pub expected: String,
|
|
}
|
|
|
|
struct CellTestOutput {
|
|
field_id: String,
|
|
expected: String,
|
|
}
|