form-model.test.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import { beforeEach, describe, expect, it, vi } from 'vitest';
  2. import { ValidateTrigger } from '@/types';
  3. import { FormModel } from '@/core/form-model';
  4. describe('FormModel', () => {
  5. let formModel = new FormModel();
  6. describe('validate trigger', () => {
  7. beforeEach(() => {
  8. formModel.dispose();
  9. formModel = new FormModel();
  10. });
  11. it('do not validate when value change if validateTrigger is onBlur', async () => {
  12. formModel.init({ validateTrigger: ValidateTrigger.onBlur });
  13. const field = formModel.createField('x');
  14. field.originalValidate = vi.fn();
  15. vi.spyOn(field, 'originalValidate');
  16. field.value = 'some value';
  17. expect(field.originalValidate).not.toHaveBeenCalledOnce();
  18. });
  19. describe('delete field', () => {
  20. beforeEach(() => {
  21. formModel.dispose();
  22. formModel = new FormModel();
  23. });
  24. it('validate onChange', async () => {
  25. formModel.init({ initialValues: { parent: { child1: 1 } } });
  26. formModel.createField('parent');
  27. formModel.createField('parent.child1');
  28. expect(formModel.values.parent?.child1).toBe(1);
  29. formModel.deleteField('parent');
  30. expect(formModel.values.parent?.child1).toBeUndefined();
  31. expect(formModel.getField('parent')).toBeUndefined();
  32. expect(formModel.getField('parent.child1')).toBeUndefined();
  33. });
  34. });
  35. });
  36. describe('FormModel.validate', () => {
  37. beforeEach(() => {
  38. formModel.dispose();
  39. formModel = new FormModel();
  40. });
  41. it('should run validate on all matched names', async () => {
  42. formModel.init({
  43. validate: {
  44. 'a.b.*': () => 'error',
  45. },
  46. });
  47. const bField = formModel.createField('a.b');
  48. const xField = formModel.createField('a.b.x');
  49. formModel.setValueIn('a.b', { x: 1, y: 2 });
  50. const results = await formModel.validate();
  51. // 1. assert validate has been executed correctly
  52. expect(results.length).toEqual(2);
  53. expect(results[0].message).toEqual('error');
  54. expect(results[0].name).toEqual('a.b.x');
  55. expect(results[1].message).toEqual('error');
  56. expect(results[1].name).toEqual('a.b.y');
  57. // 2. assert form state has been set correctly
  58. expect(formModel.state?.errors?.['a.b.x']?.[0].message).toEqual('error');
  59. // 3. assert field state has been set correctly
  60. expect(xField.state?.errors?.['a.b.x']?.[0].message).toEqual('error');
  61. // 4. assert field state has been bubbled to its parent
  62. expect(bField.state?.errors?.['a.b.x']?.[0].message).toEqual('error');
  63. });
  64. it('should correctly set form errors state when field does not exist', async () => {
  65. formModel.init({
  66. validate: {
  67. 'a.b.*': () => 'error',
  68. },
  69. });
  70. formModel.setValueIn('a.b', { x: 1, y: 2 });
  71. await formModel.validate();
  72. expect(formModel.state?.errors?.['a.b.x']?.[0].message).toEqual('error');
  73. });
  74. it('should set form and field state correctly when run validate twice', async () => {
  75. formModel.init({
  76. validate: {
  77. 'a.b.*': ({ value }) => (typeof value === 'string' ? undefined : 'error'),
  78. },
  79. });
  80. const bField = formModel.createField('a.b');
  81. const xField = formModel.createField('a.b.x');
  82. formModel.setValueIn('a.b', { x: 1, y: 2 });
  83. let results = await formModel.validate();
  84. // both x y is string, so 2 errors
  85. expect(results.length).toEqual(2);
  86. expect(formModel.state?.errors?.['a.b.x']?.[0].message).toEqual('error');
  87. expect(formModel.state?.errors?.['a.b.y']?.[0].message).toEqual('error');
  88. expect(xField.state?.errors?.['a.b.x']?.[0].message).toEqual('error');
  89. expect(bField.state?.errors?.['a.b.x']?.[0].message).toEqual('error');
  90. formModel.setValueIn('a.b', { x: '1', y: '2' });
  91. results = await formModel.validate();
  92. expect(results.length).toEqual(0);
  93. expect(formModel.state?.errors?.['a.b.x']).toEqual([]);
  94. expect(formModel.state?.errors?.['a.b.y']).toEqual([]);
  95. });
  96. });
  97. describe('FormModel set/get values', () => {
  98. beforeEach(() => {
  99. formModel.dispose();
  100. formModel = new FormModel();
  101. vi.spyOn(formModel.onFormValuesInitEmitter, 'fire');
  102. vi.spyOn(formModel.onFormValuesChangeEmitter, 'fire');
  103. vi.spyOn(formModel.onFormValuesUpdatedEmitter, 'fire');
  104. });
  105. it('should set value for root path', () => {
  106. formModel.init({
  107. initialValues: {
  108. a: 1,
  109. },
  110. });
  111. formModel.values = { a: 2 };
  112. expect(formModel.values).toEqual({ a: 2 });
  113. });
  114. it('should set initialValues and fire init and updated events', async () => {
  115. formModel.init({
  116. initialValues: {
  117. a: 1,
  118. },
  119. });
  120. expect(formModel.values).toEqual({ a: 1 });
  121. expect(formModel.onFormValuesInitEmitter.fire).toHaveBeenCalledWith({
  122. values: {
  123. a: 1,
  124. },
  125. name: '',
  126. });
  127. expect(formModel.onFormValuesUpdatedEmitter.fire).toHaveBeenCalledWith({
  128. values: {
  129. a: 1,
  130. },
  131. name: '',
  132. });
  133. });
  134. it('should set initialValues in certain path and fire change', async () => {
  135. formModel.init({
  136. initialValues: {
  137. a: 1,
  138. },
  139. });
  140. formModel.setInitValueIn('b', 2);
  141. expect(formModel.values).toEqual({ a: 1, b: 2 });
  142. expect(formModel.onFormValuesInitEmitter.fire).toHaveBeenCalledWith({
  143. values: {
  144. a: 1,
  145. b: 2,
  146. },
  147. prevValues: {
  148. a: 1,
  149. },
  150. name: 'b',
  151. });
  152. expect(formModel.onFormValuesUpdatedEmitter.fire).toHaveBeenCalledWith({
  153. values: {
  154. a: 1,
  155. b: 2,
  156. },
  157. prevValues: {
  158. a: 1,
  159. },
  160. name: 'b',
  161. });
  162. });
  163. it('should not set initialValues in certain path if value exists', async () => {
  164. formModel.init({
  165. initialValues: {
  166. a: 1,
  167. },
  168. });
  169. formModel.setInitValueIn('a', 2);
  170. expect(formModel.values).toEqual({ a: 1 });
  171. // 仅在初始化时调用一次,setInitValueIn 没有调用
  172. expect(formModel.onFormValuesInitEmitter.fire).toHaveBeenCalledTimes(1);
  173. expect(formModel.onFormValuesUpdatedEmitter.fire).toHaveBeenCalledTimes(1);
  174. });
  175. it('should set values in certain path and fire change and updated events', async () => {
  176. formModel.init({
  177. initialValues: {
  178. a: 1,
  179. },
  180. });
  181. formModel.setValueIn('a', 2);
  182. expect(formModel.values).toEqual({ a: 2 });
  183. // 仅在初始化时调用一次,setInitValueIn 没有调用
  184. expect(formModel.onFormValuesChangeEmitter.fire).toHaveBeenCalledTimes(1);
  185. // 初始化一次,变更值一次,所以是两次
  186. expect(formModel.onFormValuesUpdatedEmitter.fire).toHaveBeenCalledTimes(2);
  187. expect(formModel.onFormValuesChangeEmitter.fire).toHaveBeenCalledWith({
  188. values: {
  189. a: 2,
  190. },
  191. prevValues: {
  192. a: 1,
  193. },
  194. name: 'a',
  195. });
  196. expect(formModel.onFormValuesUpdatedEmitter.fire).toHaveBeenCalledWith({
  197. values: {
  198. a: 2,
  199. },
  200. prevValues: {
  201. a: 1,
  202. },
  203. name: 'a',
  204. });
  205. });
  206. });
  207. });