Bladeren bron

chore: add more test

appflowy 2 jaren geleden
bovenliggende
commit
d02acbae6e
28 gewijzigde bestanden met toevoegingen van 309 en 102 verwijderingen
  1. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart
  2. 1 0
      frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart
  3. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart
  4. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart
  5. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart
  6. 1 0
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart
  7. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart
  8. 0 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs
  9. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs
  10. 4 4
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs
  11. 0 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs
  12. 0 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs
  13. 4 4
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs
  14. 4 4
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs
  15. 0 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs
  16. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs
  17. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs
  18. 0 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs
  19. 4 4
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs
  20. 0 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs
  21. 0 0
      frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs
  22. 5 1
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  23. 4 5
      frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs
  24. 61 44
      frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs
  25. 163 11
      frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs
  26. 30 9
      frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs
  27. 14 6
      frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs
  28. 4 0
      shared-lib/flowy-sync/src/client_grid/grid_builder.rs

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart

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

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart

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

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/cell/date_cell_bloc.dart

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

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart

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

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_editor_bloc.dart

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

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/field/type_option/date_bloc.dart

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

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart

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

+ 0 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs


+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_option_entities.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs

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

+ 4 - 4
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/mod.rs

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

+ 0 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option.rs


+ 0 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_option_entities.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs


+ 4 - 4
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/mod.rs

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

+ 4 - 4
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/mod.rs

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

+ 0 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option.rs


+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_option_entities.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/number_type_option/number_type_option_entities.rs

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

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/mod.rs

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

+ 0 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_option.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/text_type_option/text_type_option.rs


+ 4 - 4
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/mod.rs

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

+ 0 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option.rs


+ 0 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_option_entities.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/url_type_option/url_type_option_entities.rs


+ 5 - 1
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

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

+ 4 - 5
frontend/rust-lib/flowy-grid/src/services/row/row_builder.rs

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

+ 61 - 44
frontend/rust-lib/flowy-grid/tests/grid/block_test/row_test.rs

@@ -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");
+    test.run_scripts(builder.build()).await;
+}
 
-    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(),
+#[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
         },
-    ];
-    test.run_scripts(scripts).await;
+        &vec![FACEBOOK, TWITTER].join(SELECTION_IDS_SEPARATOR),
+    );
+
+    test.run_scripts(builder.build()).await;
 }

+ 163 - 11
frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs

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

+ 30 - 9
frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs

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

+ 14 - 6
frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs

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

+ 4 - 0
shared-lib/flowy-sync/src/client_grid/grid_builder.rs

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