mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #1454 from AppFlowy-IO/feat/select_option_filter_test
Feat/select option filter test
This commit is contained in:
commit
5d20e16dc5
@ -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 {
|
||||
|
@ -36,6 +36,7 @@ impl std::default::Default for TextFilterCondition {
|
||||
TextFilterCondition::Is
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<u8> for TextFilterCondition {
|
||||
type Error = ErrorCode;
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
|
@ -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>(
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
@ -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();
|
||||
|
@ -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,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user