DatabaseTestHelper.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import {
  2. FieldType,
  3. SingleSelectTypeOptionPB,
  4. ViewLayoutTypePB,
  5. ViewPB,
  6. WorkspaceSettingPB,
  7. } from '../../../services/backend';
  8. import { FolderEventReadCurrentWorkspace } from '../../../services/backend/events/flowy-folder';
  9. import { AppBackendService } from '../../stores/effects/folder/app/app_bd_svc';
  10. import { DatabaseController } from '../../stores/effects/database/database_controller';
  11. import { RowInfo } from '../../stores/effects/database/row/row_cache';
  12. import { RowController } from '../../stores/effects/database/row/row_controller';
  13. import {
  14. CellControllerBuilder,
  15. DateCellController,
  16. NumberCellController,
  17. SelectOptionCellController,
  18. TextCellController,
  19. } from '../../stores/effects/database/cell/controller_builder';
  20. import { None, Option, Some } from 'ts-results';
  21. import { TypeOptionBackendService } from '../../stores/effects/database/field/type_option/type_option_bd_svc';
  22. import { DatabaseBackendService } from '../../stores/effects/database/database_bd_svc';
  23. import { FieldInfo } from '../../stores/effects/database/field/field_controller';
  24. import { TypeOptionController } from '../../stores/effects/database/field/type_option/type_option_controller';
  25. import { makeSingleSelectTypeOptionContext } from '../../stores/effects/database/field/type_option/type_option_context';
  26. import { SelectOptionBackendService } from '../../stores/effects/database/cell/select_option_bd_svc';
  27. // Create a database view for specific layout type
  28. // Do not use it production code. Just for testing
  29. export async function createTestDatabaseView(layout: ViewLayoutTypePB): Promise<ViewPB> {
  30. const workspaceSetting: WorkspaceSettingPB = await FolderEventReadCurrentWorkspace().then((result) => result.unwrap());
  31. const app = workspaceSetting.workspace.apps.items[0];
  32. const appService = new AppBackendService(app.id);
  33. return await appService.createView({ name: 'New Grid', layoutType: layout });
  34. }
  35. export async function openTestDatabase(viewId: string): Promise<DatabaseController> {
  36. return new DatabaseController(viewId);
  37. }
  38. export async function assertTextCell(
  39. fieldId: string,
  40. rowInfo: RowInfo,
  41. databaseController: DatabaseController,
  42. expectedContent: string
  43. ) {
  44. const cellController = await makeTextCellController(fieldId, rowInfo, databaseController).then((result) =>
  45. result.unwrap()
  46. );
  47. await cellController.subscribeChanged({
  48. onCellChanged: (value) => {
  49. const cellContent = value.unwrap();
  50. if (cellContent !== expectedContent) {
  51. throw Error('Text cell content is not match');
  52. }
  53. },
  54. });
  55. await cellController.getCellData();
  56. }
  57. export async function editTextCell(
  58. fieldId: string,
  59. rowInfo: RowInfo,
  60. databaseController: DatabaseController,
  61. content: string
  62. ) {
  63. const cellController = await makeTextCellController(fieldId, rowInfo, databaseController).then((result) =>
  64. result.unwrap()
  65. );
  66. await cellController.saveCellData(content);
  67. }
  68. export async function makeTextCellController(
  69. fieldId: string,
  70. rowInfo: RowInfo,
  71. databaseController: DatabaseController
  72. ): Promise<Option<TextCellController>> {
  73. const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.RichText, databaseController).then(
  74. (result) => result.unwrap()
  75. );
  76. return Some(builder.build() as TextCellController);
  77. }
  78. export async function makeNumberCellController(
  79. fieldId: string,
  80. rowInfo: RowInfo,
  81. databaseController: DatabaseController
  82. ): Promise<Option<NumberCellController>> {
  83. const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.Number, databaseController).then(
  84. (result) => result.unwrap()
  85. );
  86. return Some(builder.build() as NumberCellController);
  87. }
  88. export async function makeSingleSelectCellController(
  89. fieldId: string,
  90. rowInfo: RowInfo,
  91. databaseController: DatabaseController
  92. ): Promise<Option<SelectOptionCellController>> {
  93. const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.SingleSelect, databaseController).then(
  94. (result) => result.unwrap()
  95. );
  96. return Some(builder.build() as SelectOptionCellController);
  97. }
  98. export async function makeMultiSelectCellController(
  99. fieldId: string,
  100. rowInfo: RowInfo,
  101. databaseController: DatabaseController
  102. ): Promise<Option<SelectOptionCellController>> {
  103. const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.MultiSelect, databaseController).then(
  104. (result) => result.unwrap()
  105. );
  106. return Some(builder.build() as SelectOptionCellController);
  107. }
  108. export async function makeDateCellController(
  109. fieldId: string,
  110. rowInfo: RowInfo,
  111. databaseController: DatabaseController
  112. ): Promise<Option<DateCellController>> {
  113. const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.DateTime, databaseController).then(
  114. (result) => result.unwrap()
  115. );
  116. return Some(builder.build() as DateCellController);
  117. }
  118. export async function makeCellControllerBuilder(
  119. fieldId: string,
  120. rowInfo: RowInfo,
  121. fieldType: FieldType,
  122. databaseController: DatabaseController
  123. ): Promise<Option<CellControllerBuilder>> {
  124. const rowCache = databaseController.databaseViewCache.getRowCache();
  125. const cellCache = rowCache.getCellCache();
  126. const fieldController = databaseController.fieldController;
  127. const rowController = new RowController(rowInfo, fieldController, rowCache);
  128. const cellByFieldId = await rowController.loadCells();
  129. for (const cellIdentifier of cellByFieldId.values()) {
  130. const builder = new CellControllerBuilder(cellIdentifier, cellCache, fieldController);
  131. if (cellIdentifier.fieldId === fieldId) {
  132. return Some(builder);
  133. }
  134. }
  135. return None;
  136. }
  137. export function findFirstFieldInfoWithFieldType(rowInfo: RowInfo, fieldType: FieldType) {
  138. const fieldInfo = rowInfo.fieldInfos.find((element) => element.field.field_type === fieldType);
  139. if (fieldInfo === undefined) {
  140. return None;
  141. } else {
  142. return Some(fieldInfo);
  143. }
  144. }
  145. export async function assertFieldName(viewId: string, fieldId: string, fieldType: FieldType, expected: string) {
  146. const svc = new TypeOptionBackendService(viewId);
  147. const typeOptionPB = await svc.getTypeOption(fieldId, fieldType).then((result) => result.unwrap());
  148. if (typeOptionPB.field.name !== expected) {
  149. throw Error('Expect field name:' + expected + 'but receive:' + typeOptionPB.field.name);
  150. }
  151. }
  152. export async function assertNumberOfFields(viewId: string, expected: number) {
  153. const svc = new DatabaseBackendService(viewId);
  154. const databasePB = await svc.openDatabase().then((result) => result.unwrap());
  155. if (databasePB.fields.length !== expected) {
  156. throw Error('Expect number of fields:' + expected + 'but receive:' + databasePB.fields.length);
  157. }
  158. }
  159. export async function assertNumberOfRows(viewId: string, expected: number) {
  160. const svc = new DatabaseBackendService(viewId);
  161. const databasePB = await svc.openDatabase().then((result) => result.unwrap());
  162. if (databasePB.rows.length !== expected) {
  163. throw Error('Expect number of rows:' + expected + 'but receive:' + databasePB.rows.length);
  164. }
  165. }
  166. export async function assertNumberOfRowsInGroup(viewId: string, groupId: string, expected: number) {
  167. const svc = new DatabaseBackendService(viewId);
  168. await svc.openDatabase();
  169. const group = await svc.getGroup(groupId).then((result) => result.unwrap());
  170. if (group.rows.length !== expected) {
  171. throw Error('Expect number of rows in group:' + expected + 'but receive:' + group.rows.length);
  172. }
  173. }
  174. export async function createSingleSelectOptions(viewId: string, fieldInfo: FieldInfo, optionNames: string[]) {
  175. assert(fieldInfo.field.field_type === FieldType.SingleSelect, 'Only work on single select');
  176. const typeOptionController = new TypeOptionController(viewId, Some(fieldInfo));
  177. const singleSelectTypeOptionContext = makeSingleSelectTypeOptionContext(typeOptionController);
  178. const singleSelectTypeOptionPB: SingleSelectTypeOptionPB = await singleSelectTypeOptionContext
  179. .getTypeOption()
  180. .then((result) => result.unwrap());
  181. const backendSvc = new SelectOptionBackendService(viewId, fieldInfo.field.id);
  182. for (const optionName of optionNames) {
  183. const option = await backendSvc.createOption({ name: optionName }).then((result) => result.unwrap());
  184. singleSelectTypeOptionPB.options.splice(0, 0, option);
  185. }
  186. await singleSelectTypeOptionContext.setTypeOption(singleSelectTypeOptionPB);
  187. return singleSelectTypeOptionContext;
  188. }
  189. export function assert(condition: boolean, msg?: string) {
  190. if (!condition) {
  191. throw Error(msg);
  192. }
  193. }