DatabaseTestHelper.ts 9.3 KB

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