to-form.test.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import { beforeEach, describe, expect, it, vi } from 'vitest';
  6. import { toForm, toFormState } from '@/core/to-form';
  7. import { FormModel } from '@/core/form-model';
  8. describe('toForm', () => {
  9. let formModel: FormModel;
  10. beforeEach(() => {
  11. formModel = new FormModel();
  12. formModel.init({
  13. initialValues: {
  14. username: 'John',
  15. email: 'john@example.com',
  16. age: 30,
  17. },
  18. });
  19. });
  20. it('should convert FormModel to Form', () => {
  21. const form = toForm(formModel);
  22. expect(form).toBeDefined();
  23. expect(form.initialValues).toEqual({
  24. username: 'John',
  25. email: 'john@example.com',
  26. age: 30,
  27. });
  28. });
  29. it('should expose initialValues from model', () => {
  30. const form = toForm(formModel);
  31. expect(form.initialValues).toBe(formModel.initialValues);
  32. });
  33. it('should expose values getter from model', () => {
  34. const form = toForm(formModel);
  35. expect(form.values).toEqual({
  36. username: 'John',
  37. email: 'john@example.com',
  38. age: 30,
  39. });
  40. expect(form.values).toEqual(formModel.values);
  41. });
  42. it('should expose values setter to update model', () => {
  43. const form = toForm(formModel);
  44. const newValues = {
  45. username: 'Alice',
  46. email: 'alice@example.com',
  47. age: 25,
  48. };
  49. form.values = newValues;
  50. expect(formModel.values).toEqual(newValues);
  51. expect(form.values).toEqual(newValues);
  52. });
  53. it('should expose state as FormState', () => {
  54. const form = toForm(formModel);
  55. expect(form.state).toBeDefined();
  56. expect(form.state.isTouched).toBe(false);
  57. expect(form.state.isDirty).toBe(false);
  58. expect(form.state.invalid).toBe(false);
  59. expect(form.state.isValidating).toBe(false);
  60. });
  61. describe('getValueIn', () => {
  62. it('should get value by field name', () => {
  63. const form = toForm(formModel);
  64. expect(form.getValueIn('username')).toBe('John');
  65. expect(form.getValueIn('email')).toBe('john@example.com');
  66. expect(form.getValueIn('age')).toBe(30);
  67. });
  68. it('should get nested value by path', () => {
  69. formModel.values = {
  70. user: {
  71. profile: {
  72. name: 'Alice',
  73. age: 25,
  74. },
  75. },
  76. };
  77. const form = toForm(formModel);
  78. expect(form.getValueIn('user.profile.name')).toBe('Alice');
  79. expect(form.getValueIn('user.profile.age')).toBe(25);
  80. });
  81. it('should get array value by index', () => {
  82. formModel.values = {
  83. users: [{ name: 'Alice' }, { name: 'Bob' }],
  84. };
  85. const form = toForm(formModel);
  86. expect(form.getValueIn('users.0.name')).toBe('Alice');
  87. expect(form.getValueIn('users.1.name')).toBe('Bob');
  88. });
  89. it('should return undefined for non-existent path', () => {
  90. const form = toForm(formModel);
  91. expect(form.getValueIn('nonexistent')).toBeUndefined();
  92. });
  93. });
  94. describe('setValueIn', () => {
  95. it('should set value by field name', () => {
  96. const form = toForm(formModel);
  97. form.setValueIn('username', 'Bob');
  98. expect(formModel.values.username).toBe('Bob');
  99. expect(form.values.username).toBe('Bob');
  100. });
  101. it('should set nested value by path', () => {
  102. formModel.values = {
  103. user: {
  104. profile: {
  105. name: 'Alice',
  106. age: 25,
  107. },
  108. },
  109. };
  110. const form = toForm(formModel);
  111. form.setValueIn('user.profile.name', 'Charlie');
  112. expect(formModel.values.user.profile.name).toBe('Charlie');
  113. });
  114. it('should set array value by index', () => {
  115. formModel.values = {
  116. users: [{ name: 'Alice' }, { name: 'Bob' }],
  117. };
  118. const form = toForm(formModel);
  119. form.setValueIn('users.0.name', 'Charlie');
  120. expect(formModel.values.users[0].name).toBe('Charlie');
  121. });
  122. it('should create nested structure if not exists', () => {
  123. formModel.values = {};
  124. const form = toForm(formModel);
  125. form.setValueIn('user.profile.name', 'Alice');
  126. expect(formModel.values.user.profile.name).toBe('Alice');
  127. });
  128. });
  129. describe('validate', () => {
  130. it('should bind model validate method', () => {
  131. const form = toForm(formModel);
  132. expect(form.validate).toBeDefined();
  133. expect(typeof form.validate).toBe('function');
  134. });
  135. it('should call form validate method', async () => {
  136. const form = toForm(formModel);
  137. // Validate should be callable
  138. const result = await form.validate();
  139. // Without validators, should return empty object or undefined
  140. expect(result === undefined || Object.keys(result || {}).length === 0).toBe(true);
  141. });
  142. });
  143. it('should hide _formModel property (non-enumerable)', () => {
  144. const form = toForm(formModel);
  145. expect((form as any)._formModel).toBe(formModel);
  146. expect(Object.keys(form)).not.toContain('_formModel');
  147. });
  148. it('should preserve reactivity through getters', () => {
  149. const form = toForm(formModel);
  150. expect(form.values.username).toBe('John');
  151. formModel.values = { username: 'Alice' };
  152. expect(form.values.username).toBe('Alice');
  153. });
  154. it('should work with empty initialValues', () => {
  155. const emptyFormModel = new FormModel();
  156. emptyFormModel.init({});
  157. const form = toForm(emptyFormModel);
  158. expect(form.initialValues).toBeUndefined();
  159. expect(form.values).toBeUndefined();
  160. });
  161. });
  162. describe('toFormState', () => {
  163. let formModel: FormModel;
  164. beforeEach(() => {
  165. formModel = new FormModel();
  166. formModel.init({
  167. initialValues: {
  168. username: 'John',
  169. email: 'john@example.com',
  170. },
  171. });
  172. });
  173. it('should convert FormModelState to FormState', () => {
  174. const formState = toFormState(formModel.state);
  175. expect(formState).toBeDefined();
  176. expect(formState.isTouched).toBe(false);
  177. expect(formState.isDirty).toBe(false);
  178. expect(formState.invalid).toBe(false);
  179. expect(formState.isValidating).toBe(false);
  180. });
  181. it('should reflect isTouched state', () => {
  182. const formState = toFormState(formModel.state);
  183. expect(formState.isTouched).toBe(false);
  184. formModel.state.isTouched = true;
  185. expect(formState.isTouched).toBe(true);
  186. });
  187. it('should reflect isDirty state', () => {
  188. const formState = toFormState(formModel.state);
  189. expect(formState.isDirty).toBe(false);
  190. // Manually set dirty state
  191. formModel.state.isDirty = true;
  192. expect(formState.isDirty).toBe(true);
  193. });
  194. it('should reflect invalid state', () => {
  195. const formState = toFormState(formModel.state);
  196. expect(formState.invalid).toBe(false);
  197. formModel.state.invalid = true;
  198. expect(formState.invalid).toBe(true);
  199. });
  200. it('should reflect isValidating state', () => {
  201. const formState = toFormState(formModel.state);
  202. expect(formState.isValidating).toBe(false);
  203. formModel.state.isValidating = true;
  204. expect(formState.isValidating).toBe(true);
  205. });
  206. it('should expose errors from model state', () => {
  207. const formState = toFormState(formModel.state);
  208. expect(formState.errors).toBeUndefined();
  209. formModel.state.errors = {
  210. username: 'Username is required',
  211. email: 'Invalid email format',
  212. };
  213. expect(formState.errors).toEqual({
  214. username: 'Username is required',
  215. email: 'Invalid email format',
  216. });
  217. });
  218. it('should expose warnings from model state', () => {
  219. const formState = toFormState(formModel.state);
  220. expect(formState.warnings).toBeUndefined();
  221. formModel.state.warnings = {
  222. username: 'Username should be longer',
  223. email: 'Consider using a different email',
  224. };
  225. expect(formState.warnings).toEqual({
  226. username: 'Username should be longer',
  227. email: 'Consider using a different email',
  228. });
  229. });
  230. it('should preserve reactivity through getters', () => {
  231. const formState = toFormState(formModel.state);
  232. expect(formState.isTouched).toBe(false);
  233. formModel.state.isTouched = true;
  234. expect(formState.isTouched).toBe(true);
  235. });
  236. });