Nathan.fooo 7106195d8a
Feat/calendar with backend data (#1930)
* 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
2023-03-08 21:19:44 +08:00

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,
}