Browse Source

Merge pull request #1454 from AppFlowy-IO/feat/select_option_filter_test

Feat/select option filter test
Nathan.fooo 2 years ago
parent
commit
5d20e16dc5

+ 1 - 0
frontend/rust-lib/flowy-grid/src/entities/filter_entities/select_option_filter.rs

@@ -12,6 +12,7 @@ pub struct SelectOptionFilterPB {
     #[pb(index = 2)]
     pub option_ids: Vec<String>,
 }
+
 #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)]
 #[repr(u8)]
 pub enum SelectOptionCondition {

+ 1 - 0
frontend/rust-lib/flowy-grid/src/entities/filter_entities/text_filter.rs

@@ -36,6 +36,7 @@ impl std::default::Default for TextFilterCondition {
         TextFilterCondition::Is
     }
 }
+
 impl std::convert::TryFrom<u8> for TextFilterCondition {
     type Error = ErrorCode;
 

+ 10 - 1
frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs

@@ -15,6 +15,15 @@ pub struct AnyCellData {
     pub field_type: FieldType,
 }
 
+impl AnyCellData {
+    pub fn from_field_type(field_type: &FieldType) -> AnyCellData {
+        Self {
+            data: "".to_string(),
+            field_type: field_type.clone(),
+        }
+    }
+}
+
 impl std::str::FromStr for AnyCellData {
     type Err = FlowyError;
 
@@ -68,7 +77,7 @@ impl AnyCellData {
         }
     }
 
-    pub fn json(&self) -> String {
+    pub fn to_json(&self) -> String {
         serde_json::to_string(self).unwrap_or_else(|_| "".to_owned())
     }
 

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -126,7 +126,7 @@ pub fn apply_cell_data_changeset<C: ToString, T: AsRef<FieldRevision>>(
         FieldType::URL => URLTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev),
     }?;
 
-    Ok(AnyCellData::new(s, field_type).json())
+    Ok(AnyCellData::new(s, field_type).to_json())
 }
 
 pub fn decode_any_cell_data<T: TryInto<AnyCellData, Error = FlowyError> + Debug>(

+ 20 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs

@@ -82,6 +82,26 @@ mod tests {
             false,
         );
     }
+
+    #[test]
+    fn select_option_filter_is_not_empty_test() {
+        let option_1 = SelectOptionPB::new("A");
+        let option_2 = SelectOptionPB::new("B");
+        let filter = SelectOptionFilterPB {
+            condition: SelectOptionCondition::OptionIsNotEmpty,
+            option_ids: vec![option_1.id.clone(), option_2.id.clone()],
+        };
+
+        assert_eq!(
+            filter.is_visible(&SelectedSelectOptions {
+                options: vec![option_1]
+            }),
+            true
+        );
+
+        assert_eq!(filter.is_visible(&SelectedSelectOptions { options: vec![] }), false,);
+    }
+
     #[test]
     fn select_option_filter_is_not_test() {
         let option_1 = SelectOptionPB::new("A");

+ 4 - 0
frontend/rust-lib/flowy-grid/src/services/filter/cache.rs

@@ -84,6 +84,10 @@ pub(crate) struct FilterResult {
 
 impl FilterResult {
     pub(crate) fn is_visible(&self) -> bool {
+        if self.visible_by_filter_id.is_empty() {
+            return false;
+        }
+
         for visible in self.visible_by_filter_id.values() {
             if visible == &false {
                 return false;

+ 9 - 5
frontend/rust-lib/flowy-grid/src/services/filter/controller.rs

@@ -232,10 +232,9 @@ fn filter_row(
         .or_insert_with(FilterResult::default);
 
     // Iterate each cell of the row to check its visibility
-    for (field_id, cell_rev) in row_rev.cells.iter() {
-        let field_rev = field_rev_by_field_id.get(field_id)?;
+    for (field_id, field_rev) in field_rev_by_field_id {
         let filter_type = FilterType::from(field_rev);
-
+        let cell_rev = row_rev.cells.get(field_id);
         // if the visibility of the cell_rew is changed, which means the visibility of the
         // row is changed too.
         if let Some(is_visible) = filter_cell(&filter_type, field_rev, filter_map, cell_rev) {
@@ -255,6 +254,7 @@ fn filter_row(
             }
         }
     }
+
     None
 }
 
@@ -263,9 +263,13 @@ fn filter_cell(
     filter_id: &FilterType,
     field_rev: &Arc<FieldRevision>,
     filter_map: &FilterMap,
-    cell_rev: &CellRevision,
+    cell_rev: Option<&CellRevision>,
 ) -> Option<bool> {
-    let any_cell_data = AnyCellData::try_from(cell_rev).ok()?;
+    let any_cell_data = match cell_rev {
+        None => AnyCellData::from_field_type(&filter_id.field_type),
+        Some(cell_rev) => AnyCellData::try_from(cell_rev).ok()?,
+    };
+
     let is_visible = match &filter_id.field_type {
         FieldType::RichText => filter_map.text_filter.get(filter_id).and_then(|filter| {
             Some(

+ 1 - 0
frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs

@@ -2,4 +2,5 @@ mod checkbox_filter_test;
 mod date_filter_test;
 mod number_filter_test;
 mod script;
+mod select_option_filter_test;
 mod text_filter_test;

+ 26 - 1
frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs

@@ -4,7 +4,8 @@
 #![allow(unused_imports)]
 
 use futures::TryFutureExt;
-use flowy_grid::entities::{CreateFilterParams, CreateFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition, DateFilterContent};
+use flowy_grid::entities::{CreateFilterParams, CreateFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition, DateFilterContent, SelectOptionCondition};
+use flowy_grid::services::field::SelectOptionIds;
 use flowy_grid::services::setting::GridSettingChangesetBuilder;
 use grid_rev_model::{FieldRevision, FieldTypeRevision};
 use flowy_grid::services::filter::FilterType;
@@ -31,6 +32,14 @@ pub enum FilterScript {
         end: Option<i64>,
         timestamp: Option<i64>,
     },
+    CreateMultiSelectFilter {
+        condition: SelectOptionCondition,
+        option_ids: Vec<String>,
+    },
+    CreateSingleSelectFilter {
+        condition: SelectOptionCondition,
+        option_ids: Vec<String>,
+    },
     AssertFilterCount {
         count: i32,
     },
@@ -105,6 +114,22 @@ impl GridFilterTest {
 
                 self.insert_filter(payload).await;
             }
+            FilterScript::CreateMultiSelectFilter { condition, option_ids} => {
+                let field_rev = self.get_field_rev(FieldType::MultiSelect);
+                let content =
+                    SelectOptionIds::from(option_ids).to_string();
+                let payload =
+                    CreateFilterPayloadPB::new(field_rev, condition, content);
+                self.insert_filter(payload).await;
+            }
+            FilterScript::CreateSingleSelectFilter { condition, option_ids} => {
+                let field_rev = self.get_field_rev(FieldType::SingleSelect);
+                let content =
+                    SelectOptionIds::from(option_ids).to_string();
+                let payload =
+                    CreateFilterPayloadPB::new(field_rev, condition, content);
+                self.insert_filter(payload).await;
+            }
             FilterScript::AssertFilterCount { count } => {
                 let filters = self.editor.get_all_filters().await.unwrap();
                 assert_eq!(count as usize, filters.len());

+ 84 - 0
frontend/rust-lib/flowy-grid/tests/grid/filter_test/select_option_filter_test.rs

@@ -0,0 +1,84 @@
+use crate::grid::filter_test::script::FilterScript::*;
+use crate::grid::filter_test::script::GridFilterTest;
+use flowy_grid::entities::SelectOptionCondition;
+
+#[tokio::test]
+async fn grid_filter_multi_select_is_empty_test() {
+    let mut test = GridFilterTest::new().await;
+    let scripts = vec![
+        CreateMultiSelectFilter {
+            condition: SelectOptionCondition::OptionIsEmpty,
+            option_ids: vec![],
+        },
+        AssertNumberOfRows { expected: 2 },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_multi_select_is_not_empty_test() {
+    let mut test = GridFilterTest::new().await;
+    let scripts = vec![
+        CreateMultiSelectFilter {
+            condition: SelectOptionCondition::OptionIsNotEmpty,
+            option_ids: vec![],
+        },
+        AssertNumberOfRows { expected: 3 },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_multi_select_is_test() {
+    let mut test = GridFilterTest::new().await;
+    let mut options = test.get_multi_select_type_option();
+    let scripts = vec![
+        CreateMultiSelectFilter {
+            condition: SelectOptionCondition::OptionIs,
+            option_ids: vec![options.remove(0).id, options.remove(0).id],
+        },
+        AssertNumberOfRows { expected: 2 },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_multi_select_is_test2() {
+    let mut test = GridFilterTest::new().await;
+    let mut options = test.get_multi_select_type_option();
+    let scripts = vec![
+        CreateMultiSelectFilter {
+            condition: SelectOptionCondition::OptionIs,
+            option_ids: vec![options.remove(1).id],
+        },
+        AssertNumberOfRows { expected: 1 },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_single_select_is_empty_test() {
+    let mut test = GridFilterTest::new().await;
+    let scripts = vec![
+        CreateSingleSelectFilter {
+            condition: SelectOptionCondition::OptionIsEmpty,
+            option_ids: vec![],
+        },
+        AssertNumberOfRows { expected: 2 },
+    ];
+    test.run_scripts(scripts).await;
+}
+
+#[tokio::test]
+async fn grid_filter_single_select_is_test() {
+    let mut test = GridFilterTest::new().await;
+    let mut options = test.get_single_select_type_option();
+    let scripts = vec![
+        CreateSingleSelectFilter {
+            condition: SelectOptionCondition::OptionIs,
+            option_ids: vec![options.remove(0).id],
+        },
+        AssertNumberOfRows { expected: 2 },
+    ];
+    test.run_scripts(scripts).await;
+}

+ 180 - 5
frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs

@@ -45,12 +45,17 @@ impl GridEditorTest {
     pub async fn new(layout: GridLayout) -> Self {
         let sdk = FlowySDKTest::default();
         let _ = sdk.init_user().await;
-        let build_context = make_test_grid();
-        let view_data: Bytes = build_context.into();
-
         let test = match layout {
-            GridLayout::Table => ViewTest::new_grid_view(&sdk, view_data.to_vec()).await,
-            GridLayout::Board => ViewTest::new_board_view(&sdk, view_data.to_vec()).await,
+            GridLayout::Table => {
+                let build_context = make_test_grid();
+                let view_data: Bytes = build_context.into();
+                ViewTest::new_grid_view(&sdk, view_data.to_vec()).await
+            }
+            GridLayout::Board => {
+                let build_context = make_test_board();
+                let view_data: Bytes = build_context.into();
+                ViewTest::new_board_view(&sdk, view_data.to_vec()).await
+            }
         };
 
         let editor = sdk.grid_manager.open_grid(&test.view.id).await.unwrap();
@@ -96,6 +101,23 @@ impl GridEditorTest {
             .unwrap()
     }
 
+    pub fn get_multi_select_type_option(&self) -> Vec<SelectOptionPB> {
+        let field_type = FieldType::MultiSelect;
+        let field_rev = self.get_field_rev(field_type.clone());
+        let type_option = field_rev
+            .get_type_option::<MultiSelectTypeOptionPB>(field_type.into())
+            .unwrap();
+        type_option.options
+    }
+
+    pub fn get_single_select_type_option(&self) -> Vec<SelectOptionPB> {
+        let field_type = FieldType::SingleSelect;
+        let field_rev = self.get_field_rev(field_type.clone());
+        let type_option = field_rev
+            .get_type_option::<SingleSelectTypeOptionPB>(field_type.into())
+            .unwrap();
+        type_option.options
+    }
     pub fn block_id(&self) -> &str {
         &self.block_meta_revs.last().unwrap().block_id
     }
@@ -176,6 +198,159 @@ fn make_test_grid() -> BuildGridContext {
         }
     }
 
+    // We have many assumptions base on the number of the rows, so do not change the number of the loop.
+    for i in 0..5 {
+        let block_id = grid_builder.block_id().to_owned();
+        let field_revs = grid_builder.field_revs();
+        let mut row_builder = GridRowTestBuilder::new(&block_id, field_revs);
+        match i {
+            0 => {
+                for field_type in FieldType::iter() {
+                    match field_type {
+                        FieldType::RichText => row_builder.insert_text_cell("A"),
+                        FieldType::Number => row_builder.insert_number_cell("1"),
+                        FieldType::DateTime => row_builder.insert_date_cell("1647251762"),
+                        FieldType::MultiSelect => row_builder
+                            .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]),
+                        FieldType::Checkbox => row_builder.insert_checkbox_cell("true"),
+                        _ => "".to_owned(),
+                    };
+                }
+            }
+            1 => {
+                for field_type in FieldType::iter() {
+                    match field_type {
+                        FieldType::RichText => row_builder.insert_text_cell("B"),
+                        FieldType::Number => row_builder.insert_number_cell("2"),
+                        FieldType::DateTime => row_builder.insert_date_cell("1647251762"),
+                        FieldType::MultiSelect => row_builder
+                            .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]),
+                        FieldType::Checkbox => row_builder.insert_checkbox_cell("true"),
+                        _ => "".to_owned(),
+                    };
+                }
+            }
+            2 => {
+                for field_type in FieldType::iter() {
+                    match field_type {
+                        FieldType::RichText => row_builder.insert_text_cell("C"),
+                        FieldType::Number => row_builder.insert_number_cell("3"),
+                        FieldType::DateTime => row_builder.insert_date_cell("1647251762"),
+                        FieldType::SingleSelect => {
+                            row_builder.insert_single_select_cell(|mut options| options.remove(0))
+                        }
+                        FieldType::MultiSelect => {
+                            row_builder.insert_multi_select_cell(|mut options| vec![options.remove(1)])
+                        }
+                        FieldType::Checkbox => row_builder.insert_checkbox_cell("false"),
+                        _ => "".to_owned(),
+                    };
+                }
+            }
+            3 => {
+                for field_type in FieldType::iter() {
+                    match field_type {
+                        FieldType::RichText => row_builder.insert_text_cell("DA"),
+                        FieldType::Number => row_builder.insert_number_cell("4"),
+                        FieldType::DateTime => row_builder.insert_date_cell("1668704685"),
+                        FieldType::SingleSelect => {
+                            row_builder.insert_single_select_cell(|mut options| options.remove(0))
+                        }
+                        FieldType::Checkbox => row_builder.insert_checkbox_cell("false"),
+                        _ => "".to_owned(),
+                    };
+                }
+            }
+            4 => {
+                for field_type in FieldType::iter() {
+                    match field_type {
+                        FieldType::RichText => row_builder.insert_text_cell("AE"),
+                        FieldType::Number => row_builder.insert_number_cell(""),
+                        FieldType::DateTime => row_builder.insert_date_cell("1668359085"),
+                        FieldType::SingleSelect => {
+                            row_builder.insert_single_select_cell(|mut options| options.remove(1))
+                        }
+
+                        FieldType::Checkbox => row_builder.insert_checkbox_cell("false"),
+                        _ => "".to_owned(),
+                    };
+                }
+            }
+            _ => {}
+        }
+
+        let row_rev = row_builder.build();
+        grid_builder.add_row(row_rev);
+    }
+    grid_builder.build()
+}
+
+fn make_test_board() -> BuildGridContext {
+    let mut grid_builder = GridBuilder::new();
+    // Iterate through the FieldType to create the corresponding Field.
+    for field_type in FieldType::iter() {
+        let field_type: FieldType = field_type;
+
+        // The
+        match field_type {
+            FieldType::RichText => {
+                let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default())
+                    .name("Name")
+                    .visibility(true)
+                    .primary(true)
+                    .build();
+                grid_builder.add_field(text_field);
+            }
+            FieldType::Number => {
+                // Number
+                let number = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD);
+                let number_field = FieldBuilder::new(number).name("Price").visibility(true).build();
+                grid_builder.add_field(number_field);
+            }
+            FieldType::DateTime => {
+                // Date
+                let date = DateTypeOptionBuilder::default()
+                    .date_format(DateFormat::US)
+                    .time_format(TimeFormat::TwentyFourHour);
+                let date_field = FieldBuilder::new(date).name("Time").visibility(true).build();
+                grid_builder.add_field(date_field);
+            }
+            FieldType::SingleSelect => {
+                // Single Select
+                let single_select = SingleSelectTypeOptionBuilder::default()
+                    .add_option(SelectOptionPB::new(COMPLETED))
+                    .add_option(SelectOptionPB::new(PLANNED))
+                    .add_option(SelectOptionPB::new(PAUSED));
+                let single_select_field = FieldBuilder::new(single_select).name("Status").visibility(true).build();
+                grid_builder.add_field(single_select_field);
+            }
+            FieldType::MultiSelect => {
+                // MultiSelect
+                let multi_select = MultiSelectTypeOptionBuilder::default()
+                    .add_option(SelectOptionPB::new(GOOGLE))
+                    .add_option(SelectOptionPB::new(FACEBOOK))
+                    .add_option(SelectOptionPB::new(TWITTER));
+                let multi_select_field = FieldBuilder::new(multi_select)
+                    .name("Platform")
+                    .visibility(true)
+                    .build();
+                grid_builder.add_field(multi_select_field);
+            }
+            FieldType::Checkbox => {
+                // Checkbox
+                let checkbox = CheckboxTypeOptionBuilder::default();
+                let checkbox_field = FieldBuilder::new(checkbox).name("is urgent").visibility(true).build();
+                grid_builder.add_field(checkbox_field);
+            }
+            FieldType::URL => {
+                // URL
+                let url = URLTypeOptionBuilder::default();
+                let url_field = FieldBuilder::new(url).name("link").visibility(true).build();
+                grid_builder.add_field(url_field);
+            }
+        }
+    }
+
     // We have many assumptions base on the number of the rows, so do not change the number of the loop.
     for i in 0..5 {
         let block_id = grid_builder.block_id().to_owned();

+ 1 - 1
shared-lib/grid-rev-model/src/grid_block.rs

@@ -64,7 +64,7 @@ impl RowChangeset {
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct CellRevision {
     pub data: String,
 }