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

Feat/select option filter test
This commit is contained in:
Nathan.fooo 2022-11-15 10:44:06 +08:00 committed by GitHub
commit 5d20e16dc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 338 additions and 14 deletions

View File

@ -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 {

View File

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

View File

@ -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())
}

View File

@ -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>(

View File

@ -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");

View File

@ -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;

View File

@ -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(

View File

@ -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;

View File

@ -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());

View File

@ -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;
}

View File

@ -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();

View File

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