chore: add more test

This commit is contained in:
appflowy 2022-07-13 22:56:34 +08:00
parent 602aab45e2
commit d02acbae6e
28 changed files with 311 additions and 104 deletions

View File

@ -7,10 +7,10 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';

View File

@ -5,6 +5,7 @@ import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:table_calendar/table_calendar.dart';

View File

@ -1,4 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

View File

@ -1,4 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';

View File

@ -1,4 +1,4 @@
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';

View File

@ -1,5 +1,6 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';

View File

@ -9,7 +9,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

View File

@ -57,7 +57,7 @@ impl ToString for CheckboxCellData {
self.0.clone()
}
}
pub struct CheckboxCellDataParser;
pub struct CheckboxCellDataParser();
impl CellBytesParser for CheckboxCellDataParser {
type Object = CheckboxCellData;
fn parse(&self, bytes: &Bytes) -> FlowyResult<Self::Object> {

View File

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

View File

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

View File

@ -1,9 +1,9 @@
#![allow(clippy::module_inception)]
mod format;
mod number_option;
mod number_option_entities;
mod number_tests;
mod number_type_option;
mod number_type_option_entities;
pub use format::*;
pub use number_option::*;
pub use number_option_entities::*;
pub use number_type_option::*;
pub use number_type_option_entities::*;

View File

@ -2,7 +2,7 @@ use crate::services::cell::CellBytesParser;
use crate::services::field::number_currency::Currency;
use crate::services::field::{strip_currency_symbol, NumberFormat, STRIP_SYMBOL};
use bytes::Bytes;
use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_error::{FlowyError, FlowyResult};
use rust_decimal::Decimal;
use rusty_money::Money;
use std::str::FromStr;

View File

@ -1,2 +1,2 @@
mod text_option;
pub use text_option::*;
mod text_type_option;
pub use text_type_option::*;

View File

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

View File

@ -270,10 +270,14 @@ impl GridRevisionEditor {
pub async fn create_row(&self, start_row_id: Option<String>) -> FlowyResult<RowInfo> {
let field_revs = self.grid_pad.read().await.get_field_revs(None)?;
let field_revs_ref = field_revs
.iter()
.map(|field_rev| field_rev.as_ref())
.collect::<Vec<&FieldRevision>>();
let block_id = self.block_id().await?;
// insert empty row below the row whose id is upper_row_id
let row_rev = RowRevisionBuilder::new(&field_revs).build(&block_id);
let row_rev = RowRevisionBuilder::new(&field_revs_ref).build(&block_id);
let row_order = RowInfo::from(&row_rev);
// insert the row

View File

@ -4,19 +4,18 @@ use flowy_error::{FlowyError, FlowyResult};
use flowy_grid_data_model::revision::{gen_row_id, CellRevision, FieldRevision, RowRevision, DEFAULT_ROW_HEIGHT};
use indexmap::IndexMap;
use std::collections::HashMap;
use std::sync::Arc;
pub struct RowRevisionBuilder<'a> {
field_rev_map: HashMap<&'a String, &'a Arc<FieldRevision>>,
field_rev_map: HashMap<&'a String, &'a FieldRevision>,
payload: CreateRowRevisionPayload,
}
impl<'a> RowRevisionBuilder<'a> {
pub fn new(fields: &'a [Arc<FieldRevision>]) -> Self {
pub fn new(fields: &'a [&'a FieldRevision]) -> Self {
let field_rev_map = fields
.iter()
.map(|field| (&field.id, field))
.collect::<HashMap<&String, &Arc<FieldRevision>>>();
.map(|field| (&field.id, *field))
.collect::<HashMap<&String, &'a FieldRevision>>();
let payload = CreateRowRevisionPayload {
row_id: gen_row_id(),

View File

@ -1,6 +1,8 @@
use crate::grid::block_test::script::GridRowTest;
use crate::grid::block_test::script::RowScript::*;
use crate::grid::block_test::script::{CreateRowScriptBuilder, GridRowTest};
use crate::grid::grid_editor::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, TWITTER};
use flowy_grid::entities::FieldType;
use flowy_grid::services::field::{NO, SELECTION_IDS_SEPARATOR};
use flowy_grid_data_model::revision::RowMetaChangeset;
#[tokio::test]
@ -66,50 +68,65 @@ async fn grid_delete_row() {
#[tokio::test]
async fn grid_row_add_cells_test() {
let mut test = GridRowTest::new().await;
let mut builder = test.row_builder();
let mut builder = CreateRowScriptBuilder::new(&test);
builder.insert(FieldType::RichText, "hello world", "hello world");
builder.insert(FieldType::DateTime, "1647251762", "2022/03/14");
builder.insert(FieldType::Number, "18,443", "$18,443.00");
builder.insert(FieldType::Checkbox, "false", NO);
builder.insert(FieldType::URL, "https://appflowy.io", "https://appflowy.io");
builder.insert_single_select_cell(|mut options| options.remove(0), COMPLETED);
builder.insert_multi_select_cell(
|options| options,
&vec![GOOGLE, FACEBOOK, TWITTER].join(SELECTION_IDS_SEPARATOR),
);
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");
let url_field_id = builder.insert_url_cell("https://appflowy.io");
let row_rev = builder.build();
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;
test.run_scripts(builder.build()).await;
}
#[tokio::test]
async fn grid_row_insert_number_test() {
let mut test = GridRowTest::new().await;
for (val, expected) in &[("1647251762", "2022/03/14"), ("2022/03/14", ""), ("", "")] {
let mut builder = CreateRowScriptBuilder::new(&test);
builder.insert(FieldType::DateTime, val, expected);
test.run_scripts(builder.build()).await;
}
}
#[tokio::test]
async fn grid_row_insert_date_test() {
let mut test = GridRowTest::new().await;
for (val, expected) in &[
("18,443", "$18,443.00"),
("0", "$0.00"),
("100000", "$100,000.00"),
("$100,000.00", "$100,000.00"),
("", ""),
] {
let mut builder = CreateRowScriptBuilder::new(&test);
builder.insert(FieldType::Number, val, expected);
test.run_scripts(builder.build()).await;
}
}
#[tokio::test]
async fn grid_row_insert_single_select_test() {
let mut test = GridRowTest::new().await;
let mut builder = CreateRowScriptBuilder::new(&test);
builder.insert_single_select_cell(|mut options| options.pop().unwrap(), PAUSED);
test.run_scripts(builder.build()).await;
}
#[tokio::test]
async fn grid_row_insert_multi_select_test() {
let mut test = GridRowTest::new().await;
let mut builder = CreateRowScriptBuilder::new(&test);
builder.insert_multi_select_cell(
|mut options| {
options.remove(0);
options
},
&vec![FACEBOOK, TWITTER].join(SELECTION_IDS_SEPARATOR),
);
test.run_scripts(builder.build()).await;
}

View File

@ -1,15 +1,18 @@
use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
use crate::grid::block_test::util::GridRowTestBuilder;
use crate::grid::grid_editor::GridEditorTest;
use flowy_grid::entities::{CellIdentifier, FieldType, RowInfo};
use flowy_grid::services::field::{
DateCellDataParser, NumberCellDataParser, NumberFormat, NumberTypeOption, SelectOptionCellDataParser,
SelectOptionIdsParser, SelectOptionOperation, SingleSelectTypeOption, TextCellDataParser, URLCellDataParser,
CheckboxCellDataParser, DateCellDataParser, MultiSelectTypeOption, NumberCellDataParser, NumberTypeOption,
SelectOption, SelectOptionCellDataParser, SelectOptionIdsParser, SingleSelectTypeOption, TextCellDataParser,
URLCellDataParser, SELECTION_IDS_SEPARATOR,
};
use flowy_grid_data_model::revision::{
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
FieldRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
};
use std::collections::HashMap;
use std::sync::Arc;
use strum::IntoEnumIterator;
pub enum RowScript {
CreateEmptyRow,
@ -71,7 +74,19 @@ impl GridRowTest {
}
pub fn row_builder(&self) -> GridRowTestBuilder {
GridRowTestBuilder::new(self.block_id(), &self.field_revs)
let field_revs_ref = self
.field_revs
.iter()
.map(|field_rev| field_rev.as_ref())
.collect::<Vec<&FieldRevision>>();
GridRowTestBuilder::new(
self.block_id(),
&self
.field_revs
.iter()
.map(|field_rev| field_rev.as_ref())
.collect::<Vec<&FieldRevision>>(),
)
}
pub async fn run_script(&mut self, script: RowScript) {
@ -177,7 +192,7 @@ impl GridRowTest {
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(NumberCellDataParser(number_type_option.format.clone()))
.with_parser(NumberCellDataParser(number_type_option.format))
.unwrap();
assert_eq!(cell_data.to_string(), expected);
}
@ -193,18 +208,45 @@ impl GridRowTest {
assert_eq!(cell_data.date, expected);
}
FieldType::SingleSelect => {
let select_options = self
let cell_data = self
.editor
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(SelectOptionCellDataParser())
.unwrap();
let select_option = select_options.select_options.first().unwrap();
let select_option = cell_data.select_options.first().unwrap();
assert_eq!(select_option.name, expected);
}
FieldType::MultiSelect => {}
FieldType::Checkbox => {}
FieldType::MultiSelect => {
let cell_data = self
.editor
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(SelectOptionCellDataParser())
.unwrap();
let s = cell_data
.select_options
.into_iter()
.map(|option| option.name)
.collect::<Vec<String>>()
.join(SELECTION_IDS_SEPARATOR);
assert_eq!(s, expected);
}
FieldType::Checkbox => {
let cell_data = self
.editor
.get_cell_bytes(&cell_id)
.await
.unwrap()
.with_parser(CheckboxCellDataParser())
.unwrap();
assert_eq!(cell_data.to_string(), expected);
}
FieldType::URL => {
let cell_data = self
.editor
@ -215,7 +257,7 @@ impl GridRowTest {
.unwrap();
assert_eq!(cell_data.content, expected);
assert_eq!(cell_data.url, expected);
// assert_eq!(cell_data.url, expected);
}
}
}
@ -234,3 +276,113 @@ impl std::ops::DerefMut for GridRowTest {
&mut self.inner
}
}
pub struct CreateRowScriptBuilder<'a> {
builder: GridRowTestBuilder<'a>,
data_by_field_type: HashMap<FieldType, CellTestData>,
output_by_field_type: HashMap<FieldType, CellTestOutput>,
}
impl<'a> CreateRowScriptBuilder<'a> {
pub fn new(test: &'a GridRowTest) -> Self {
Self {
builder: test.row_builder(),
data_by_field_type: HashMap::new(),
output_by_field_type: HashMap::new(),
}
}
pub fn insert(&mut self, field_type: FieldType, input: &str, expected: &str) {
self.data_by_field_type.insert(
field_type,
CellTestData {
input: input.to_string(),
expected: expected.to_owned(),
},
);
}
pub fn insert_single_select_cell<F>(&mut self, f: F, expected: &str)
where
F: Fn(Vec<SelectOption>) -> SelectOption,
{
let field_id = self.builder.insert_single_select_cell(f);
self.output_by_field_type.insert(
FieldType::SingleSelect,
CellTestOutput {
field_id,
expected: expected.to_owned(),
},
);
}
pub fn insert_multi_select_cell<F>(&mut self, f: F, expected: &str)
where
F: Fn(Vec<SelectOption>) -> Vec<SelectOption>,
{
let field_id = self.builder.insert_multi_select_cell(f);
self.output_by_field_type.insert(
FieldType::MultiSelect,
CellTestOutput {
field_id,
expected: expected.to_owned(),
},
);
}
pub fn build(mut self) -> Vec<RowScript> {
let mut scripts = vec![];
let output_by_field_type = &mut self.output_by_field_type;
for field_type in FieldType::iter() {
let field_type: FieldType = field_type;
if let Some(data) = self.data_by_field_type.get(&field_type) {
let field_id = match field_type {
FieldType::RichText => self.builder.insert_text_cell(&data.input),
FieldType::Number => self.builder.insert_number_cell(&data.input),
FieldType::DateTime => self.builder.insert_date_cell(&data.input),
FieldType::Checkbox => self.builder.insert_checkbox_cell(&data.input),
FieldType::URL => self.builder.insert_url_cell(&data.input),
_ => "".to_owned(),
};
if !field_id.is_empty() {
output_by_field_type.insert(
field_type,
CellTestOutput {
field_id,
expected: data.expected.clone(),
},
);
}
}
}
let row_rev = self.builder.build();
let row_id = row_rev.id.clone();
scripts.push(CreateRow { row_rev });
for field_type in FieldType::iter() {
if let Some(data) = output_by_field_type.get(&field_type) {
let script = AssertCell {
row_id: row_id.clone(),
field_id: data.field_id.clone(),
field_type,
expected: data.expected.clone(),
};
scripts.push(script);
}
}
scripts
}
}
pub struct CellTestData {
pub input: String,
pub expected: String,
}
struct CellTestOutput {
field_id: String,
expected: String,
}

View File

@ -1,19 +1,21 @@
use flowy_grid::entities::FieldType;
use flowy_grid::services::field::selection_type_option::{SelectOption, SELECTION_IDS_SEPARATOR};
use flowy_grid::services::field::{DateCellChangeset, MultiSelectTypeOption, SingleSelectTypeOption};
use flowy_grid::services::field::{
DateCellChangeset, MultiSelectTypeOption, SelectOption, SingleSelectTypeOption, SELECTION_IDS_SEPARATOR,
};
use flowy_grid::services::row::RowRevisionBuilder;
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
use std::sync::Arc;
use strum::EnumCount;
pub struct GridRowTestBuilder<'a> {
block_id: String,
field_revs: &'a [Arc<FieldRevision>],
field_revs: &'a [&'a FieldRevision],
inner_builder: RowRevisionBuilder<'a>,
}
impl<'a> GridRowTestBuilder<'a> {
pub fn new(block_id: &str, field_revs: &'a [Arc<FieldRevision>]) -> Self {
pub fn new(block_id: &str, field_revs: &'a [&'a FieldRevision]) -> Self {
assert_eq!(field_revs.len(), FieldType::COUNT);
let inner_builder = RowRevisionBuilder::new(field_revs);
Self {
@ -51,11 +53,13 @@ impl<'a> GridRowTestBuilder<'a> {
date_field.id.clone()
}
pub fn insert_checkbox_cell(&mut self, data: &str) {
let number_field = self.field_rev_with_type(&FieldType::Checkbox);
pub fn insert_checkbox_cell(&mut self, data: &str) -> String {
let checkbox_field = self.field_rev_with_type(&FieldType::Checkbox);
self.inner_builder
.insert_cell(&number_field.id, data.to_string())
.insert_cell(&checkbox_field.id, data.to_string())
.unwrap();
checkbox_field.id.clone()
}
pub fn insert_url_cell(&mut self, data: &str) -> String {
@ -64,6 +68,7 @@ impl<'a> GridRowTestBuilder<'a> {
url_field.id.clone()
}
#[allow(dead_code)]
pub fn insert_single_select_cell<F>(&mut self, f: F) -> String
where
F: Fn(&Vec<SelectOption>) -> &SelectOption,
@ -78,7 +83,7 @@ impl<'a> GridRowTestBuilder<'a> {
single_select_field.id.clone()
}
pub fn insert_multi_select_cell<F>(&mut self, f: F)
pub fn insert_multi_select_cell<F>(&mut self, f: F) -> String
where
F: Fn(&Vec<SelectOption>) -> &Vec<SelectOption>,
{
@ -93,6 +98,8 @@ impl<'a> GridRowTestBuilder<'a> {
self.inner_builder
.insert_select_option_cell(&multi_select_field.id, ops_ids)
.unwrap();
multi_select_field.id.clone()
}
pub fn field_rev_with_type(&self, field_type: &FieldType) -> FieldRevision {
@ -111,3 +118,17 @@ impl<'a> GridRowTestBuilder<'a> {
self.inner_builder.build(&self.block_id)
}
}
impl<'a> std::ops::Deref for GridRowTestBuilder<'a> {
type Target = RowRevisionBuilder<'a>;
fn deref(&self) -> &Self::Target {
&self.inner_builder
}
}
impl<'a> std::ops::DerefMut for GridRowTestBuilder<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner_builder
}
}

View File

@ -96,6 +96,14 @@ impl GridEditorTest {
}
}
pub const GOOGLE: &str = "Google";
pub const FACEBOOK: &str = "Facebook";
pub const TWITTER: &str = "Twitter";
pub const COMPLETED: &str = "Completed";
pub const PLANNED: &str = "Planned";
pub const PAUSED: &str = "Paused";
// This grid is assumed to contain all the Fields.
fn make_test_grid() -> BuildGridContext {
let mut grid_builder = GridBuilder::new();
@ -129,18 +137,18 @@ fn make_test_grid() -> BuildGridContext {
FieldType::SingleSelect => {
// Single Select
let single_select = SingleSelectTypeOptionBuilder::default()
.option(SelectOption::new("Completed"))
.option(SelectOption::new("Planned"))
.option(SelectOption::new("Paused"));
.option(SelectOption::new(COMPLETED))
.option(SelectOption::new(PLANNED))
.option(SelectOption::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()
.option(SelectOption::new("Google"))
.option(SelectOption::new("Facebook"))
.option(SelectOption::new("Twitter"));
.option(SelectOption::new(GOOGLE))
.option(SelectOption::new(FACEBOOK))
.option(SelectOption::new(TWITTER));
let multi_select_field = FieldBuilder::new(multi_select)
.name("Platform")
.visibility(true)

View File

@ -45,6 +45,10 @@ impl GridBuilder {
self.add_row(row);
}
// pub fn field_revs(&self) -> Vec<FieldRevision> {
// self.build_context.field_revs
// }
pub fn build(self) -> BuildGridContext {
self.build_context
}