to-field-array.test.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import { beforeEach, describe, expect, it } from 'vitest';
  6. import { toFieldArray } from '@/core/to-field-array';
  7. import { FormModel } from '@/core/form-model';
  8. import { FieldArrayModel } from '@/core/field-array-model';
  9. describe('toFieldArray', () => {
  10. let formModel: FormModel;
  11. let fieldArrayModel: FieldArrayModel;
  12. beforeEach(() => {
  13. formModel = new FormModel();
  14. formModel.init({});
  15. formModel.createFieldArray('users');
  16. fieldArrayModel = formModel.getField<FieldArrayModel>('users')!;
  17. });
  18. it('should convert FieldArrayModel to FieldArray', () => {
  19. const fieldArray = toFieldArray(fieldArrayModel);
  20. expect(fieldArray).toBeDefined();
  21. expect(fieldArray.name).toBe('users');
  22. expect(fieldArray.value === undefined || Array.isArray(fieldArray.value)).toBe(true);
  23. });
  24. it('should expose key property from model id', () => {
  25. const fieldArray = toFieldArray(fieldArrayModel);
  26. expect(fieldArray.key).toBe(fieldArrayModel.id);
  27. });
  28. it('should expose name property from model path', () => {
  29. const fieldArray = toFieldArray(fieldArrayModel);
  30. expect(fieldArray.name).toBe('users');
  31. expect(fieldArray.name).toBe(fieldArrayModel.path.toString());
  32. });
  33. it('should expose value property from model value', () => {
  34. fieldArrayModel.value = [{ name: 'Alice' }, { name: 'Bob' }];
  35. const fieldArray = toFieldArray(fieldArrayModel);
  36. expect(fieldArray.value).toEqual([{ name: 'Alice' }, { name: 'Bob' }]);
  37. expect(fieldArray.value).toBe(fieldArrayModel.value);
  38. });
  39. it('should update model value via onChange', () => {
  40. const fieldArray = toFieldArray(fieldArrayModel);
  41. const newValue = [{ name: 'Charlie' }];
  42. fieldArray.onChange(newValue);
  43. expect(fieldArrayModel.value).toEqual(newValue);
  44. expect(fieldArray.value).toEqual(newValue);
  45. });
  46. it('should map over array elements correctly', () => {
  47. fieldArrayModel.value = [{ name: 'Alice' }, { name: 'Bob' }];
  48. const fieldArray = toFieldArray(fieldArrayModel);
  49. const mapped = fieldArray.map((field, index) => {
  50. expect(field).toBeDefined();
  51. expect(field.name).toBe(`users.${index}`);
  52. return field.value;
  53. });
  54. expect(mapped).toHaveLength(2);
  55. expect(mapped).toEqual([{ name: 'Alice' }, { name: 'Bob' }]);
  56. });
  57. it('should append new item and return Field', () => {
  58. const fieldArray = toFieldArray(fieldArrayModel);
  59. const newItem = { name: 'Charlie' };
  60. const newField = fieldArray.append(newItem);
  61. expect(newField).toBeDefined();
  62. expect(newField.name).toBe('users.0');
  63. expect(newField.value).toEqual(newItem);
  64. expect(fieldArrayModel.value).toEqual([newItem]);
  65. });
  66. it('should delete item by index', () => {
  67. fieldArrayModel.value = [{ name: 'Alice' }, { name: 'Bob' }, { name: 'Charlie' }];
  68. const fieldArray = toFieldArray(fieldArrayModel);
  69. fieldArray.delete(1);
  70. expect(fieldArrayModel.value).toEqual([{ name: 'Alice' }, { name: 'Charlie' }]);
  71. expect(fieldArray.value).toEqual([{ name: 'Alice' }, { name: 'Charlie' }]);
  72. });
  73. it('should remove item by index (same as delete)', () => {
  74. fieldArrayModel.value = [{ name: 'Alice' }, { name: 'Bob' }, { name: 'Charlie' }];
  75. const fieldArray = toFieldArray(fieldArrayModel);
  76. fieldArray.remove(1);
  77. expect(fieldArrayModel.value).toEqual([{ name: 'Alice' }, { name: 'Charlie' }]);
  78. expect(fieldArray.value).toEqual([{ name: 'Alice' }, { name: 'Charlie' }]);
  79. });
  80. it('should swap items at two indices', () => {
  81. fieldArrayModel.value = [{ name: 'Alice' }, { name: 'Bob' }, { name: 'Charlie' }];
  82. const fieldArray = toFieldArray(fieldArrayModel);
  83. fieldArray.swap(0, 2);
  84. expect(fieldArrayModel.value).toEqual([
  85. { name: 'Charlie' },
  86. { name: 'Bob' },
  87. { name: 'Alice' },
  88. ]);
  89. expect(fieldArray.value).toEqual([{ name: 'Charlie' }, { name: 'Bob' }, { name: 'Alice' }]);
  90. });
  91. it('should move item from one index to another', () => {
  92. fieldArrayModel.value = [{ name: 'Alice' }, { name: 'Bob' }, { name: 'Charlie' }];
  93. const fieldArray = toFieldArray(fieldArrayModel);
  94. fieldArray.move(0, 2);
  95. expect(fieldArrayModel.value).toEqual([
  96. { name: 'Bob' },
  97. { name: 'Charlie' },
  98. { name: 'Alice' },
  99. ]);
  100. expect(fieldArray.value).toEqual([{ name: 'Bob' }, { name: 'Charlie' }, { name: 'Alice' }]);
  101. });
  102. it('should hide _fieldModel property (non-enumerable)', () => {
  103. const fieldArray = toFieldArray(fieldArrayModel);
  104. // _fieldModel should exist but not be enumerable
  105. expect((fieldArray as any)._fieldModel).toBe(fieldArrayModel);
  106. expect(Object.keys(fieldArray)).not.toContain('_fieldModel');
  107. });
  108. it('should support complex nested operations', () => {
  109. const fieldArray = toFieldArray(fieldArrayModel);
  110. // Append multiple items
  111. fieldArray.append({ name: 'Alice', age: 30 });
  112. fieldArray.append({ name: 'Bob', age: 25 });
  113. fieldArray.append({ name: 'Charlie', age: 35 });
  114. expect(fieldArray.value).toHaveLength(3);
  115. // Map and modify
  116. const names = fieldArray.map((field) => field.value.name);
  117. expect(names).toEqual(['Alice', 'Bob', 'Charlie']);
  118. // Swap
  119. fieldArray.swap(0, 1);
  120. expect(fieldArray.value[0].name).toBe('Bob');
  121. expect(fieldArray.value[1].name).toBe('Alice');
  122. // Remove
  123. fieldArray.remove(2);
  124. expect(fieldArray.value).toHaveLength(2);
  125. // Move
  126. fieldArray.move(1, 0);
  127. expect(fieldArray.value[0].name).toBe('Alice');
  128. expect(fieldArray.value[1].name).toBe('Bob');
  129. });
  130. it('should work with empty array', () => {
  131. const fieldArray = toFieldArray(fieldArrayModel);
  132. // Value might be undefined or empty array initially
  133. expect(fieldArray.value === undefined || Array.isArray(fieldArray.value)).toBe(true);
  134. const mapped = fieldArray.map((field) => field.value);
  135. expect(Array.isArray(mapped)).toBe(true);
  136. expect(mapped.length === 0 || mapped.length > 0).toBe(true);
  137. });
  138. it('should preserve reactivity through getters', () => {
  139. const fieldArray = toFieldArray(fieldArrayModel);
  140. // Initial value might be undefined or empty array
  141. expect(fieldArray.value === undefined || Array.isArray(fieldArray.value)).toBe(true);
  142. // Modify through model
  143. fieldArrayModel.value = [{ name: 'Alice' }];
  144. // Should reflect in fieldArray (getter)
  145. expect(fieldArray.value).toEqual([{ name: 'Alice' }]);
  146. // Modify through fieldArray
  147. fieldArray.onChange([{ name: 'Bob' }]);
  148. // Should reflect in model
  149. expect(fieldArrayModel.value).toEqual([{ name: 'Bob' }]);
  150. });
  151. });