mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: add date filter tests
This commit is contained in:
parent
0e137f12f5
commit
c80fa5da78
@ -1,5 +1,3 @@
|
||||
use crate::entities::parser::NotEmptyStr;
|
||||
use crate::entities::FieldType;
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::ErrorCode;
|
||||
use grid_rev_model::FilterRevision;
|
||||
@ -17,68 +15,25 @@ pub struct DateFilterPB {
|
||||
|
||||
#[pb(index = 3, one_of)]
|
||||
pub end: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf, Default, Clone, Debug)]
|
||||
pub struct CreateGridDateFilterPayload {
|
||||
#[pb(index = 1)]
|
||||
pub field_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub field_type: FieldType,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub condition: DateFilterCondition,
|
||||
|
||||
#[pb(index = 4, one_of)]
|
||||
pub timestamp: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Default, Clone, Debug)]
|
||||
pub struct DateFilterContent {
|
||||
pub start: Option<i64>,
|
||||
|
||||
#[pb(index = 5, one_of)]
|
||||
pub end: Option<i64>,
|
||||
pub timestamp: Option<i64>,
|
||||
}
|
||||
|
||||
pub struct CreateGridDateFilterParams {
|
||||
pub field_id: String,
|
||||
|
||||
pub field_type: FieldType,
|
||||
|
||||
pub condition: DateFilterCondition,
|
||||
|
||||
pub start: Option<i64>,
|
||||
|
||||
pub end: Option<i64>,
|
||||
}
|
||||
|
||||
impl TryInto<CreateGridDateFilterParams> for CreateGridDateFilterPayload {
|
||||
type Error = ErrorCode;
|
||||
|
||||
fn try_into(self) -> Result<CreateGridDateFilterParams, Self::Error> {
|
||||
let field_id = NotEmptyStr::parse(self.field_id)
|
||||
.map_err(|_| ErrorCode::FieldIdIsEmpty)?
|
||||
.0;
|
||||
Ok(CreateGridDateFilterParams {
|
||||
field_id,
|
||||
condition: self.condition,
|
||||
start: self.start,
|
||||
field_type: self.field_type,
|
||||
end: self.end,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default)]
|
||||
struct DateRange {
|
||||
start: Option<i64>,
|
||||
end: Option<i64>,
|
||||
}
|
||||
|
||||
impl ToString for DateRange {
|
||||
impl ToString for DateFilterContent {
|
||||
fn to_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|_| "".to_string())
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for DateRange {
|
||||
impl FromStr for DateFilterContent {
|
||||
type Err = serde_json::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
@ -96,6 +51,7 @@ pub enum DateFilterCondition {
|
||||
DateOnOrAfter = 4,
|
||||
DateWithIn = 5,
|
||||
DateIsEmpty = 6,
|
||||
DateIsNotEmpty = 7,
|
||||
}
|
||||
|
||||
impl std::convert::From<DateFilterCondition> for u32 {
|
||||
@ -133,9 +89,10 @@ impl std::convert::From<Arc<FilterRevision>> for DateFilterPB {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Ok(range) = DateRange::from_str(&rev.content) {
|
||||
filter.start = range.start;
|
||||
filter.end = range.end;
|
||||
if let Ok(content) = DateFilterContent::from_str(&rev.content) {
|
||||
filter.start = content.start;
|
||||
filter.end = content.end;
|
||||
filter.timestamp = content.timestamp;
|
||||
};
|
||||
|
||||
filter
|
||||
|
@ -1,30 +1,60 @@
|
||||
use crate::entities::{DateFilterCondition, DateFilterPB};
|
||||
use crate::services::cell::{AnyCellData, CellData, CellFilterOperation};
|
||||
use crate::services::field::{DateTimestamp, DateTypeOptionPB};
|
||||
use chrono::NaiveDateTime;
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
impl DateFilterPB {
|
||||
pub fn is_visible<T: Into<i64>>(&self, cell_timestamp: T) -> bool {
|
||||
if self.start.is_none() {
|
||||
return false;
|
||||
}
|
||||
let cell_timestamp = cell_timestamp.into();
|
||||
let start_timestamp = *self.start.as_ref().unwrap();
|
||||
// We assume that the cell_timestamp doesn't contain hours, just day.
|
||||
match self.condition {
|
||||
DateFilterCondition::DateIs => cell_timestamp == start_timestamp,
|
||||
DateFilterCondition::DateBefore => cell_timestamp < start_timestamp,
|
||||
DateFilterCondition::DateAfter => cell_timestamp > start_timestamp,
|
||||
DateFilterCondition::DateOnOrBefore => cell_timestamp <= start_timestamp,
|
||||
DateFilterCondition::DateOnOrAfter => cell_timestamp >= start_timestamp,
|
||||
DateFilterCondition::DateWithIn => {
|
||||
if let Some(end_timestamp) = self.end.as_ref() {
|
||||
cell_timestamp >= start_timestamp && cell_timestamp <= *end_timestamp
|
||||
} else {
|
||||
false
|
||||
pub fn is_visible<T: Into<Option<i64>>>(&self, cell_timestamp: T) -> bool {
|
||||
match cell_timestamp.into() {
|
||||
None => DateFilterCondition::DateIsEmpty == self.condition,
|
||||
Some(timestamp) => {
|
||||
match self.condition {
|
||||
DateFilterCondition::DateIsNotEmpty => {
|
||||
return true;
|
||||
}
|
||||
DateFilterCondition::DateIsEmpty => {
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let cell_time = NaiveDateTime::from_timestamp(timestamp, 0);
|
||||
let cell_date = cell_time.date();
|
||||
match self.timestamp {
|
||||
None => {
|
||||
if self.start.is_none() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.end.is_none() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let start_time = NaiveDateTime::from_timestamp(*self.start.as_ref().unwrap(), 0);
|
||||
let start_date = start_time.date();
|
||||
|
||||
let end_time = NaiveDateTime::from_timestamp(*self.end.as_ref().unwrap(), 0);
|
||||
let end_date = end_time.date();
|
||||
|
||||
cell_date >= start_date && cell_date <= end_date
|
||||
}
|
||||
Some(timestamp) => {
|
||||
let expected_timestamp = NaiveDateTime::from_timestamp(timestamp, 0);
|
||||
let expected_date = expected_timestamp.date();
|
||||
|
||||
// We assume that the cell_timestamp doesn't contain hours, just day.
|
||||
match self.condition {
|
||||
DateFilterCondition::DateIs => cell_date == expected_date,
|
||||
DateFilterCondition::DateBefore => cell_date < expected_date,
|
||||
DateFilterCondition::DateAfter => cell_date > expected_date,
|
||||
DateFilterCondition::DateOnOrBefore => cell_date <= expected_date,
|
||||
DateFilterCondition::DateOnOrAfter => cell_date >= expected_date,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DateFilterCondition::DateIsEmpty => cell_timestamp == 0_i64,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,11 +79,12 @@ mod tests {
|
||||
fn date_filter_is_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateIs,
|
||||
start: Some(123),
|
||||
timestamp: Some(1668387885),
|
||||
end: None,
|
||||
start: None,
|
||||
};
|
||||
|
||||
for (val, visible) in vec![(123, true), (12, false)] {
|
||||
for (val, visible) in vec![(1668387885, true), (1647251762, false)] {
|
||||
assert_eq!(filter.is_visible(val as i64), visible);
|
||||
}
|
||||
}
|
||||
@ -61,23 +92,26 @@ mod tests {
|
||||
fn date_filter_before_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateBefore,
|
||||
start: Some(123),
|
||||
timestamp: Some(1668387885),
|
||||
start: None,
|
||||
end: None,
|
||||
};
|
||||
|
||||
for (val, visible) in vec![(123, false), (122, true)] {
|
||||
assert_eq!(filter.is_visible(val as i64), visible);
|
||||
for (val, visible, msg) in vec![(1668387884, false, "1"), (1647251762, true, "2")] {
|
||||
assert_eq!(filter.is_visible(val as i64), visible, "{}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_filter_before_or_on_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateOnOrBefore,
|
||||
start: Some(123),
|
||||
timestamp: Some(1668387885),
|
||||
start: None,
|
||||
end: None,
|
||||
};
|
||||
|
||||
for (val, visible) in vec![(123, true), (122, true)] {
|
||||
for (val, visible) in vec![(1668387884, true), (1668387885, true)] {
|
||||
assert_eq!(filter.is_visible(val as i64), visible);
|
||||
}
|
||||
}
|
||||
@ -85,24 +119,45 @@ mod tests {
|
||||
fn date_filter_after_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateAfter,
|
||||
start: Some(123),
|
||||
timestamp: Some(1668387885),
|
||||
start: None,
|
||||
end: None,
|
||||
};
|
||||
|
||||
for (val, visible) in vec![(1234, true), (122, false), (0, false)] {
|
||||
for (val, visible) in vec![(1668387888, false), (1668531885, true), (0, false)] {
|
||||
assert_eq!(filter.is_visible(val as i64), visible);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_filter_within_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateWithIn,
|
||||
start: Some(123),
|
||||
end: Some(130),
|
||||
start: Some(1668272685), // 11/13
|
||||
end: Some(1668618285), // 11/17
|
||||
timestamp: None,
|
||||
};
|
||||
|
||||
for (val, visible) in vec![(123, true), (130, true), (132, false)] {
|
||||
for (val, visible, _msg) in vec![
|
||||
(1668272685, true, "11/13"),
|
||||
(1668359085, true, "11/14"),
|
||||
(1668704685, false, "11/18"),
|
||||
] {
|
||||
assert_eq!(filter.is_visible(val as i64), visible);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_filter_is_empty_test() {
|
||||
let filter = DateFilterPB {
|
||||
condition: DateFilterCondition::DateIsEmpty,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: None,
|
||||
};
|
||||
|
||||
for (val, visible) in vec![(None, true), (Some(123), false)] {
|
||||
assert_eq!(filter.is_visible(val), visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ impl DateTypeOptionPB {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn today_desc_from_timestamp<T: AsRef<i64>>(&self, timestamp: T) -> DateCellDataPB {
|
||||
let timestamp = *timestamp.as_ref();
|
||||
fn today_desc_from_timestamp<T: Into<i64>>(&self, timestamp: T) -> DateCellDataPB {
|
||||
let timestamp = timestamp.into();
|
||||
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
|
||||
if native.timestamp() == 0 {
|
||||
return DateCellDataPB::default();
|
||||
|
@ -68,14 +68,15 @@ impl ToString for DateCellChangeset {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DateTimestamp(i64);
|
||||
impl AsRef<i64> for DateTimestamp {
|
||||
fn as_ref(&self) -> &i64 {
|
||||
&self.0
|
||||
pub struct DateTimestamp(Option<i64>);
|
||||
|
||||
impl std::convert::From<DateTimestamp> for i64 {
|
||||
fn from(timestamp: DateTimestamp) -> Self {
|
||||
timestamp.0.unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<DateTimestamp> for i64 {
|
||||
impl std::convert::From<DateTimestamp> for Option<i64> {
|
||||
fn from(timestamp: DateTimestamp) -> Self {
|
||||
timestamp.0
|
||||
}
|
||||
@ -86,7 +87,7 @@ impl FromCellString for DateTimestamp {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let num = s.parse::<i64>().unwrap_or(0);
|
||||
let num = s.parse::<i64>().ok();
|
||||
Ok(DateTimestamp(num))
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ impl<'a> GridRowTestBuilder<'a> {
|
||||
let value = serde_json::to_string(&DateCellChangeset {
|
||||
date: Some(data.to_string()),
|
||||
time: None,
|
||||
is_utc: true,
|
||||
})
|
||||
.unwrap();
|
||||
let date_field = self.field_rev_with_type(&FieldType::DateTime);
|
||||
|
@ -29,7 +29,7 @@ impl GridCellTest {
|
||||
|
||||
match script {
|
||||
CellScript::UpdateCell { changeset, is_err } => {
|
||||
let result = self.editor.update_cell(changeset).await;
|
||||
let result = self.editor.update_cell_with_changeset(changeset).await;
|
||||
if is_err {
|
||||
assert!(result.is_err())
|
||||
} else {
|
||||
|
@ -58,6 +58,7 @@ pub fn make_date_cell_string(s: &str) -> String {
|
||||
serde_json::to_string(&DateCellChangeset {
|
||||
date: Some(s.to_string()),
|
||||
time: None,
|
||||
is_utc: true,
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -3,15 +3,76 @@ use crate::grid::filter_test::script::GridFilterTest;
|
||||
use flowy_grid::entities::DateFilterCondition;
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic]
|
||||
async fn grid_filter_date_is_check_test() {
|
||||
async fn grid_filter_date_is_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateIs,
|
||||
content: "1647251762".to_string(),
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
},
|
||||
AssertNumberOfRows { expected: 3 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_date_after_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateAfter,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1647251762),
|
||||
},
|
||||
AssertNumberOfRows { expected: 2 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_date_on_or_after_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateOnOrAfter,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
},
|
||||
AssertNumberOfRows { expected: 2 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_date_on_or_before_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateOnOrBefore,
|
||||
start: None,
|
||||
end: None,
|
||||
timestamp: Some(1668359085),
|
||||
},
|
||||
AssertNumberOfRows { expected: 4 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn grid_filter_date_within_test() {
|
||||
let mut test = GridFilterTest::new().await;
|
||||
let scripts = vec![
|
||||
CreateDateFilter {
|
||||
condition: DateFilterCondition::DateWithIn,
|
||||
start: Some(1647251762),
|
||||
end: Some(1668704685),
|
||||
timestamp: None,
|
||||
},
|
||||
AssertNumberOfRows { expected: 5 },
|
||||
];
|
||||
test.run_scripts(scripts).await;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use futures::TryFutureExt;
|
||||
use flowy_grid::entities::{CreateFilterParams, CreateFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition};
|
||||
use flowy_grid::entities::{CreateFilterParams, CreateFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition, DateFilterContent};
|
||||
use flowy_grid::services::setting::GridSettingChangesetBuilder;
|
||||
use grid_rev_model::{FieldRevision, FieldTypeRevision};
|
||||
use flowy_grid::services::filter::FilterType;
|
||||
@ -27,7 +27,9 @@ pub enum FilterScript {
|
||||
},
|
||||
CreateDateFilter{
|
||||
condition: DateFilterCondition,
|
||||
content: String,
|
||||
start: Option<i64>,
|
||||
end: Option<i64>,
|
||||
timestamp: Option<i64>,
|
||||
},
|
||||
AssertFilterCount {
|
||||
count: i32,
|
||||
@ -91,10 +93,16 @@ impl GridFilterTest {
|
||||
CreateFilterPayloadPB::new(field_rev, condition, "".to_string());
|
||||
self.insert_filter(payload).await;
|
||||
}
|
||||
FilterScript::CreateDateFilter { condition, content} => {
|
||||
FilterScript::CreateDateFilter { condition, start, end, timestamp} => {
|
||||
let field_rev = self.get_field_rev(FieldType::DateTime);
|
||||
let content = DateFilterContent {
|
||||
start,
|
||||
end,
|
||||
timestamp,
|
||||
}.to_string();
|
||||
let payload =
|
||||
CreateFilterPayloadPB::new(field_rev, condition, content);
|
||||
|
||||
self.insert_filter(payload).await;
|
||||
}
|
||||
FilterScript::AssertFilterCount { count } => {
|
||||
@ -125,7 +133,6 @@ impl GridFilterTest {
|
||||
}
|
||||
|
||||
async fn insert_filter(&self, payload: CreateFilterPayloadPB) {
|
||||
|
||||
let params: CreateFilterParams = payload.try_into().unwrap();
|
||||
let _ = self.editor.create_filter(params).await.unwrap();
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ fn make_test_grid() -> BuildGridContext {
|
||||
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("1647251762"),
|
||||
FieldType::DateTime => row_builder.insert_date_cell("1668704685"),
|
||||
FieldType::SingleSelect => {
|
||||
row_builder.insert_single_select_cell(|mut options| options.remove(1))
|
||||
}
|
||||
@ -250,7 +250,7 @@ fn make_test_grid() -> BuildGridContext {
|
||||
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("1647251762"),
|
||||
FieldType::DateTime => row_builder.insert_date_cell("1668359085"),
|
||||
FieldType::SingleSelect => {
|
||||
row_builder.insert_single_select_cell(|mut options| options.remove(2))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user