mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: advanced filters backend logic (#4688)
* feat: implement advanced filters * test: adapt tests to changes * test: add advanced filter tests * chore: adapt flutter frontend to changes * chore: adapt tauri frontend to changes * chore: bump collab * chore: launch review --------- Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
@ -0,0 +1,314 @@
|
||||
use bytes::Bytes;
|
||||
use flowy_database2::entities::{
|
||||
CheckboxFilterConditionPB, CheckboxFilterPB, DateFilterConditionPB, DateFilterPB, FieldType,
|
||||
FilterDataPB, FilterPB, FilterType, NumberFilterConditionPB, NumberFilterPB,
|
||||
};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use protobuf::ProtobufError;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged, FilterScript::*};
|
||||
|
||||
/// Create a single advanced filter:
|
||||
///
|
||||
/// 1. Add an OR filter
|
||||
/// 2. Add a Checkbox and an AND filter to its children
|
||||
/// 3. Add a DateTime and a Number filter to the AND filter's children
|
||||
///
|
||||
#[tokio::test]
|
||||
async fn create_advanced_filter_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
|
||||
let create_checkbox_filter = || -> CheckboxFilterPB {
|
||||
CheckboxFilterPB {
|
||||
condition: CheckboxFilterConditionPB::IsChecked,
|
||||
}
|
||||
};
|
||||
|
||||
let create_date_filter = || -> DateFilterPB {
|
||||
DateFilterPB {
|
||||
condition: DateFilterConditionPB::DateAfter,
|
||||
timestamp: Some(1651366800),
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
|
||||
let create_number_filter = || -> NumberFilterPB {
|
||||
NumberFilterPB {
|
||||
condition: NumberFilterConditionPB::NumberIsNotEmpty,
|
||||
content: "".to_string(),
|
||||
}
|
||||
};
|
||||
|
||||
let scripts = vec![
|
||||
CreateOrFilter {
|
||||
parent_filter_id: None,
|
||||
changed: None,
|
||||
},
|
||||
Wait { millisecond: 100 },
|
||||
AssertFilters {
|
||||
expected: vec![FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Or,
|
||||
children: vec![],
|
||||
data: None,
|
||||
}],
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
// OR
|
||||
|
||||
let or_filter = test.get_filter(FilterType::Or, None).await.unwrap();
|
||||
|
||||
let checkbox_filter_bytes: Result<Bytes, ProtobufError> = create_checkbox_filter().try_into();
|
||||
let checkbox_filter_bytes = checkbox_filter_bytes.unwrap().to_vec();
|
||||
|
||||
let scripts = vec![
|
||||
CreateDataFilter {
|
||||
parent_filter_id: Some(or_filter.id.clone()),
|
||||
field_type: FieldType::Checkbox,
|
||||
data: BoxAny::new(create_checkbox_filter()),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 4,
|
||||
}),
|
||||
},
|
||||
CreateAndFilter {
|
||||
parent_filter_id: Some(or_filter.id),
|
||||
changed: None,
|
||||
},
|
||||
Wait { millisecond: 100 },
|
||||
AssertFilters {
|
||||
expected: vec![FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Or,
|
||||
children: vec![
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Data,
|
||||
children: vec![],
|
||||
data: Some(FilterDataPB {
|
||||
field_id: "".to_string(),
|
||||
field_type: FieldType::Checkbox,
|
||||
data: checkbox_filter_bytes.clone(),
|
||||
}),
|
||||
},
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::And,
|
||||
children: vec![],
|
||||
data: None,
|
||||
},
|
||||
],
|
||||
data: None,
|
||||
}],
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 3 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
// IS_CHECK OR AND
|
||||
|
||||
let and_filter = test.get_filter(FilterType::And, None).await.unwrap();
|
||||
|
||||
let date_filter_bytes: Result<Bytes, ProtobufError> = create_date_filter().try_into();
|
||||
let date_filter_bytes = date_filter_bytes.unwrap().to_vec();
|
||||
let number_filter_bytes: Result<Bytes, ProtobufError> = create_number_filter().try_into();
|
||||
let number_filter_bytes = number_filter_bytes.unwrap().to_vec();
|
||||
|
||||
let scripts = vec![
|
||||
CreateDataFilter {
|
||||
parent_filter_id: Some(and_filter.id.clone()),
|
||||
field_type: FieldType::DateTime,
|
||||
data: BoxAny::new(create_date_filter()),
|
||||
changed: None,
|
||||
},
|
||||
CreateDataFilter {
|
||||
parent_filter_id: Some(and_filter.id),
|
||||
field_type: FieldType::Number,
|
||||
data: BoxAny::new(create_number_filter()),
|
||||
changed: None,
|
||||
},
|
||||
Wait { millisecond: 100 },
|
||||
AssertFilters {
|
||||
expected: vec![FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Or,
|
||||
children: vec![
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Data,
|
||||
children: vec![],
|
||||
data: Some(FilterDataPB {
|
||||
field_id: "".to_string(),
|
||||
field_type: FieldType::Checkbox,
|
||||
data: checkbox_filter_bytes,
|
||||
}),
|
||||
},
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::And,
|
||||
children: vec![
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Data,
|
||||
children: vec![],
|
||||
data: Some(FilterDataPB {
|
||||
field_id: "".to_string(),
|
||||
field_type: FieldType::DateTime,
|
||||
data: date_filter_bytes,
|
||||
}),
|
||||
},
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Data,
|
||||
children: vec![],
|
||||
data: Some(FilterDataPB {
|
||||
field_id: "".to_string(),
|
||||
field_type: FieldType::Number,
|
||||
data: number_filter_bytes,
|
||||
}),
|
||||
},
|
||||
],
|
||||
data: None,
|
||||
},
|
||||
],
|
||||
data: None,
|
||||
}],
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 4 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
// IS_CHECK OR (DATE > 1651366800 AND NUMBER NOT EMPTY)
|
||||
}
|
||||
|
||||
/// Create the same advanced filter single advanced filter:
|
||||
///
|
||||
/// 1. Add an OR filter
|
||||
/// 2. Add a Checkbox and a DateTime filter to its children
|
||||
/// 3. Add a Number filter to the DateTime filter's children
|
||||
///
|
||||
#[tokio::test]
|
||||
async fn create_advanced_filter_with_conversion_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
|
||||
let create_checkbox_filter = || -> CheckboxFilterPB {
|
||||
CheckboxFilterPB {
|
||||
condition: CheckboxFilterConditionPB::IsChecked,
|
||||
}
|
||||
};
|
||||
|
||||
let create_date_filter = || -> DateFilterPB {
|
||||
DateFilterPB {
|
||||
condition: DateFilterConditionPB::DateAfter,
|
||||
timestamp: Some(1651366800),
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
|
||||
let create_number_filter = || -> NumberFilterPB {
|
||||
NumberFilterPB {
|
||||
condition: NumberFilterConditionPB::NumberIsNotEmpty,
|
||||
content: "".to_string(),
|
||||
}
|
||||
};
|
||||
|
||||
let scripts = vec![CreateOrFilter {
|
||||
parent_filter_id: None,
|
||||
changed: None,
|
||||
}];
|
||||
test.run_scripts(scripts).await;
|
||||
// IS_CHECK OR DATE > 1651366800
|
||||
|
||||
let or_filter = test.get_filter(FilterType::Or, None).await.unwrap();
|
||||
|
||||
let scripts = vec![
|
||||
CreateDataFilter {
|
||||
parent_filter_id: Some(or_filter.id.clone()),
|
||||
field_type: FieldType::Checkbox,
|
||||
data: BoxAny::new(create_checkbox_filter()),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 4,
|
||||
}),
|
||||
},
|
||||
CreateDataFilter {
|
||||
parent_filter_id: Some(or_filter.id.clone()),
|
||||
field_type: FieldType::DateTime,
|
||||
data: BoxAny::new(create_date_filter()),
|
||||
changed: None,
|
||||
},
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
// OR
|
||||
|
||||
let date_filter = test
|
||||
.get_filter(FilterType::Data, Some(FieldType::DateTime))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let checkbox_filter_bytes: Result<Bytes, ProtobufError> = create_checkbox_filter().try_into();
|
||||
let checkbox_filter_bytes = checkbox_filter_bytes.unwrap().to_vec();
|
||||
let date_filter_bytes: Result<Bytes, ProtobufError> = create_date_filter().try_into();
|
||||
let date_filter_bytes = date_filter_bytes.unwrap().to_vec();
|
||||
let number_filter_bytes: Result<Bytes, ProtobufError> = create_number_filter().try_into();
|
||||
let number_filter_bytes = number_filter_bytes.unwrap().to_vec();
|
||||
|
||||
let scripts = vec![
|
||||
CreateDataFilter {
|
||||
parent_filter_id: Some(date_filter.id),
|
||||
field_type: FieldType::Number,
|
||||
data: BoxAny::new(create_number_filter()),
|
||||
changed: None,
|
||||
},
|
||||
Wait { millisecond: 100 },
|
||||
AssertFilters {
|
||||
expected: vec![FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Or,
|
||||
children: vec![
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Data,
|
||||
children: vec![],
|
||||
data: Some(FilterDataPB {
|
||||
field_id: "".to_string(),
|
||||
field_type: FieldType::Checkbox,
|
||||
data: checkbox_filter_bytes,
|
||||
}),
|
||||
},
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::And,
|
||||
children: vec![
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Data,
|
||||
children: vec![],
|
||||
data: Some(FilterDataPB {
|
||||
field_id: "".to_string(),
|
||||
field_type: FieldType::DateTime,
|
||||
data: date_filter_bytes,
|
||||
}),
|
||||
},
|
||||
FilterPB {
|
||||
id: "".to_string(),
|
||||
filter_type: FilterType::Data,
|
||||
children: vec![],
|
||||
data: Some(FilterDataPB {
|
||||
field_id: "".to_string(),
|
||||
field_type: FieldType::Number,
|
||||
data: number_filter_bytes,
|
||||
}),
|
||||
},
|
||||
],
|
||||
data: None,
|
||||
},
|
||||
],
|
||||
data: None,
|
||||
}],
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 4 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
// IS_CHECK OR (DATE > 1651366800 AND NUMBER NOT EMPTY)
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
use flowy_database2::entities::CheckboxFilterConditionPB;
|
||||
use flowy_database2::entities::{CheckboxFilterConditionPB, CheckboxFilterPB, FieldType};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
|
||||
use crate::database::filter_test::script::FilterScript::*;
|
||||
use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged};
|
||||
@ -6,27 +7,39 @@ use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged}
|
||||
#[tokio::test]
|
||||
async fn grid_filter_checkbox_is_check_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let expected = 3;
|
||||
let row_count = test.row_details.len();
|
||||
// The initial number of unchecked is 3
|
||||
// The initial number of checked is 2
|
||||
let scripts = vec![CreateCheckboxFilter {
|
||||
condition: CheckboxFilterConditionPB::IsChecked,
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - 3,
|
||||
}),
|
||||
}];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_checkbox_is_uncheck_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let expected = 4;
|
||||
let row_count = test.row_details.len();
|
||||
// The initial number of checked is 3
|
||||
// The initial number of unchecked is 4
|
||||
let scripts = vec![
|
||||
CreateCheckboxFilter {
|
||||
condition: CheckboxFilterConditionPB::IsUnChecked,
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Checkbox,
|
||||
data: BoxAny::new(CheckboxFilterPB {
|
||||
condition: CheckboxFilterConditionPB::IsChecked,
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
}),
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_checkbox_is_uncheck_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let expected = 4;
|
||||
let row_count = test.row_details.len();
|
||||
let scripts = vec![
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Checkbox,
|
||||
data: BoxAny::new(CheckboxFilterPB {
|
||||
condition: CheckboxFilterConditionPB::IsUnChecked,
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
|
@ -1,5 +1,6 @@
|
||||
use flowy_database2::entities::{ChecklistFilterConditionPB, FieldType};
|
||||
use flowy_database2::entities::{ChecklistFilterConditionPB, ChecklistFilterPB, FieldType};
|
||||
use flowy_database2::services::field::checklist_type_option::ChecklistCellData;
|
||||
use lib_infra::box_any::BoxAny;
|
||||
|
||||
use crate::database::filter_test::script::FilterScript::*;
|
||||
use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged};
|
||||
@ -16,8 +17,12 @@ async fn grid_filter_checklist_is_incomplete_test() {
|
||||
row_id: test.row_details[0].row.id.clone(),
|
||||
selected_option_ids: option_ids,
|
||||
},
|
||||
CreateChecklistFilter {
|
||||
condition: ChecklistFilterConditionPB::IsIncomplete,
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Checklist,
|
||||
data: BoxAny::new(ChecklistFilterPB {
|
||||
condition: ChecklistFilterConditionPB::IsIncomplete,
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -39,8 +44,12 @@ async fn grid_filter_checklist_is_complete_test() {
|
||||
row_id: test.row_details[0].row.id.clone(),
|
||||
selected_option_ids: option_ids,
|
||||
},
|
||||
CreateChecklistFilter {
|
||||
condition: ChecklistFilterConditionPB::IsComplete,
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Checklist,
|
||||
data: BoxAny::new(ChecklistFilterPB {
|
||||
condition: ChecklistFilterConditionPB::IsComplete,
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
|
@ -1,4 +1,5 @@
|
||||
use flowy_database2::entities::DateFilterConditionPB;
|
||||
use flowy_database2::entities::{DateFilterConditionPB, DateFilterPB, FieldType};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
|
||||
use crate::database::filter_test::script::FilterScript::*;
|
||||
use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged};
|
||||
@ -9,11 +10,15 @@ async fn grid_filter_date_is_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 3;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterConditionPB::DateIs,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::DateTime,
|
||||
data: BoxAny::new(DateFilterPB {
|
||||
condition: DateFilterConditionPB::DateIs,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -30,11 +35,15 @@ async fn grid_filter_date_after_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 3;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterConditionPB::DateAfter,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::DateTime,
|
||||
data: BoxAny::new(DateFilterPB {
|
||||
condition: DateFilterConditionPB::DateAfter,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -51,11 +60,15 @@ async fn grid_filter_date_on_or_after_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 3;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterConditionPB::DateOnOrAfter,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::DateTime,
|
||||
data: BoxAny::new(DateFilterPB {
|
||||
condition: DateFilterConditionPB::DateOnOrAfter,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -72,11 +85,15 @@ async fn grid_filter_date_on_or_before_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 4;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterConditionPB::DateOnOrBefore,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::DateTime,
|
||||
data: BoxAny::new(DateFilterPB {
|
||||
condition: DateFilterConditionPB::DateOnOrBefore,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -93,11 +110,15 @@ async fn grid_filter_date_within_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 5;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterConditionPB::DateWithIn,
|
||||
start: Some(1647251762),
|
||||
end: Some(1668704685),
|
||||
timestamp: None,
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::DateTime,
|
||||
data: BoxAny::new(DateFilterPB {
|
||||
condition: DateFilterConditionPB::DateWithIn,
|
||||
start: Some(1647251762),
|
||||
end: Some(1668704685),
|
||||
timestamp: None,
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
|
@ -1,3 +1,4 @@
|
||||
mod advanced_filter_test;
|
||||
mod checkbox_filter_test;
|
||||
mod checklist_filter_test;
|
||||
mod date_filter_test;
|
||||
|
@ -1,4 +1,5 @@
|
||||
use flowy_database2::entities::NumberFilterConditionPB;
|
||||
use flowy_database2::entities::{FieldType, NumberFilterConditionPB, NumberFilterPB};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
|
||||
use crate::database::filter_test::script::FilterScript::*;
|
||||
use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged};
|
||||
@ -9,9 +10,13 @@ async fn grid_filter_number_is_equal_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 1;
|
||||
let scripts = vec![
|
||||
CreateNumberFilter {
|
||||
condition: NumberFilterConditionPB::Equal,
|
||||
content: "1".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Number,
|
||||
data: BoxAny::new(NumberFilterPB {
|
||||
condition: NumberFilterConditionPB::Equal,
|
||||
content: "1".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -28,9 +33,13 @@ async fn grid_filter_number_is_less_than_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 2;
|
||||
let scripts = vec![
|
||||
CreateNumberFilter {
|
||||
condition: NumberFilterConditionPB::LessThan,
|
||||
content: "3".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Number,
|
||||
data: BoxAny::new(NumberFilterPB {
|
||||
condition: NumberFilterConditionPB::LessThan,
|
||||
content: "3".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -48,9 +57,13 @@ async fn grid_filter_number_is_less_than_test2() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 2;
|
||||
let scripts = vec![
|
||||
CreateNumberFilter {
|
||||
condition: NumberFilterConditionPB::LessThan,
|
||||
content: "$3".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Number,
|
||||
data: BoxAny::new(NumberFilterPB {
|
||||
condition: NumberFilterConditionPB::LessThan,
|
||||
content: "$3".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -67,9 +80,13 @@ async fn grid_filter_number_is_less_than_or_equal_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 3;
|
||||
let scripts = vec![
|
||||
CreateNumberFilter {
|
||||
condition: NumberFilterConditionPB::LessThanOrEqualTo,
|
||||
content: "3".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Number,
|
||||
data: BoxAny::new(NumberFilterPB {
|
||||
condition: NumberFilterConditionPB::LessThanOrEqualTo,
|
||||
content: "3".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -86,9 +103,13 @@ async fn grid_filter_number_is_empty_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 2;
|
||||
let scripts = vec![
|
||||
CreateNumberFilter {
|
||||
condition: NumberFilterConditionPB::NumberIsEmpty,
|
||||
content: "".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Number,
|
||||
data: BoxAny::new(NumberFilterPB {
|
||||
condition: NumberFilterConditionPB::NumberIsEmpty,
|
||||
content: "".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -105,9 +126,13 @@ async fn grid_filter_number_is_not_empty_test() {
|
||||
let row_count = test.row_details.len();
|
||||
let expected = 5;
|
||||
let scripts = vec![
|
||||
CreateNumberFilter {
|
||||
condition: NumberFilterConditionPB::NumberIsNotEmpty,
|
||||
content: "".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::Number,
|
||||
data: BoxAny::new(NumberFilterPB {
|
||||
condition: NumberFilterConditionPB::NumberIsNotEmpty,
|
||||
content: "".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
|
@ -3,14 +3,12 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use collab_database::rows::RowId;
|
||||
use flowy_database2::services::filter::FilterContext;
|
||||
use flowy_database2::services::filter::{FilterChangeset, FilterInner};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use tokio::sync::broadcast::Receiver;
|
||||
|
||||
use flowy_database2::entities::{
|
||||
CheckboxFilterConditionPB, CheckboxFilterPB, ChecklistFilterConditionPB, ChecklistFilterPB,
|
||||
DatabaseViewSettingPB, DateFilterConditionPB, DateFilterPB, DeleteFilterPayloadPB, FieldType,
|
||||
FilterPB, NumberFilterConditionPB, NumberFilterPB, SelectOptionConditionPB, SelectOptionFilterPB,
|
||||
TextFilterConditionPB, TextFilterPB, UpdateFilterParams, UpdateFilterPayloadPB,
|
||||
DatabaseViewSettingPB, FieldType, FilterPB, FilterType, TextFilterConditionPB, TextFilterPB,
|
||||
};
|
||||
use flowy_database2::services::database_view::DatabaseViewChanged;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
@ -37,12 +35,10 @@ pub enum FilterScript {
|
||||
option_id: String,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
InsertFilter {
|
||||
payload: UpdateFilterPayloadPB,
|
||||
},
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB,
|
||||
content: String,
|
||||
CreateDataFilter {
|
||||
parent_filter_id: Option<String>,
|
||||
field_type: FieldType,
|
||||
data: BoxAny,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
UpdateTextFilter {
|
||||
@ -51,50 +47,36 @@ pub enum FilterScript {
|
||||
content: String,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
CreateNumberFilter {
|
||||
condition: NumberFilterConditionPB,
|
||||
content: String,
|
||||
CreateAndFilter {
|
||||
parent_filter_id: Option<String>,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
CreateCheckboxFilter {
|
||||
condition: CheckboxFilterConditionPB,
|
||||
CreateOrFilter {
|
||||
parent_filter_id: Option<String>,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
CreateDateFilter {
|
||||
condition: DateFilterConditionPB,
|
||||
start: Option<i64>,
|
||||
end: Option<i64>,
|
||||
timestamp: Option<i64>,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
CreateMultiSelectFilter {
|
||||
condition: SelectOptionConditionPB,
|
||||
option_ids: Vec<String>,
|
||||
},
|
||||
CreateSingleSelectFilter {
|
||||
condition: SelectOptionConditionPB,
|
||||
option_ids: Vec<String>,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
CreateChecklistFilter {
|
||||
condition: ChecklistFilterConditionPB,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
AssertFilterCount {
|
||||
count: i32,
|
||||
},
|
||||
DeleteFilter {
|
||||
filter_context: FilterContext,
|
||||
filter_id: String,
|
||||
field_id: String,
|
||||
changed: Option<FilterRowChanged>,
|
||||
},
|
||||
AssertFilterContent {
|
||||
filter_id: String,
|
||||
condition: i64,
|
||||
content: String,
|
||||
// CreateSimpleAdvancedFilter,
|
||||
// CreateComplexAdvancedFilter,
|
||||
AssertFilterCount {
|
||||
count: usize,
|
||||
},
|
||||
AssertNumberOfVisibleRows {
|
||||
expected: usize,
|
||||
},
|
||||
AssertFilters {
|
||||
/// 1. assert that the filter type is correct
|
||||
/// 2. if the filter is data, assert that the field_type, condition and content are correct
|
||||
/// (no field_id)
|
||||
/// 3. if the filter is and/or, assert that each child is correct as well.
|
||||
expected: Vec<FilterPB>,
|
||||
},
|
||||
// AssertSimpleAdvancedFilter,
|
||||
// AssertComplexAdvancedFilterResult,
|
||||
#[allow(dead_code)]
|
||||
AssertGridSetting {
|
||||
expected_setting: DatabaseViewSettingPB,
|
||||
@ -118,14 +100,54 @@ impl DatabaseFilterTest {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view_id(&self) -> String {
|
||||
self.view_id.clone()
|
||||
}
|
||||
|
||||
pub async fn get_all_filters(&self) -> Vec<FilterPB> {
|
||||
self.editor.get_all_filters(&self.view_id).await.items
|
||||
}
|
||||
|
||||
pub async fn get_filter(
|
||||
&self,
|
||||
filter_type: FilterType,
|
||||
field_type: Option<FieldType>,
|
||||
) -> Option<FilterPB> {
|
||||
let filters = self.inner.editor.get_all_filters(&self.view_id).await;
|
||||
|
||||
for filter in filters.items.iter() {
|
||||
let result = Self::find_filter(filter, filter_type, field_type);
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn find_filter(
|
||||
filter: &FilterPB,
|
||||
filter_type: FilterType,
|
||||
field_type: Option<FieldType>,
|
||||
) -> Option<FilterPB> {
|
||||
match &filter.filter_type {
|
||||
FilterType::And | FilterType::Or if filter.filter_type == filter_type => Some(filter.clone()),
|
||||
FilterType::And | FilterType::Or => {
|
||||
for child_filter in filter.children.iter() {
|
||||
if let Some(result) = Self::find_filter(child_filter, filter_type, field_type) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
FilterType::Data
|
||||
if filter.filter_type == filter_type
|
||||
&& field_type.map_or(false, |field_type| {
|
||||
field_type == filter.data.clone().unwrap().field_type
|
||||
}) =>
|
||||
{
|
||||
Some(filter.clone())
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_scripts(&mut self, scripts: Vec<FilterScript>) {
|
||||
for script in scripts {
|
||||
self.run_script(script).await;
|
||||
@ -139,13 +161,7 @@ impl DatabaseFilterTest {
|
||||
text,
|
||||
changed,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.subscribe_view_changed().await;
|
||||
self.assert_future_changed(changed).await;
|
||||
self.update_text_cell(row_id, &text).await.unwrap();
|
||||
},
|
||||
@ -163,46 +179,35 @@ impl DatabaseFilterTest {
|
||||
option_id,
|
||||
changed,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.subscribe_view_changed().await;
|
||||
self.assert_future_changed(changed).await;
|
||||
self
|
||||
.update_single_select_cell(row_id, &option_id)
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
FilterScript::InsertFilter { payload } => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.insert_filter(payload).await;
|
||||
},
|
||||
FilterScript::CreateTextFilter {
|
||||
condition,
|
||||
content,
|
||||
FilterScript::CreateDataFilter {
|
||||
parent_filter_id,
|
||||
field_type,
|
||||
data,
|
||||
changed,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.subscribe_view_changed().await;
|
||||
self.assert_future_changed(changed).await;
|
||||
let field = self.get_first_field(FieldType::RichText);
|
||||
let text_filter = TextFilterPB { condition, content };
|
||||
let payload = UpdateFilterPayloadPB::new(&self.view_id(), &field, text_filter);
|
||||
self.insert_filter(payload).await;
|
||||
let field = self.get_first_field(field_type);
|
||||
let params = FilterChangeset::Insert {
|
||||
parent_filter_id,
|
||||
data: FilterInner::Data {
|
||||
field_id: field.id,
|
||||
field_type,
|
||||
condition_and_content: data,
|
||||
},
|
||||
};
|
||||
self
|
||||
.editor
|
||||
.modify_view_filters(&self.view_id, params)
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
FilterScript::UpdateTextFilter {
|
||||
filter,
|
||||
@ -210,172 +215,76 @@ impl DatabaseFilterTest {
|
||||
content,
|
||||
changed,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.assert_future_changed(changed).await;
|
||||
let params = UpdateFilterParams {
|
||||
view_id: self.view_id(),
|
||||
field_id: filter.field_id,
|
||||
filter_id: Some(filter.id),
|
||||
field_type: filter.field_type,
|
||||
condition: condition as i64,
|
||||
content,
|
||||
};
|
||||
self.editor.create_or_update_filter(params).await.unwrap();
|
||||
},
|
||||
FilterScript::CreateNumberFilter {
|
||||
condition,
|
||||
content,
|
||||
changed,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.assert_future_changed(changed).await;
|
||||
let field = self.get_first_field(FieldType::Number);
|
||||
let number_filter = NumberFilterPB { condition, content };
|
||||
let payload = UpdateFilterPayloadPB::new(&self.view_id(), &field, number_filter);
|
||||
self.insert_filter(payload).await;
|
||||
},
|
||||
FilterScript::CreateCheckboxFilter { condition, changed } => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.assert_future_changed(changed).await;
|
||||
let field = self.get_first_field(FieldType::Checkbox);
|
||||
let checkbox_filter = CheckboxFilterPB { condition };
|
||||
let payload = UpdateFilterPayloadPB::new(&self.view_id(), &field, checkbox_filter);
|
||||
self.insert_filter(payload).await;
|
||||
},
|
||||
FilterScript::CreateDateFilter {
|
||||
condition,
|
||||
start,
|
||||
end,
|
||||
timestamp,
|
||||
changed,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.assert_future_changed(changed).await;
|
||||
let field = self.get_first_field(FieldType::DateTime);
|
||||
let date_filter = DateFilterPB {
|
||||
condition,
|
||||
start,
|
||||
end,
|
||||
timestamp,
|
||||
};
|
||||
self.subscribe_view_changed().await;
|
||||
|
||||
let payload = UpdateFilterPayloadPB::new(&self.view_id(), &field, date_filter);
|
||||
self.insert_filter(payload).await;
|
||||
},
|
||||
FilterScript::CreateMultiSelectFilter {
|
||||
condition,
|
||||
option_ids,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
let field = self.get_first_field(FieldType::MultiSelect);
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition,
|
||||
option_ids,
|
||||
self.assert_future_changed(changed).await;
|
||||
let current_filter = filter.data.unwrap();
|
||||
let params = FilterChangeset::UpdateData {
|
||||
filter_id: filter.id,
|
||||
data: FilterInner::Data {
|
||||
field_id: current_filter.field_id,
|
||||
field_type: current_filter.field_type,
|
||||
condition_and_content: BoxAny::new(TextFilterPB { condition, content }),
|
||||
},
|
||||
};
|
||||
let payload = UpdateFilterPayloadPB::new(&self.view_id(), &field, filter);
|
||||
self.insert_filter(payload).await;
|
||||
self
|
||||
.editor
|
||||
.modify_view_filters(&self.view_id, params)
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
FilterScript::CreateSingleSelectFilter {
|
||||
condition,
|
||||
option_ids,
|
||||
FilterScript::CreateAndFilter {
|
||||
parent_filter_id,
|
||||
changed,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.subscribe_view_changed().await;
|
||||
self.assert_future_changed(changed).await;
|
||||
let field = self.get_first_field(FieldType::SingleSelect);
|
||||
let filter = SelectOptionFilterPB {
|
||||
condition,
|
||||
option_ids,
|
||||
let params = FilterChangeset::Insert {
|
||||
parent_filter_id,
|
||||
data: FilterInner::And { children: vec![] },
|
||||
};
|
||||
let payload = UpdateFilterPayloadPB::new(&self.view_id(), &field, filter);
|
||||
self.insert_filter(payload).await;
|
||||
self
|
||||
.editor
|
||||
.modify_view_filters(&self.view_id, params)
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
FilterScript::CreateChecklistFilter { condition, changed } => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
FilterScript::CreateOrFilter {
|
||||
parent_filter_id,
|
||||
changed,
|
||||
} => {
|
||||
self.subscribe_view_changed().await;
|
||||
self.assert_future_changed(changed).await;
|
||||
let field = self.get_first_field(FieldType::Checklist);
|
||||
let filter = ChecklistFilterPB { condition };
|
||||
let payload = UpdateFilterPayloadPB::new(&self.view_id(), &field, filter);
|
||||
self.insert_filter(payload).await;
|
||||
let params = FilterChangeset::Insert {
|
||||
parent_filter_id,
|
||||
data: FilterInner::Or { children: vec![] },
|
||||
};
|
||||
self
|
||||
.editor
|
||||
.modify_view_filters(&self.view_id, params)
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
FilterScript::AssertFilterCount { count } => {
|
||||
let filters = self.editor.get_all_filters(&self.view_id).await.items;
|
||||
assert_eq!(count as usize, filters.len());
|
||||
},
|
||||
FilterScript::AssertFilterContent {
|
||||
filter_id,
|
||||
condition,
|
||||
content,
|
||||
} => {
|
||||
let filter = self
|
||||
.editor
|
||||
.get_filter(&self.view_id, &filter_id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(&filter.content, &content);
|
||||
assert_eq!(filter.condition, condition);
|
||||
assert_eq!(count, filters.len());
|
||||
},
|
||||
FilterScript::DeleteFilter {
|
||||
filter_context,
|
||||
filter_id,
|
||||
field_id,
|
||||
changed,
|
||||
} => {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id())
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
self.subscribe_view_changed().await;
|
||||
self.assert_future_changed(changed).await;
|
||||
let params = DeleteFilterPayloadPB {
|
||||
filter_id: filter_context.filter_id,
|
||||
view_id: self.view_id(),
|
||||
field_id: filter_context.field_id,
|
||||
field_type: filter_context.field_type,
|
||||
let params = FilterChangeset::Delete {
|
||||
filter_id,
|
||||
field_id,
|
||||
};
|
||||
self.editor.delete_filter(params).await.unwrap();
|
||||
self
|
||||
.editor
|
||||
.modify_view_filters(&self.view_id, params)
|
||||
.await
|
||||
.unwrap();
|
||||
},
|
||||
FilterScript::AssertGridSetting { expected_setting } => {
|
||||
let setting = self
|
||||
@ -385,6 +294,12 @@ impl DatabaseFilterTest {
|
||||
.unwrap();
|
||||
assert_eq!(expected_setting, setting);
|
||||
},
|
||||
FilterScript::AssertFilters { expected } => {
|
||||
let actual = self.get_all_filters().await;
|
||||
for (actual_filter, expected_filter) in actual.iter().zip(expected.iter()) {
|
||||
Self::assert_filter(actual_filter, expected_filter);
|
||||
}
|
||||
},
|
||||
FilterScript::AssertNumberOfVisibleRows { expected } => {
|
||||
let grid = self.editor.get_database_data(&self.view_id).await.unwrap();
|
||||
assert_eq!(grid.rows.len(), expected);
|
||||
@ -395,6 +310,16 @@ impl DatabaseFilterTest {
|
||||
}
|
||||
}
|
||||
|
||||
async fn subscribe_view_changed(&mut self) {
|
||||
self.recv = Some(
|
||||
self
|
||||
.editor
|
||||
.subscribe_view_changed(&self.view_id)
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
async fn assert_future_changed(&mut self, change: Option<FilterRowChanged>) {
|
||||
if change.is_none() {
|
||||
return;
|
||||
@ -424,9 +349,24 @@ impl DatabaseFilterTest {
|
||||
});
|
||||
}
|
||||
|
||||
async fn insert_filter(&self, payload: UpdateFilterPayloadPB) {
|
||||
let params: UpdateFilterParams = payload.try_into().unwrap();
|
||||
self.editor.create_or_update_filter(params).await.unwrap();
|
||||
fn assert_filter(actual: &FilterPB, expected: &FilterPB) {
|
||||
assert_eq!(actual.filter_type, expected.filter_type);
|
||||
assert_eq!(actual.children.is_empty(), expected.children.is_empty());
|
||||
assert_eq!(actual.data.is_some(), expected.data.is_some());
|
||||
|
||||
match actual.filter_type {
|
||||
FilterType::Data => {
|
||||
let actual_data = actual.data.clone().unwrap();
|
||||
let expected_data = expected.data.clone().unwrap();
|
||||
assert_eq!(actual_data.field_type, expected_data.field_type);
|
||||
assert_eq!(actual_data.data, expected_data.data);
|
||||
},
|
||||
FilterType::And | FilterType::Or => {
|
||||
for (actual_child, expected_child) in actual.children.iter().zip(expected.children.iter()) {
|
||||
Self::assert_filter(actual_child, expected_child);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use flowy_database2::entities::{FieldType, SelectOptionConditionPB};
|
||||
use flowy_database2::entities::{FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
|
||||
use crate::database::filter_test::script::FilterScript::*;
|
||||
use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged};
|
||||
@ -7,9 +8,14 @@ use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged}
|
||||
async fn grid_filter_multi_select_is_empty_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateMultiSelectFilter {
|
||||
condition: SelectOptionConditionPB::OptionIsEmpty,
|
||||
option_ids: vec![],
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::MultiSelect,
|
||||
data: BoxAny::new(SelectOptionFilterPB {
|
||||
condition: SelectOptionConditionPB::OptionIsEmpty,
|
||||
option_ids: vec![],
|
||||
}),
|
||||
changed: None,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 2 },
|
||||
];
|
||||
@ -20,9 +26,14 @@ async fn grid_filter_multi_select_is_empty_test() {
|
||||
async fn grid_filter_multi_select_is_not_empty_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateMultiSelectFilter {
|
||||
condition: SelectOptionConditionPB::OptionIsNotEmpty,
|
||||
option_ids: vec![],
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::MultiSelect,
|
||||
data: BoxAny::new(SelectOptionFilterPB {
|
||||
condition: SelectOptionConditionPB::OptionIsNotEmpty,
|
||||
option_ids: vec![],
|
||||
}),
|
||||
changed: None,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 5 },
|
||||
];
|
||||
@ -35,9 +46,14 @@ async fn grid_filter_multi_select_is_test() {
|
||||
let field = test.get_first_field(FieldType::MultiSelect);
|
||||
let mut options = test.get_multi_select_type_option(&field.id);
|
||||
let scripts = vec![
|
||||
CreateMultiSelectFilter {
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![options.remove(0).id, options.remove(0).id],
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::MultiSelect,
|
||||
data: BoxAny::new(SelectOptionFilterPB {
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![options.remove(0).id, options.remove(0).id],
|
||||
}),
|
||||
changed: None,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 5 },
|
||||
];
|
||||
@ -50,9 +66,14 @@ async fn grid_filter_multi_select_is_test2() {
|
||||
let field = test.get_first_field(FieldType::MultiSelect);
|
||||
let mut options = test.get_multi_select_type_option(&field.id);
|
||||
let scripts = vec![
|
||||
CreateMultiSelectFilter {
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![options.remove(1).id],
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::MultiSelect,
|
||||
data: BoxAny::new(SelectOptionFilterPB {
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![options.remove(1).id],
|
||||
}),
|
||||
changed: None,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 4 },
|
||||
];
|
||||
@ -65,9 +86,13 @@ async fn grid_filter_single_select_is_empty_test() {
|
||||
let expected = 3;
|
||||
let row_count = test.row_details.len();
|
||||
let scripts = vec![
|
||||
CreateSingleSelectFilter {
|
||||
condition: SelectOptionConditionPB::OptionIsEmpty,
|
||||
option_ids: vec![],
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::SingleSelect,
|
||||
data: BoxAny::new(SelectOptionFilterPB {
|
||||
condition: SelectOptionConditionPB::OptionIsEmpty,
|
||||
option_ids: vec![],
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -86,9 +111,13 @@ async fn grid_filter_single_select_is_test() {
|
||||
let expected = 2;
|
||||
let row_count = test.row_details.len();
|
||||
let scripts = vec![
|
||||
CreateSingleSelectFilter {
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![options.remove(0).id],
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::SingleSelect,
|
||||
data: BoxAny::new(SelectOptionFilterPB {
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![options.remove(0).id],
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - expected,
|
||||
@ -109,9 +138,13 @@ async fn grid_filter_single_select_is_test2() {
|
||||
let row_count = test.row_details.len();
|
||||
|
||||
let scripts = vec![
|
||||
CreateSingleSelectFilter {
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![option.id.clone()],
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::SingleSelect,
|
||||
data: BoxAny::new(SelectOptionFilterPB {
|
||||
condition: SelectOptionConditionPB::OptionIs,
|
||||
option_ids: vec![option.id.clone()],
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: row_count - 2,
|
||||
|
@ -1,7 +1,5 @@
|
||||
use flowy_database2::entities::{
|
||||
FieldType, TextFilterConditionPB, TextFilterPB, UpdateFilterPayloadPB,
|
||||
};
|
||||
use flowy_database2::services::filter::FilterContext;
|
||||
use flowy_database2::entities::{FieldType, TextFilterConditionPB, TextFilterPB};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
|
||||
use crate::database::filter_test::script::FilterScript::*;
|
||||
use crate::database::filter_test::script::*;
|
||||
@ -10,9 +8,13 @@ use crate::database::filter_test::script::*;
|
||||
async fn grid_filter_text_is_empty_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB::TextIsEmpty,
|
||||
content: "".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::TextIsEmpty,
|
||||
content: "".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 5,
|
||||
@ -28,9 +30,13 @@ async fn grid_filter_text_is_not_empty_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
// Only one row's text of the initial rows is ""
|
||||
let scripts = vec![
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB::TextIsNotEmpty,
|
||||
content: "".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::TextIsNotEmpty,
|
||||
content: "".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 1,
|
||||
@ -44,7 +50,8 @@ async fn grid_filter_text_is_not_empty_test() {
|
||||
test
|
||||
.run_scripts(vec![
|
||||
DeleteFilter {
|
||||
filter_context: FilterContext::from(&filter),
|
||||
filter_id: filter.id,
|
||||
field_id: filter.data.unwrap().field_id,
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 1,
|
||||
hiding_num_of_rows: 0,
|
||||
@ -59,9 +66,13 @@ async fn grid_filter_text_is_not_empty_test() {
|
||||
async fn grid_filter_is_text_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
// Only one row's text of the initial rows is "A"
|
||||
let scripts = vec![CreateTextFilter {
|
||||
condition: TextFilterConditionPB::Is,
|
||||
content: "A".to_string(),
|
||||
let scripts = vec![CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::Is,
|
||||
content: "A".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 5,
|
||||
@ -73,9 +84,13 @@ async fn grid_filter_is_text_test() {
|
||||
#[tokio::test]
|
||||
async fn grid_filter_contain_text_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let scripts = vec![CreateTextFilter {
|
||||
condition: TextFilterConditionPB::Contains,
|
||||
content: "A".to_string(),
|
||||
let scripts = vec![CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::Contains,
|
||||
content: "A".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 2,
|
||||
@ -90,9 +105,13 @@ async fn grid_filter_contain_text_test2() {
|
||||
let row_detail = test.row_details.clone();
|
||||
|
||||
let scripts = vec![
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB::Contains,
|
||||
content: "A".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::Contains,
|
||||
content: "A".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 2,
|
||||
@ -114,9 +133,13 @@ async fn grid_filter_contain_text_test2() {
|
||||
async fn grid_filter_does_not_contain_text_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
// None of the initial rows contains the text "AB"
|
||||
let scripts = vec![CreateTextFilter {
|
||||
condition: TextFilterConditionPB::DoesNotContain,
|
||||
content: "AB".to_string(),
|
||||
let scripts = vec![CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::DoesNotContain,
|
||||
content: "AB".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 0,
|
||||
@ -128,9 +151,13 @@ async fn grid_filter_does_not_contain_text_test() {
|
||||
#[tokio::test]
|
||||
async fn grid_filter_start_with_text_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let scripts = vec![CreateTextFilter {
|
||||
condition: TextFilterConditionPB::StartsWith,
|
||||
content: "A".to_string(),
|
||||
let scripts = vec![CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::StartsWith,
|
||||
content: "A".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 3,
|
||||
@ -143,9 +170,13 @@ async fn grid_filter_start_with_text_test() {
|
||||
async fn grid_filter_ends_with_text_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB::EndsWith,
|
||||
content: "A".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::EndsWith,
|
||||
content: "A".to_string(),
|
||||
}),
|
||||
changed: None,
|
||||
},
|
||||
AssertNumberOfVisibleRows { expected: 2 },
|
||||
@ -157,9 +188,13 @@ async fn grid_filter_ends_with_text_test() {
|
||||
async fn grid_update_text_filter_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB::EndsWith,
|
||||
content: "A".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::EndsWith,
|
||||
content: "A".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 4,
|
||||
@ -190,14 +225,16 @@ async fn grid_update_text_filter_test() {
|
||||
#[tokio::test]
|
||||
async fn grid_filter_delete_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let field = test.get_first_field(FieldType::RichText).clone();
|
||||
let text_filter = TextFilterPB {
|
||||
condition: TextFilterConditionPB::TextIsEmpty,
|
||||
content: "".to_string(),
|
||||
};
|
||||
let payload = UpdateFilterPayloadPB::new(&test.view_id(), &field, text_filter);
|
||||
let scripts = vec![
|
||||
InsertFilter { payload },
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
changed: None,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::TextIsEmpty,
|
||||
content: "".to_string(),
|
||||
}),
|
||||
},
|
||||
AssertFilterCount { count: 1 },
|
||||
AssertNumberOfVisibleRows { expected: 1 },
|
||||
];
|
||||
@ -207,7 +244,8 @@ async fn grid_filter_delete_test() {
|
||||
test
|
||||
.run_scripts(vec![
|
||||
DeleteFilter {
|
||||
filter_context: FilterContext::from(&filter),
|
||||
filter_id: filter.id,
|
||||
field_id: filter.data.unwrap().field_id,
|
||||
changed: None,
|
||||
},
|
||||
AssertFilterCount { count: 0 },
|
||||
@ -221,9 +259,13 @@ async fn grid_filter_update_empty_text_cell_test() {
|
||||
let mut test = DatabaseFilterTest::new().await;
|
||||
let row_details = test.row_details.clone();
|
||||
let scripts = vec![
|
||||
CreateTextFilter {
|
||||
condition: TextFilterConditionPB::TextIsEmpty,
|
||||
content: "".to_string(),
|
||||
CreateDataFilter {
|
||||
parent_filter_id: None,
|
||||
field_type: FieldType::RichText,
|
||||
data: BoxAny::new(TextFilterPB {
|
||||
condition: TextFilterConditionPB::TextIsEmpty,
|
||||
content: "".to_string(),
|
||||
}),
|
||||
changed: Some(FilterRowChanged {
|
||||
showing_num_of_rows: 0,
|
||||
hiding_num_of_rows: 5,
|
||||
|
Reference in New Issue
Block a user