DatabaseTestHelper.ts 9.1 KB

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