chore: unit test for cell content

This commit is contained in:
appflowy 2022-07-13 17:25:03 +08:00
parent f10e324b73
commit 602aab45e2
33 changed files with 338 additions and 175 deletions

View File

@ -1,9 +1,11 @@
use crate::entities::FieldType;
use crate::services::cell::{CellData, FromCellString};
use bytes::Bytes;
use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_grid_data_model::revision::CellRevision;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
/// AnyCellData is a generic CellData, you can parse the cell_data according to the field_type.
/// When the type of field is changed, it's different from the field_type of AnyCellData.
/// So it will return an empty data. You could check the CellDataOperation trait for more information.
@ -46,6 +48,15 @@ impl std::convert::TryFrom<CellRevision> for AnyCellData {
}
}
impl<T> std::convert::From<AnyCellData> for CellData<T>
where
T: FromCellString,
{
fn from(any_call_data: AnyCellData) -> Self {
CellData::from(any_call_data.data)
}
}
impl AnyCellData {
pub fn new(content: String, field_type: FieldType) -> Self {
AnyCellData {
@ -102,6 +113,11 @@ impl AnyCellData {
#[derive(Default)]
pub struct CellBytes(pub Bytes);
pub trait CellBytesParser {
type Object;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object>;
}
impl CellBytes {
pub fn new<T: AsRef<[u8]>>(data: T) -> Self {
let bytes = Bytes::from(data.as_ref().to_vec());
@ -116,12 +132,19 @@ impl CellBytes {
Ok(Self(bytes))
}
pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult<T>
pub fn with_parser<P>(&self, parser: P) -> FlowyResult<P::Object>
where
<T as TryFrom<&'a [u8]>>::Error: std::fmt::Debug,
P: CellBytesParser,
{
T::try_from(self.0.as_ref()).map_err(internal_error)
parser.parse(&self.0)
}
// pub fn parse<'a, T: TryFrom<&'a [u8]>>(&'a self) -> FlowyResult<T>
// where
// <T as TryFrom<&'a [u8]>>::Error: std::fmt::Debug,
// {
// T::try_from(self.0.as_ref()).map_err(internal_error)
// }
}
impl ToString for CellBytes {

View File

@ -163,9 +163,9 @@ where
}
}
impl std::convert::From<String> for CellData<String> {
fn from(s: String) -> Self {
CellData(Some(s))
impl<T> std::convert::From<T> for CellData<T> {
fn from(val: T) -> Self {
CellData(Some(val))
}
}

View File

@ -7,6 +7,7 @@ use flowy_derive::ProtoBuf;
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Default)]
pub struct CheckboxTypeOptionBuilder(CheckboxTypeOption);
@ -69,7 +70,7 @@ impl CellDataOperation<CheckboxCellData, String> for CheckboxTypeOption {
_cell_rev: Option<CellRevision>,
) -> Result<String, FlowyError> {
let changeset = changeset.try_into_inner()?;
let cell_data = CheckboxCellData::from_str(&changeset);
let cell_data = CheckboxCellData::from_str(&changeset)?;
Ok(cell_data.to_string())
}
}

View File

@ -1,5 +1,7 @@
use crate::services::cell::{AnyCellData, FromCellString};
use crate::services::cell::{CellBytesParser, FromCellString};
use bytes::Bytes;
use flowy_error::{FlowyError, FlowyResult};
use std::str::FromStr;
pub const YES: &str = "Yes";
pub const NO: &str = "No";
@ -7,7 +9,21 @@ pub const NO: &str = "No";
pub struct CheckboxCellData(pub String);
impl CheckboxCellData {
pub fn from_str(s: &str) -> Self {
pub fn is_check(&self) -> bool {
self.0 == YES
}
}
impl AsRef<[u8]> for CheckboxCellData {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl FromStr for CheckboxCellData {
type Err = FlowyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let lower_case_str: &str = &s.to_lowercase();
let val = match lower_case_str {
"1" => Some(true),
@ -20,29 +36,11 @@ impl CheckboxCellData {
};
match val {
Some(true) => Self(YES.to_string()),
Some(false) => Self(NO.to_string()),
None => Self("".to_string()),
Some(true) => Ok(Self(YES.to_string())),
Some(false) => Ok(Self(NO.to_string())),
None => Ok(Self("".to_string())),
}
}
pub fn is_check(&self) -> bool {
&self.0 == YES
}
}
impl AsRef<[u8]> for CheckboxCellData {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl std::convert::TryFrom<AnyCellData> for CheckboxCellData {
type Error = FlowyError;
fn try_from(value: AnyCellData) -> Result<Self, Self::Error> {
Ok(Self::from_str(&value.data))
}
}
impl FromCellString for CheckboxCellData {
@ -50,7 +48,7 @@ impl FromCellString for CheckboxCellData {
where
Self: Sized,
{
Ok(Self::from_str(s))
Self::from_str(s)
}
}
@ -59,3 +57,13 @@ impl ToString for CheckboxCellData {
self.0.clone()
}
}
pub struct CheckboxCellDataParser;
impl CellBytesParser for CheckboxCellDataParser {
type Object = CheckboxCellData;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {
match String::from_utf8(bytes.to_vec()) {
Ok(s) => CheckboxCellData::from_str(&s),
Err(_) => Ok(CheckboxCellData("".to_string())),
}
}
}

View File

@ -1,6 +1,6 @@
mod checkbox_option;
mod checkbox_option_entities;
mod tests;
mod checkbox_tests;
pub use checkbox_option::*;
pub use checkbox_option_entities::*;

View File

@ -1,22 +1,17 @@
use crate::entities::{FieldType};
use crate::entities::FieldType;
use crate::impl_type_option;
use crate::services::cell::{
AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellChangeset,
FromCellString,
};
use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
use crate::services::field::{
BoxTypeOptionBuilder, DateCellChangeset, DateCellData, DateFormat, DateTimestamp, TimeFormat, TypeOptionBuilder,
};
use bytes::Bytes;
use chrono::format::strftime::StrftimeItems;
use chrono::{NaiveDateTime, Timelike};
use flowy_derive::{ProtoBuf};
use flowy_derive::ProtoBuf;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use serde::{Deserialize, Serialize};
// Date
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
pub struct DateTypeOption {
@ -129,7 +124,8 @@ impl CellDisplayable<DateTimestamp> for DateTypeOption {
_field_rev: &FieldRevision,
) -> FlowyResult<CellBytes> {
let timestamp = cell_data.try_into_inner()?;
CellBytes::from(self.today_desc_from_timestamp(timestamp))
let date_cell_data = self.today_desc_from_timestamp(timestamp);
CellBytes::from(date_cell_data)
}
}

View File

@ -1,6 +1,7 @@
use crate::entities::CellChangeset;
use crate::entities::{CellIdentifier, CellIdentifierPayload};
use crate::services::cell::{AnyCellData, FromCellChangeset, FromCellString};
use crate::services::cell::{CellBytesParser, FromCellChangeset, FromCellString};
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::{internal_error, ErrorCode, FlowyResult};
@ -117,12 +118,6 @@ impl FromCellString for DateTimestamp {
}
}
impl std::convert::From<AnyCellData> for DateTimestamp {
fn from(data: AnyCellData) -> Self {
let num = data.data.parse::<i64>().unwrap_or(0);
DateTimestamp(num)
}
}
#[derive(Clone, Debug, Copy, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)]
pub enum DateFormat {
Local = 0,
@ -204,3 +199,12 @@ impl std::default::Default for TimeFormat {
TimeFormat::TwentyFourHour
}
}
pub struct DateCellDataParser();
impl CellBytesParser for DateCellDataParser {
type Object = DateCellData;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {
DateCellData::try_from(bytes.as_ref()).map_err(internal_error)
}
}

View File

@ -2,8 +2,8 @@
mod tests {
use crate::entities::FieldType;
use crate::services::cell::{CellDataChangeset, CellDataOperation};
use crate::services::field::FieldBuilder;
use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat};
use crate::services::field::*;
// use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat};
use flowy_grid_data_model::revision::FieldRevision;
use strum::IntoEnumIterator;
@ -260,7 +260,7 @@ mod tests {
let decoded_data = type_option
.decode_cell_data(encoded_data.into(), &FieldType::DateTime, field_rev)
.unwrap()
.parse::<DateCellData>()
.with_parser(DateCellDataParser())
.unwrap();
if type_option.include_time {

View File

@ -1,6 +1,6 @@
mod date_option;
mod date_option_entities;
mod tests;
mod date_tests;
pub use date_option::*;
pub use date_option_entities::*;

View File

@ -2,7 +2,7 @@
mod format;
mod number_option;
mod number_option_entities;
mod tests;
mod number_tests;
pub use format::*;
pub use number_option::*;

View File

@ -1,8 +1,6 @@
use crate::impl_type_option;
use crate::entities::FieldType;
use crate::impl_type_option;
use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation};
use crate::services::field::number_currency::Currency;
use crate::services::field::type_options::number_type_option::format::*;
use crate::services::field::{BoxTypeOptionBuilder, NumberCellData, TypeOptionBuilder};
use bytes::Bytes;
@ -11,7 +9,7 @@ use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use rust_decimal::Decimal;
use rusty_money::Money;
use serde::{Deserialize, Serialize};
use std::str::FromStr;

View File

@ -1,6 +1,8 @@
use crate::services::cell::CellBytesParser;
use crate::services::field::number_currency::Currency;
use crate::services::field::{strip_currency_symbol, NumberFormat, STRIP_SYMBOL};
use flowy_error::{FlowyError, FlowyResult};
use bytes::Bytes;
use flowy_error::{internal_error, FlowyError, FlowyResult};
use rust_decimal::Decimal;
use rusty_money::Money;
use std::str::FromStr;
@ -68,17 +70,17 @@ impl NumberCellData {
}
}
impl FromStr for NumberCellData {
type Err = rust_decimal::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
return Ok(Self::default());
}
let decimal = Decimal::from_str(s)?;
Ok(Self::from_decimal(decimal))
}
}
// impl FromStr for NumberCellData {
// type Err = FlowyError;
//
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// if s.is_empty() {
// return Ok(Self::default());
// }
// let decimal = Decimal::from_str(s).map_err(internal_error)?;
// Ok(Self::from_decimal(decimal))
// }
// }
impl ToString for NumberCellData {
fn to_string(&self) -> String {
@ -91,3 +93,13 @@ impl ToString for NumberCellData {
}
}
}
pub struct NumberCellDataParser(pub NumberFormat);
impl CellBytesParser for NumberCellDataParser {
type Object = NumberCellData;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {
match String::from_utf8(bytes.to_vec()) {
Ok(s) => NumberCellData::from_format_str(&s, true, &self.0),
Err(_) => Ok(NumberCellData::default()),
}
}
}

View File

@ -180,7 +180,7 @@ mod tests {
type_option
.decode_cell_data(cell_data.into(), &field_type, field_rev)
.unwrap()
.parse::<SelectOptionCellData>()
.with_parser(SelectOptionCellDataParser())
.unwrap()
.select_options,
);

View File

@ -1,8 +1,9 @@
use crate::entities::{CellChangeset, CellIdentifier, CellIdentifierPayload, FieldType};
use crate::services::cell::{AnyCellData, CellBytes, CellData, CellDisplayable, FromCellChangeset, FromCellString};
use crate::services::cell::{CellBytes, CellBytesParser, CellData, CellDisplayable, FromCellChangeset, FromCellString};
use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};
use bytes::Bytes;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_error::{internal_error, ErrorCode, FlowyResult};
use flowy_grid_data_model::parser::NotEmptyStr;
use flowy_grid_data_model::revision::{FieldRevision, TypeOptionDataEntry};
use nanoid::nanoid;
@ -160,20 +161,6 @@ impl SelectOptionIds {
}
}
impl std::convert::TryFrom<AnyCellData> for SelectOptionIds {
type Error = FlowyError;
fn try_from(value: AnyCellData) -> Result<Self, Self::Error> {
Ok(Self::from(value.data))
}
}
impl std::convert::From<AnyCellData> for CellData<SelectOptionIds> {
fn from(any_cell_data: AnyCellData) -> Self {
any_cell_data.data.into()
}
}
impl FromCellString for SelectOptionIds {
fn from_cell_str(s: &str) -> FlowyResult<Self>
where
@ -215,6 +202,25 @@ impl std::ops::DerefMut for SelectOptionIds {
&mut self.0
}
}
pub struct SelectOptionIdsParser();
impl CellBytesParser for SelectOptionIdsParser {
type Object = SelectOptionIds;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {
match String::from_utf8(bytes.to_vec()) {
Ok(s) => Ok(SelectOptionIds::from(s)),
Err(_) => Ok(SelectOptionIds::from("".to_owned())),
}
}
}
pub struct SelectOptionCellDataParser();
impl CellBytesParser for SelectOptionCellDataParser {
type Object = SelectOptionCellData;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {
SelectOptionCellData::try_from(bytes.as_ref()).map_err(internal_error)
}
}
#[derive(Clone, Debug, Default, ProtoBuf)]
pub struct SelectOptionCellChangesetPayload {

View File

@ -162,7 +162,7 @@ mod tests {
type_option
.decode_cell_data(cell_data.into(), &field_type, field_rev)
.unwrap()
.parse::<SelectOptionCellData>()
.with_parser(SelectOptionCellDataParser())
.unwrap()
.select_options,
);

View File

@ -1,7 +1,8 @@
use crate::entities::FieldType;
use crate::impl_type_option;
use crate::services::cell::{
try_decode_cell_data, AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable,
try_decode_cell_data, CellBytes, CellBytesParser, CellData, CellDataChangeset, CellDataOperation, CellDisplayable,
FromCellString,
};
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
use bytes::Bytes;
@ -83,11 +84,23 @@ impl AsRef<str> for TextCellData {
}
}
impl std::convert::TryFrom<AnyCellData> for TextCellData {
type Error = FlowyError;
impl FromCellString for TextCellData {
fn from_cell_str(s: &str) -> FlowyResult<Self>
where
Self: Sized,
{
Ok(TextCellData(s.to_owned()))
}
}
fn try_from(value: AnyCellData) -> Result<Self, Self::Error> {
Ok(TextCellData(value.data))
pub struct TextCellDataParser();
impl CellBytesParser for TextCellDataParser {
type Object = TextCellData;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {
match String::from_utf8(bytes.to_vec()) {
Ok(s) => Ok(TextCellData(s)),
Err(_) => Ok(TextCellData("".to_owned())),
}
}
}
@ -111,7 +124,7 @@ mod tests {
type_option
.decode_cell_data(1647251762.to_string().into(), &field_type, &date_time_field_rev)
.unwrap()
.parse::<DateCellData>()
.with_parser(DateCellDataParser())
.unwrap()
.date,
"Mar 14,2022".to_owned()
@ -131,7 +144,7 @@ mod tests {
&single_select_field_rev
)
.unwrap()
.parse::<SelectOptionCellData>()
.with_parser(SelectOptionCellDataParser())
.unwrap()
.select_options,
vec![done_option],
@ -154,7 +167,7 @@ mod tests {
type_option
.decode_cell_data(cell_data.into(), &FieldType::MultiSelect, &multi_select_field_rev)
.unwrap()
.parse::<SelectOptionCellData>()
.with_parser(SelectOptionCellDataParser())
.unwrap()
.select_options,
vec![google_option, facebook_option]

View File

@ -1,6 +1,6 @@
mod tests;
mod url_option;
mod url_option_entities;
mod url_tests;
pub use url_option::*;
pub use url_option_entities::*;

View File

@ -1,13 +1,11 @@
use crate::entities::FieldType;
use crate::impl_type_option;
use crate::services::cell::{
AnyCellData, CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable, FromCellString,
};
use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder, URLCellData};
use bytes::Bytes;
use fancy_regex::Regex;
use flowy_derive::ProtoBuf;
use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
@ -64,16 +62,12 @@ impl CellDataOperation<URLCellData, String> for URLTypeOption {
changeset: CellDataChangeset<String>,
_cell_rev: Option<CellRevision>,
) -> Result<String, FlowyError> {
let changeset = changeset.try_into_inner()?;
let content = changeset.try_into_inner()?;
let mut url = "".to_string();
if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
if let Ok(Some(m)) = URL_REGEX.find(&content) {
url = auto_append_scheme(m.as_str());
}
URLCellData {
url,
content: changeset,
}
.to_json()
URLCellData { url, content }.to_json()
}
}

View File

@ -1,6 +1,7 @@
use crate::services::cell::{AnyCellData, FromCellString};
use crate::services::cell::{CellBytesParser, FromCellString};
use bytes::Bytes;
use flowy_derive::ProtoBuf;
use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_error::{internal_error, FlowyResult};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
@ -25,16 +26,17 @@ impl URLCellData {
}
}
pub struct URLCellDataParser();
impl CellBytesParser for URLCellDataParser {
type Object = URLCellData;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {
URLCellData::try_from(bytes.as_ref()).map_err(internal_error)
}
}
impl FromCellString for URLCellData {
fn from_cell_str(s: &str) -> FlowyResult<Self> {
serde_json::from_str::<URLCellData>(s).map_err(internal_error)
}
}
impl std::convert::TryFrom<AnyCellData> for URLCellData {
type Error = FlowyError;
fn try_from(data: AnyCellData) -> Result<Self, Self::Error> {
serde_json::from_str::<URLCellData>(&data.data).map_err(internal_error)
}
}

View File

@ -2,7 +2,7 @@
mod tests {
use crate::entities::FieldType;
use crate::services::cell::{CellData, CellDataOperation};
use crate::services::field::FieldBuilder;
use crate::services::field::{FieldBuilder, URLCellDataParser};
use crate::services::field::{URLCellData, URLTypeOption};
use flowy_grid_data_model::revision::FieldRevision;
@ -61,7 +61,7 @@ mod tests {
type_option
.decode_cell_data(encoded_data.into(), field_type, field_rev)
.unwrap()
.parse::<URLCellData>()
.with_parser(URLCellDataParser())
.unwrap()
}
}

View File

@ -1,5 +1,5 @@
use crate::entities::{CheckboxCondition, GridCheckboxFilter};
use crate::services::cell::{AnyCellData, CellFilterOperation};
use crate::services::cell::{AnyCellData, CellData, CellFilterOperation};
use crate::services::field::{CheckboxCellData, CheckboxTypeOption};
use flowy_error::FlowyResult;
@ -18,7 +18,8 @@ impl CellFilterOperation<GridCheckboxFilter> for CheckboxTypeOption {
if !any_cell_data.is_checkbox() {
return Ok(true);
}
let checkbox_cell_data: CheckboxCellData = any_cell_data.try_into()?;
let cell_data: CellData<CheckboxCellData> = any_cell_data.into();
let checkbox_cell_data = cell_data.try_into_inner()?;
Ok(filter.is_visible(&checkbox_cell_data))
}
}

View File

@ -1,5 +1,5 @@
use crate::entities::{DateFilterCondition, GridDateFilter};
use crate::services::cell::{AnyCellData, CellFilterOperation};
use crate::services::cell::{AnyCellData, CellData, CellFilterOperation};
use crate::services::field::{DateTimestamp, DateTypeOption};
use flowy_error::FlowyResult;
@ -34,7 +34,8 @@ impl CellFilterOperation<GridDateFilter> for DateTypeOption {
if !any_cell_data.is_date() {
return Ok(true);
}
let timestamp: DateTimestamp = any_cell_data.into();
let cell_data: CellData<DateTimestamp> = any_cell_data.into();
let timestamp = cell_data.try_into_inner()?;
Ok(filter.is_visible(timestamp))
}
}

View File

@ -47,9 +47,7 @@ impl CellFilterOperation<GridNumberFilter> for NumberTypeOption {
#[cfg(test)]
mod tests {
use crate::entities::{GridNumberFilter, NumberFilterCondition};
use crate::services::field::{NumberCellData, NumberFormat};
use std::str::FromStr;
#[test]
fn number_filter_equal_test() {
let number_filter = GridNumberFilter {
@ -58,7 +56,7 @@ mod tests {
};
for (num_str, visible) in [("123", true), ("1234", false), ("", false)] {
let data = NumberCellData::from_str(num_str).unwrap();
let data = NumberCellData::from_format_str(num_str, true, &NumberFormat::Num).unwrap();
assert_eq!(number_filter.is_visible(&data), visible);
}
@ -75,7 +73,7 @@ mod tests {
content: Some("12".to_owned()),
};
for (num_str, visible) in [("123", true), ("10", false), ("30", true), ("", false)] {
let data = NumberCellData::from_str(num_str).unwrap();
let data = NumberCellData::from_format_str(num_str, true, &NumberFormat::Num).unwrap();
assert_eq!(number_filter.is_visible(&data), visible);
}
}
@ -87,7 +85,7 @@ mod tests {
content: Some("100".to_owned()),
};
for (num_str, visible) in [("12", true), ("1234", false), ("30", true), ("", true)] {
let data = NumberCellData::from_str(num_str).unwrap();
let data = NumberCellData::from_format_str(num_str, true, &NumberFormat::Num).unwrap();
assert_eq!(number_filter.is_visible(&data), visible);
}
}

View File

@ -1,5 +1,5 @@
use crate::entities::{GridTextFilter, TextFilterCondition};
use crate::services::cell::{AnyCellData, CellFilterOperation};
use crate::services::cell::{AnyCellData, CellData, CellFilterOperation};
use crate::services::field::{RichTextTypeOption, TextCellData};
use flowy_error::FlowyResult;
@ -30,7 +30,8 @@ impl CellFilterOperation<GridTextFilter> for RichTextTypeOption {
return Ok(true);
}
let text_cell_data: TextCellData = any_cell_data.try_into()?;
let cell_data: CellData<TextCellData> = any_cell_data.into();
let text_cell_data = cell_data.try_into_inner()?;
Ok(filter.is_visible(text_cell_data))
}
}

View File

@ -1,5 +1,5 @@
use crate::entities::GridTextFilter;
use crate::services::cell::{AnyCellData, CellFilterOperation};
use crate::services::cell::{AnyCellData, CellData, CellFilterOperation};
use crate::services::field::{TextCellData, URLTypeOption};
use flowy_error::FlowyResult;
@ -9,7 +9,8 @@ impl CellFilterOperation<GridTextFilter> for URLTypeOption {
return Ok(true);
}
let text_cell_data: TextCellData = any_cell_data.try_into()?;
let cell_data: CellData<TextCellData> = any_cell_data.into();
let text_cell_data = cell_data.try_into_inner()?;
Ok(filter.is_visible(&text_cell_data))
}
}

View File

@ -3,7 +3,7 @@ use crate::entities::CellIdentifier;
use crate::entities::*;
use crate::manager::{GridTaskSchedulerRwLock, GridUser};
use crate::services::block_manager::GridBlockManager;
use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data};
use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data, CellBytes};
use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
use crate::services::filter::{GridFilterChangeset, GridFilterService};
use crate::services::persistence::block_index::BlockIndexCache;
@ -340,16 +340,16 @@ impl GridRevisionEditor {
}
pub async fn get_cell(&self, params: &CellIdentifier) -> Option<Cell> {
let cell_bytes = self.get_cell_bytes(params).await?;
Some(Cell::new(&params.field_id, cell_bytes.to_vec()))
}
pub async fn get_cell_bytes(&self, params: &CellIdentifier) -> Option<CellBytes> {
let field_rev = self.get_field_rev(&params.field_id).await?;
let row_rev = self.block_manager.get_row_rev(&params.row_id).await.ok()??;
let cell_rev = row_rev.cells.get(&params.field_id)?.clone();
let data = decode_any_cell_data(cell_rev.data, &field_rev).to_vec();
Some(Cell::new(&params.field_id, data))
}
pub async fn get_cell_display(&self, _params: &CellIdentifier) -> Option<String> {
todo!()
Some(decode_any_cell_data(cell_rev.data, &field_rev))
}
pub async fn get_cell_rev(&self, row_id: &str, field_id: &str) -> FlowyResult<Option<CellRevision>> {

View File

@ -1,5 +1,6 @@
use crate::grid::block_test::script::GridRowTest;
use crate::grid::block_test::script::RowScript::*;
use flowy_grid::entities::FieldType;
use flowy_grid_data_model::revision::RowMetaChangeset;
#[tokio::test]
@ -67,15 +68,48 @@ async fn grid_row_add_cells_test() {
let mut test = GridRowTest::new().await;
let mut builder = test.row_builder();
builder.insert_text_cell("hello world");
builder.insert_number_cell("18,443");
builder.insert_date_cell("1647251762");
builder.insert_single_select_cell(|options| options.first().unwrap());
let text_field_id = builder.insert_text_cell("hello world");
let number_field_id = builder.insert_number_cell("18,443");
let date_field_id = builder.insert_date_cell("1647251762");
let single_select_field_id = builder.insert_single_select_cell(|options| options.first().unwrap());
builder.insert_multi_select_cell(|options| options);
builder.insert_checkbox_cell("false");
builder.insert_url_cell("1");
let url_field_id = builder.insert_url_cell("https://appflowy.io");
let row_rev = builder.build();
let scripts = vec![CreateRow { row_rev }];
let row_id = row_rev.id.clone();
let scripts = vec![
CreateRow { row_rev },
AssertCell {
row_id: row_id.clone(),
field_id: text_field_id,
field_type: FieldType::RichText,
expected: "hello world".to_owned(),
},
AssertCell {
row_id: row_id.clone(),
field_id: number_field_id,
field_type: FieldType::Number,
expected: "$18,443.00".to_owned(),
},
AssertCell {
row_id: row_id.clone(),
field_id: single_select_field_id,
field_type: FieldType::SingleSelect,
expected: "Completed".to_owned(),
},
AssertCell {
row_id: row_id.clone(),
field_id: date_field_id,
field_type: FieldType::DateTime,
expected: "2022/03/14".to_owned(),
},
AssertCell {
row_id: row_id.clone(),
field_id: url_field_id,
field_type: FieldType::URL,
expected: "https://appflowy.io/".to_owned(),
},
];
test.run_scripts(scripts).await;
}

View File

@ -1,7 +1,11 @@
use crate::grid::block_test::util::GridRowTestBuilder;
use crate::grid::grid_editor::GridEditorTest;
use flowy_grid::entities::{CellIdentifier, RowInfo};
use flowy_grid::entities::{CellIdentifier, FieldType, RowInfo};
use flowy_grid::services::field::{
DateCellDataParser, NumberCellDataParser, NumberFormat, NumberTypeOption, SelectOptionCellDataParser,
SelectOptionIdsParser, SelectOptionOperation, SingleSelectTypeOption, TextCellDataParser, URLCellDataParser,
};
use flowy_grid_data_model::revision::{
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
};
@ -24,7 +28,8 @@ pub enum RowScript {
AssertCell {
row_id: String,
field_id: String,
expected_display: Option<String>,
field_type: FieldType,
expected: String,
},
AssertRowCount(usize),
CreateBlock {
@ -101,20 +106,15 @@ impl GridRowTest {
RowScript::AssertCell {
row_id,
field_id,
expected_display,
field_type,
expected,
} => {
let id = CellIdentifier {
grid_id: self.grid_id.clone(),
field_id,
row_id,
};
let display = self.editor.get_cell_display(&id).await;
match expected_display {
None => {}
Some(expected_display) => {
assert_eq!(display.unwrap(), expected_display);
}
}
self.compare_cell_content(id, field_type, expected).await;
}
RowScript::AssertRow { expected_row } => {
let row = &*self
@ -153,6 +153,72 @@ impl GridRowTest {
}
}
}
async fn compare_cell_content(&self, cell_id: CellIdentifier, field_type: FieldType, expected: String) {
match field_type {
FieldType::RichText => {
let cell_data = self
.editor
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(TextCellDataParser())
.unwrap();
assert_eq!(cell_data.as_ref(), &expected);
}
FieldType::Number => {
let field_rev = self.editor.get_field_rev(&cell_id.field_id).await.unwrap();
let number_type_option = field_rev
.get_type_option_entry::<NumberTypeOption>(FieldType::Number.into())
.unwrap();
let cell_data = self
.editor
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(NumberCellDataParser(number_type_option.format.clone()))
.unwrap();
assert_eq!(cell_data.to_string(), expected);
}
FieldType::DateTime => {
let cell_data = self
.editor
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(DateCellDataParser())
.unwrap();
assert_eq!(cell_data.date, expected);
}
FieldType::SingleSelect => {
let select_options = self
.editor
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(SelectOptionCellDataParser())
.unwrap();
let select_option = select_options.select_options.first().unwrap();
assert_eq!(select_option.name, expected);
}
FieldType::MultiSelect => {}
FieldType::Checkbox => {}
FieldType::URL => {
let cell_data = self
.editor
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(URLCellDataParser())
.unwrap();
assert_eq!(cell_data.content, expected);
assert_eq!(cell_data.url, expected);
}
}
}
}
impl std::ops::Deref for GridRowTest {

View File

@ -23,21 +23,24 @@ impl<'a> GridRowTestBuilder<'a> {
}
}
pub fn insert_text_cell(&mut self, data: &str) {
pub fn insert_text_cell(&mut self, data: &str) -> String {
let text_field = self.field_rev_with_type(&FieldType::RichText);
self.inner_builder
.insert_cell(&text_field.id, data.to_string())
.unwrap();
text_field.id.clone()
}
pub fn insert_number_cell(&mut self, data: &str) {
pub fn insert_number_cell(&mut self, data: &str) -> String {
let number_field = self.field_rev_with_type(&FieldType::Number);
self.inner_builder
.insert_cell(&number_field.id, data.to_string())
.unwrap();
number_field.id.clone()
}
pub fn insert_date_cell(&mut self, data: &str) {
pub fn insert_date_cell(&mut self, data: &str) -> String {
let value = serde_json::to_string(&DateCellChangeset {
date: Some(data.to_string()),
time: None,
@ -45,6 +48,7 @@ impl<'a> GridRowTestBuilder<'a> {
.unwrap();
let date_field = self.field_rev_with_type(&FieldType::DateTime);
self.inner_builder.insert_cell(&date_field.id, value).unwrap();
date_field.id.clone()
}
pub fn insert_checkbox_cell(&mut self, data: &str) {
@ -54,14 +58,13 @@ impl<'a> GridRowTestBuilder<'a> {
.unwrap();
}
pub fn insert_url_cell(&mut self, data: &str) {
let number_field = self.field_rev_with_type(&FieldType::URL);
self.inner_builder
.insert_cell(&number_field.id, data.to_string())
.unwrap();
pub fn insert_url_cell(&mut self, data: &str) -> String {
let url_field = self.field_rev_with_type(&FieldType::URL);
self.inner_builder.insert_cell(&url_field.id, data.to_string()).unwrap();
url_field.id.clone()
}
pub fn insert_single_select_cell<F>(&mut self, f: F)
pub fn insert_single_select_cell<F>(&mut self, f: F) -> String
where
F: Fn(&Vec<SelectOption>) -> &SelectOption,
{
@ -71,6 +74,8 @@ impl<'a> GridRowTestBuilder<'a> {
self.inner_builder
.insert_select_option_cell(&single_select_field.id, option.id.clone())
.unwrap();
single_select_field.id.clone()
}
pub fn insert_multi_select_cell<F>(&mut self, f: F)

View File

@ -1,12 +1,12 @@
use crate::grid::filter_test::script::FilterScript::*;
use crate::grid::filter_test::script::*;
use flowy_grid::entities::{CreateGridFilterPayload, TextFilterCondition};
use flowy_grid::entities::{CreateGridFilterPayload, FieldType, TextFilterCondition};
use flowy_grid_data_model::revision::FieldRevision;
#[tokio::test]
async fn grid_filter_create_test() {
let mut test = GridFilterTest::new().await;
let field_rev = test.text_field();
let field_rev = test.get_field_rev(FieldType::RichText);
let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
test.run_scripts(scripts).await;
@ -16,7 +16,7 @@ async fn grid_filter_create_test() {
#[should_panic]
async fn grid_filter_invalid_condition_panic_test() {
let mut test = GridFilterTest::new().await;
let field_rev = test.text_field().clone();
let field_rev = test.get_field_rev(FieldType::RichText).clone();
// 100 is not a valid condition, so this test should be panic.
let payload = CreateGridFilterPayload::new(&field_rev, 100, Some("".to_owned()));
@ -27,7 +27,7 @@ async fn grid_filter_invalid_condition_panic_test() {
#[tokio::test]
async fn grid_filter_delete_test() {
let mut test = GridFilterTest::new().await;
let field_rev = test.text_field().clone();
let field_rev = test.get_field_rev(FieldType::RichText).clone();
let payload = create_filter(&field_rev, TextFilterCondition::TextIsEmpty, "abc");
let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
test.run_scripts(scripts).await;
@ -36,7 +36,7 @@ async fn grid_filter_delete_test() {
test.run_scripts(vec![
DeleteGridTableFilter {
filter_id: filter.id,
field_rev,
field_rev: field_rev.as_ref().clone(),
},
AssertTableFilterCount { count: 0 },
])

View File

@ -64,7 +64,7 @@ impl GridEditorTest {
}
}
pub(crate) async fn get_row_revs(&self) -> Vec<Arc<RowRevision>> {
pub async fn get_row_revs(&self) -> Vec<Arc<RowRevision>> {
self.editor
.grid_block_snapshots(None)
.await
@ -79,12 +79,12 @@ impl GridEditorTest {
self.editor.get_grid_filter(&layout_type).await.unwrap()
}
pub fn text_field(&self) -> &FieldRevision {
pub fn get_field_rev(&self, field_type: FieldType) -> &Arc<FieldRevision> {
self.field_revs
.iter()
.filter(|field_rev| {
let t_field_type: FieldType = field_rev.field_type_rev.into();
t_field_type == FieldType::RichText
t_field_type == field_type
})
.collect::<Vec<_>>()
.pop()
@ -129,7 +129,6 @@ fn make_test_grid() -> BuildGridContext {
FieldType::SingleSelect => {
// Single Select
let single_select = SingleSelectTypeOptionBuilder::default()
.option(SelectOption::new("Live"))
.option(SelectOption::new("Completed"))
.option(SelectOption::new("Planned"))
.option(SelectOption::new("Paused"));