field-array-model.test.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. import { beforeEach, describe, expect, it, vi } from 'vitest';
  2. import { Errors, ValidateTrigger, Warnings } from '@/types';
  3. import { FormModel } from '@/core/form-model';
  4. import { type FieldArrayModel } from '@/core/field-array-model';
  5. describe('FormArrayModel', () => {
  6. let formModel = new FormModel();
  7. describe('children', () => {
  8. let arrayField: FieldArrayModel;
  9. beforeEach(() => {
  10. formModel.dispose();
  11. formModel = new FormModel();
  12. // 创建数组
  13. formModel.createFieldArray('arr');
  14. const field = formModel.getField<FieldArrayModel>('arr');
  15. field!.append('a');
  16. field!.append('b');
  17. field!.append('c');
  18. arrayField = field!;
  19. });
  20. it('can get children', () => {
  21. expect(arrayField.children.length).toBe(3);
  22. });
  23. });
  24. describe('append & delete', () => {
  25. let arrayField: FieldArrayModel;
  26. let arrEffect = vi.fn();
  27. let aEffect = vi.fn();
  28. let bEffect = vi.fn();
  29. let cEffect = vi.fn();
  30. let appendEffect = vi.fn();
  31. let deleteEffect = vi.fn();
  32. let aValidate = vi.fn();
  33. let bValidate = vi.fn();
  34. let cValidate = vi.fn();
  35. beforeEach(() => {
  36. formModel.dispose();
  37. formModel = new FormModel();
  38. arrEffect = vi.fn();
  39. aEffect = vi.fn();
  40. bEffect = vi.fn();
  41. cEffect = vi.fn();
  42. appendEffect = vi.fn();
  43. deleteEffect = vi.fn();
  44. aValidate = vi.fn();
  45. bValidate = vi.fn();
  46. cValidate = vi.fn();
  47. formModel.init({
  48. validateTrigger: ValidateTrigger.onChange,
  49. validate: {
  50. ['arr.0']: aValidate,
  51. ['arr.1']: bValidate,
  52. ['arr.2']: cValidate,
  53. },
  54. });
  55. // 创建其他field, 用于测试其他元素不会被影响
  56. formModel.createField('other');
  57. // 创建数组
  58. formModel.createFieldArray('arr');
  59. const field = formModel.getField<FieldArrayModel>('arr');
  60. const a = field!.append('a');
  61. const b = field!.append('b');
  62. const c = field!.append('c');
  63. arrayField = field!;
  64. arrayField.onValueChange(arrEffect);
  65. arrayField.onAppend(appendEffect);
  66. arrayField.onDelete(deleteEffect);
  67. a.onValueChange(aEffect);
  68. b.onValueChange(bEffect);
  69. c.onValueChange(cEffect);
  70. });
  71. it('append', async () => {
  72. vi.spyOn(arrayField, 'validate');
  73. arrayField.append('d');
  74. expect(arrayField.children.length).toBe(4);
  75. expect(formModel.getField('other')).toBeDefined();
  76. expect(arrEffect).toHaveBeenCalledTimes(1);
  77. expect(appendEffect).toHaveBeenCalledTimes(1);
  78. expect(arrayField.validate).toHaveBeenCalledTimes(1);
  79. });
  80. it('should fire OnFormValueChange event for arr when append', () => {
  81. vi.spyOn(formModel.onFormValuesInitEmitter, 'fire');
  82. vi.spyOn(formModel.onFormValuesChangeEmitter, 'fire');
  83. arrayField.append('d');
  84. expect(formModel.onFormValuesChangeEmitter.fire).toHaveBeenCalledWith({
  85. values: {
  86. arr: ['a', 'b', 'c', 'd'],
  87. },
  88. prevValues: {
  89. arr: ['a', 'b', 'c'],
  90. },
  91. name: 'arr',
  92. options: {
  93. action: 'array-append',
  94. indexes: [3],
  95. },
  96. });
  97. expect(formModel.onFormValuesInitEmitter.fire).toHaveBeenCalledWith({
  98. values: {
  99. arr: ['a', 'b', 'c', 'd'],
  100. },
  101. prevValues: {
  102. arr: ['a', 'b', 'c'],
  103. },
  104. name: 'arr.3',
  105. });
  106. });
  107. it('delete first element', () => {
  108. vi.spyOn(formModel.onFormValuesChangeEmitter, 'fire');
  109. vi.spyOn(arrayField, 'validate');
  110. vi.spyOn(arrayField.onValueChangeEmitter, 'fire');
  111. arrayField.delete(0);
  112. // assert value
  113. expect(arrayField.children.length).toBe(2);
  114. expect(arrayField.children[0].value).toBe('b');
  115. expect(arrayField.children[1].value).toBe('c');
  116. expect(formModel.getField('other')).toBeDefined();
  117. // assert change events
  118. expect(arrayField.onValueChangeEmitter.fire).toHaveBeenCalledTimes(1);
  119. expect(aEffect).toHaveBeenCalledTimes(1);
  120. expect(bEffect).toHaveBeenCalledTimes(1);
  121. expect(cEffect).toHaveBeenCalledTimes(1);
  122. expect(deleteEffect).toHaveBeenCalledTimes(1);
  123. expect(formModel.onFormValuesChangeEmitter.fire).toHaveBeenCalledWith({
  124. values: {
  125. arr: ['b', 'c'],
  126. },
  127. prevValues: {
  128. arr: ['a', 'b', 'c'],
  129. },
  130. name: 'arr',
  131. options: {
  132. action: 'array-splice',
  133. indexes: [0],
  134. },
  135. });
  136. expect(formModel.onFormValuesChangeEmitter.fire).toHaveBeenCalledTimes(1);
  137. // assert validate trigger
  138. expect(arrayField.validate).toHaveBeenCalledTimes(1);
  139. expect(aValidate).not.toHaveBeenCalled();
  140. expect(bValidate).not.toHaveBeenCalled();
  141. expect(cValidate).not.toHaveBeenCalled();
  142. });
  143. it('delete middle element', () => {
  144. vi.spyOn(arrayField, 'validate');
  145. vi.spyOn(arrayField.onValueChangeEmitter, 'fire');
  146. arrayField.delete(1);
  147. // assert values
  148. expect(arrayField.children.length).toBe(2);
  149. expect(arrayField.children[0].value).toBe('a');
  150. expect(arrayField.children[1].value).toBe('c');
  151. expect(formModel.getField('other')).toBeDefined();
  152. // assert change events
  153. expect(arrayField.onValueChangeEmitter.fire).toHaveBeenCalledTimes(1);
  154. expect(aEffect).not.toHaveBeenCalled();
  155. expect(bEffect).toHaveBeenCalledTimes(1);
  156. expect(cEffect).toHaveBeenCalledTimes(1);
  157. // assert validate trigger
  158. expect(bValidate).not.toHaveBeenCalled();
  159. expect(cValidate).not.toHaveBeenCalled();
  160. expect(arrayField.validate).toHaveBeenCalledTimes(1);
  161. });
  162. it('delete last element', () => {
  163. arrayField.delete(2);
  164. expect(arrayField.children.length).toBe(2);
  165. expect(arrayField.children[0].value).toBe('a');
  166. expect(arrayField.children[1].value).toBe('b');
  167. expect(formModel.getField('other')).toBeDefined();
  168. expect(arrEffect).toHaveBeenCalled();
  169. expect(cEffect).toHaveBeenCalled();
  170. });
  171. it('delete element which has nested field', () => {
  172. vi.spyOn(arrayField, 'validate');
  173. const axField = formModel.createField('arr.0.x');
  174. const bxField = formModel.createField('arr.1.x');
  175. vi.spyOn(axField, 'validate');
  176. vi.spyOn(bxField, 'validate');
  177. formModel.setValueIn('arr.0', { x: 1 });
  178. formModel.setValueIn('arr.1', { x: 2 });
  179. expect(arrayField.value).toEqual([{ x: 1 }, { x: 2 }, 'c']);
  180. arrayField.delete(0);
  181. expect(arrayField.value).toEqual([{ x: 2 }, 'c']);
  182. // assert change events
  183. expect(aEffect).toHaveBeenCalledTimes(2); // setValueIn 触发一次, delete 触发一次
  184. expect(bEffect).toHaveBeenCalledTimes(2); // setValueIn 触发一次, delete 触发一次
  185. expect(cEffect).toHaveBeenCalledTimes(1);
  186. // assert validate trigger
  187. expect(aValidate).toHaveBeenCalledTimes(1); // setValueIn 触发一次, delete 不会触发
  188. expect(bValidate).toHaveBeenCalledTimes(1); // setValueIn 触发一次, delete 不会触发
  189. expect(cValidate).not.toHaveBeenCalled();
  190. expect(axField.validate).toHaveBeenCalledTimes(1); // setValueIn 触发一次, delete 不会触发
  191. expect(bxField.validate).toHaveBeenCalledTimes(1); // setValueIn 触发一次, delete 不会触发
  192. });
  193. it('more elements delete', () => {
  194. /**
  195. * 数组为 [a,b,c,d]
  196. * 删除 b
  197. * 希望数组值为 [a,c,d]
  198. * 希望formModel中的field也正确对应
  199. */
  200. arrayField.append('d');
  201. vi.spyOn(arrayField, 'validate');
  202. arrayField.delete(1);
  203. // assert values
  204. expect(arrayField.children.length).toBe(3);
  205. expect(arrayField.children[0].value).toBe('a');
  206. expect(arrayField.children[1].value).toBe('c');
  207. expect(arrayField.children[2].value).toBe('d');
  208. expect(formModel.getField('arr.2')?.value).toBe('d');
  209. expect(formModel.getField('other')).toBeDefined();
  210. // assert value change events
  211. expect(arrEffect).toHaveBeenCalled();
  212. expect(bEffect).toHaveBeenCalled();
  213. expect(cEffect).toHaveBeenCalled();
  214. expect(arrayField.validate).toHaveBeenCalledTimes(1);
  215. });
  216. });
  217. describe('_splice', () => {
  218. let arrayField: FieldArrayModel;
  219. let aEffect = vi.fn();
  220. let bEffect = vi.fn();
  221. let cEffect = vi.fn();
  222. let dEffect = vi.fn();
  223. let eEffect = vi.fn();
  224. let aValidate = vi.fn();
  225. let bValidate = vi.fn();
  226. let cValidate = vi.fn();
  227. let dValidate = vi.fn();
  228. let eValidate = vi.fn();
  229. beforeEach(() => {
  230. formModel.dispose();
  231. formModel = new FormModel();
  232. aEffect = vi.fn();
  233. bEffect = vi.fn();
  234. cEffect = vi.fn();
  235. dEffect = vi.fn();
  236. eEffect = vi.fn();
  237. aValidate = vi.fn();
  238. bValidate = vi.fn();
  239. cValidate = vi.fn();
  240. dValidate = vi.fn();
  241. eValidate = vi.fn();
  242. formModel.createFieldArray('arr');
  243. formModel.init({
  244. validateTrigger: ValidateTrigger.onChange,
  245. validate: {
  246. ['arr.0']: aValidate,
  247. ['arr.1']: bValidate,
  248. ['arr.2']: cValidate,
  249. ['arr.3']: dValidate,
  250. ['arr.4']: eValidate,
  251. },
  252. });
  253. const field = formModel.getField<FieldArrayModel>('arr');
  254. const aField = field!.append('a');
  255. const bField = field!.append('b');
  256. const cField = field!.append('c');
  257. const dField = field!.append('d');
  258. const eField = field!.append('e');
  259. aField.onValueChange(aEffect);
  260. bField.onValueChange(bEffect);
  261. cField.onValueChange(cEffect);
  262. dField.onValueChange(dEffect);
  263. eField.onValueChange(eEffect);
  264. arrayField = field!;
  265. vi.spyOn(arrayField, 'validate');
  266. vi.spyOn(arrayField.onValueChangeEmitter, 'fire');
  267. });
  268. it('should throw error when delete count exceeds array length', () => {
  269. expect(() => {
  270. arrayField._splice(0, 6);
  271. }).toThrowError();
  272. });
  273. it('should throw error when delete in empty array', () => {
  274. arrayField._splice(0, 5);
  275. expect(() => {
  276. arrayField._splice(0);
  277. }).toThrowError();
  278. });
  279. it('splice first 2', () => {
  280. arrayField._splice(0, 2);
  281. // assert values
  282. expect(arrayField.children.length).toBe(3);
  283. expect(arrayField.children[0].value).toBe('c');
  284. expect(arrayField.children[1].value).toBe('d');
  285. expect(arrayField.children[2].value).toBe('e');
  286. // assert value change events
  287. expect(arrayField.onValueChangeEmitter.fire).toHaveBeenCalledTimes(1);
  288. expect(aEffect).toHaveBeenCalledTimes(1);
  289. expect(bEffect).toHaveBeenCalledTimes(1);
  290. expect(cEffect).toHaveBeenCalledTimes(1);
  291. expect(dEffect).toHaveBeenCalledTimes(1);
  292. expect(eEffect).toHaveBeenCalledTimes(1);
  293. // assert validate trigger
  294. expect(arrayField.validate).toHaveBeenCalledTimes(1);
  295. expect(aValidate).not.toHaveBeenCalled();
  296. expect(bValidate).not.toHaveBeenCalled();
  297. expect(cValidate).not.toHaveBeenCalled();
  298. expect(dValidate).not.toHaveBeenCalled();
  299. expect(eValidate).not.toHaveBeenCalled();
  300. });
  301. it('splice last 2', () => {
  302. arrayField._splice(3, 2);
  303. // assert values
  304. expect(arrayField.children.length).toBe(3);
  305. expect(arrayField.children[0].value).toBe('a');
  306. expect(arrayField.children[1].value).toBe('b');
  307. expect(arrayField.children[2].value).toBe('c');
  308. // assert value change events
  309. expect(arrayField.onValueChangeEmitter.fire).toHaveBeenCalledTimes(1);
  310. expect(aEffect).not.toHaveBeenCalled();
  311. expect(bEffect).not.toHaveBeenCalled();
  312. expect(cEffect).not.toHaveBeenCalled();
  313. expect(dEffect).toHaveBeenCalledTimes(1);
  314. expect(eEffect).toHaveBeenCalledTimes(1);
  315. // assert validate trigger
  316. expect(arrayField.validate).toHaveBeenCalledTimes(1);
  317. expect(aValidate).not.toHaveBeenCalled();
  318. expect(bValidate).not.toHaveBeenCalled();
  319. expect(cValidate).not.toHaveBeenCalled();
  320. expect(dValidate).not.toHaveBeenCalled();
  321. expect(eValidate).not.toHaveBeenCalled();
  322. });
  323. it('splice middle elements', () => {
  324. arrayField._splice(1, 2);
  325. // assert values
  326. expect(arrayField.children.length).toBe(3);
  327. expect(arrayField.children[0].value).toBe('a');
  328. expect(arrayField.children[1].value).toBe('d');
  329. expect(arrayField.children[2].value).toBe('e');
  330. // assert value change events
  331. expect(arrayField.onValueChangeEmitter.fire).toHaveBeenCalledTimes(1);
  332. expect(aEffect).not.toHaveBeenCalled();
  333. expect(bEffect).toHaveBeenCalledTimes(1);
  334. expect(cEffect).toHaveBeenCalledTimes(1);
  335. expect(dEffect).toHaveBeenCalledTimes(1);
  336. expect(eEffect).toHaveBeenCalledTimes(1);
  337. // assert validate trigger
  338. expect(arrayField.validate).toHaveBeenCalledTimes(1);
  339. expect(aValidate).not.toHaveBeenCalled();
  340. expect(bValidate).not.toHaveBeenCalled();
  341. expect(cValidate).not.toHaveBeenCalled();
  342. expect(dValidate).not.toHaveBeenCalled();
  343. expect(eValidate).not.toHaveBeenCalled();
  344. });
  345. it('splice all elements', () => {
  346. arrayField._splice(0, 5);
  347. expect(arrayField.children.length).toBe(0);
  348. expect(arrayField.value).toEqual([]);
  349. // assert value change events
  350. expect(arrayField.onValueChangeEmitter.fire).toHaveBeenCalledTimes(1);
  351. expect(aEffect).toHaveBeenCalledTimes(1);
  352. expect(bEffect).toHaveBeenCalledTimes(1);
  353. expect(cEffect).toHaveBeenCalledTimes(1);
  354. expect(dEffect).toHaveBeenCalledTimes(1);
  355. expect(eEffect).toHaveBeenCalledTimes(1);
  356. // assert validate trigger
  357. expect(arrayField.validate).toHaveBeenCalledTimes(1);
  358. expect(aValidate).not.toHaveBeenCalled();
  359. expect(bValidate).not.toHaveBeenCalled();
  360. expect(cValidate).not.toHaveBeenCalled();
  361. expect(dValidate).not.toHaveBeenCalled();
  362. expect(eValidate).not.toHaveBeenCalled();
  363. });
  364. });
  365. describe('State check when _splice', () => {
  366. beforeEach(() => {
  367. formModel.dispose();
  368. formModel = new FormModel();
  369. });
  370. it('should keep state of rest fields after delete a prev field', () => {
  371. const arrayField = formModel.createFieldArray('arr');
  372. formModel.init({
  373. validateTrigger: ValidateTrigger.onChange,
  374. });
  375. arrayField!.append('a');
  376. arrayField!.append('b');
  377. arrayField!.append('c');
  378. // 设置第1项的state
  379. const aFieldModel = formModel.getField('arr.1');
  380. aFieldModel!.state.errors = {
  381. 'arr.1': [{ name: 'arr.1', message: 'error' }],
  382. } as unknown as Errors;
  383. aFieldModel!.state.warnings = {
  384. 'arr.1': [{ name: 'arr.1', message: 'warning' }],
  385. } as unknown as Warnings;
  386. // 删除第0项
  387. arrayField._splice(0);
  388. // 原第一项变为第0项且他的state 被保留了, 且errors 中的路径标识也更新了
  389. expect(formModel.getField('arr.0')!.state.errors).toEqual({
  390. 'arr.0': [{ name: 'arr.0', message: 'error' }],
  391. });
  392. expect(formModel.getField('arr.0')!.state.warnings).toEqual({
  393. 'arr.0': [{ name: 'arr.0', message: 'warning' }],
  394. });
  395. expect(formModel.getField('arr.2')).toBeUndefined();
  396. });
  397. it('should keep state of rest fields after delete a prev field, when nested field', () => {
  398. const arrayField = formModel.createFieldArray('arr');
  399. formModel.init({
  400. validateTrigger: ValidateTrigger.onChange,
  401. initialValues: {
  402. arr: [
  403. { x: 1, y: 2 },
  404. { x: 0, y: 0 },
  405. { x: 0, y: 0 },
  406. ],
  407. },
  408. });
  409. formModel.createField('arr.0');
  410. formModel.createField('arr.0.x');
  411. formModel.createField('arr.0.y');
  412. formModel.createField('arr.1');
  413. formModel.createField('arr.1.x');
  414. formModel.createField('arr.1.y');
  415. formModel.createField('arr.2');
  416. formModel.createField('arr.2.x');
  417. formModel.createField('arr.2.y');
  418. // 设置第1项的state
  419. const aFieldModel = formModel.getField('arr.1.x');
  420. aFieldModel!.state.errors = {
  421. 'arr.1.x': [{ name: 'arr.1.x', message: 'error' }],
  422. } as unknown as Errors;
  423. // 删除第0项
  424. arrayField._splice(0);
  425. // 原第一项变为第0项且他的state errors 被保留了, 且errors 中的路径标识也更新了
  426. expect(formModel.getField('arr.0')!.state.errors).toEqual({
  427. 'arr.0.x': [{ name: 'arr.0.x', message: 'error' }],
  428. });
  429. expect(formModel.getField('arr.0.x')!.state.errors).toEqual({
  430. 'arr.0.x': [{ name: 'arr.0.x', message: 'error' }],
  431. });
  432. expect(formModel.getField('arr.2')).toBeUndefined();
  433. expect(formModel.getField('arr.2.x')).toBeUndefined();
  434. expect(formModel.getField('arr.2.y')).toBeUndefined();
  435. });
  436. it('should align errors and warnings state with existing field in fieldMap ', () => {
  437. const arrayField = formModel.createFieldArray('arr');
  438. formModel.init({
  439. validateTrigger: ValidateTrigger.onChange,
  440. initialValues: {
  441. arr: [
  442. { x: 1, y: 2 },
  443. { x: 0, y: 0 },
  444. { x: 0, y: 0 },
  445. ],
  446. },
  447. });
  448. const field0 = formModel.createField('arr.0');
  449. const field0x = formModel.createField('arr.0.x');
  450. const field0y = formModel.createField('arr.0.y');
  451. const field1 = formModel.createField('arr.1');
  452. const field1x = formModel.createField('arr.1.x');
  453. const field1y = formModel.createField('arr.1.y');
  454. const field2 = formModel.createField('arr.2');
  455. const field2x = formModel.createField('arr.2.x');
  456. const field2y = formModel.createField('arr.2.y');
  457. field0x.state.errors = {
  458. 'arr.0.x': [{ name: 'arr.0.x', message: 'error' }],
  459. } as unknown as Errors;
  460. field0x.bubbleState();
  461. field1x.state.errors = {
  462. 'arr.1.x': [{ name: 'arr.1.x', message: 'error' }],
  463. } as unknown as Errors;
  464. field1x.bubbleState();
  465. field2x.state.errors = {
  466. 'arr.2.x': [{ name: 'arr.2.x', message: 'error' }],
  467. } as unknown as Errors;
  468. // 删除第0项
  469. arrayField._splice(0);
  470. expect(formModel.state.errors['arr.0.x']).toEqual([{ name: 'arr.0.x', message: 'error' }]);
  471. expect(formModel.state.errors['arr.1.x']).toEqual([{ name: 'arr.1.x', message: 'error' }]);
  472. expect(formModel.state.errors['arr.2.x']).toBeUndefined();
  473. expect(field0.state.errors['arr.0.x']).toEqual([{ name: 'arr.0.x', message: 'error' }]);
  474. expect(field1.state.errors['arr.1.x']).toEqual([{ name: 'arr.1.x', message: 'error' }]);
  475. expect(arrayField.state.errors['arr.0.x']).toEqual([{ name: 'arr.0.x', message: 'error' }]);
  476. expect(arrayField.state.errors['arr.1.x']).toEqual([{ name: 'arr.1.x', message: 'error' }]);
  477. expect(arrayField.state.errors['arr.2.x']).toBeUndefined();
  478. });
  479. it('should not keep previous error state when delete first elem in array then add back ', () => {
  480. const arrayField = formModel.createFieldArray('arr');
  481. formModel.init({
  482. validateTrigger: ValidateTrigger.onChange,
  483. initialValues: {
  484. arr: [{ x: 1, y: 2 }],
  485. },
  486. });
  487. const field0 = formModel.createField('arr.0');
  488. const field0x = formModel.createField('arr.0.x');
  489. const field0y = formModel.createField('arr.0.y');
  490. field0x.state.errors = {
  491. 'arr.0.x': [{ name: 'arr.0.x', message: 'error' }],
  492. } as unknown as Errors;
  493. field0x.bubbleState();
  494. // 删除第0项
  495. arrayField._splice(0);
  496. expect(formModel.state.errors['arr.0.x']).toBeUndefined();
  497. expect(arrayField.state.errors['arr.0.x']).toBeUndefined();
  498. expect(formModel._fieldMap.get('arr.0')).toBeUndefined();
  499. arrayField.append({ x: 1, y: 2 });
  500. formModel.createField('arr.0.x');
  501. expect(formModel._fieldMap.get('arr.0')).toBeDefined();
  502. expect(formModel.state.errors['arr.0.x']).toBeUndefined();
  503. expect(formModel._fieldMap.get('arr.0.x').state.errors).toBeUndefined();
  504. });
  505. });
  506. describe('swap', () => {
  507. beforeEach(() => {
  508. formModel.dispose();
  509. formModel = new FormModel();
  510. });
  511. it('can swap from 0 to middle index', () => {
  512. const arrayField = formModel.createFieldArray('arr');
  513. arrayField!.append('a');
  514. arrayField!.append('b');
  515. arrayField!.append('c');
  516. formModel.init({});
  517. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c'] });
  518. arrayField.swap(0, 1);
  519. expect(formModel.values).toEqual({ arr: ['b', 'a', 'c'] });
  520. });
  521. it('can swap from 0 to last index', () => {
  522. const arrayField = formModel.createFieldArray('arr');
  523. arrayField!.append('a');
  524. arrayField!.append('b');
  525. arrayField!.append('c');
  526. formModel.init({});
  527. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c'] });
  528. arrayField.swap(0, 2);
  529. expect(formModel.values).toEqual({ arr: ['c', 'b', 'a'] });
  530. });
  531. it('can swap from middle index to last index', () => {
  532. const arrayField = formModel.createFieldArray('arr');
  533. arrayField!.append('a');
  534. arrayField!.append('b');
  535. arrayField!.append('c');
  536. formModel.init({});
  537. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c'] });
  538. arrayField.swap(1, 2);
  539. expect(formModel.values).toEqual({ arr: ['a', 'c', 'b'] });
  540. });
  541. it('can swap from middle index to another middle index', () => {
  542. const arrayField = formModel.createFieldArray('arr');
  543. arrayField!.append('a');
  544. arrayField!.append('b');
  545. arrayField!.append('c');
  546. arrayField!.append('d');
  547. formModel.init({});
  548. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c', 'd'] });
  549. arrayField.swap(1, 2);
  550. expect(formModel.values).toEqual({ arr: ['a', 'c', 'b', 'd'] });
  551. });
  552. it('should trigger array effect and child effect', () => {
  553. const arrayField = formModel.createFieldArray('arr');
  554. const fieldA = arrayField!.append('a');
  555. arrayField!.append('b');
  556. arrayField!.append('c');
  557. const arrayEffect = vi.fn();
  558. arrayField.onValueChange(arrayEffect);
  559. const fieldAEffect = vi.fn();
  560. fieldA.onValueChange(fieldAEffect);
  561. formModel.init({});
  562. arrayField.swap(1, 2);
  563. expect(arrayEffect).toHaveBeenCalledOnce();
  564. expect(fieldAEffect).toHaveBeenCalledOnce();
  565. });
  566. });
  567. describe('move', () => {
  568. beforeEach(() => {
  569. formModel.dispose();
  570. formModel = new FormModel();
  571. });
  572. it('should throw error when from or to exceeds bound', () => {
  573. const arrayField = formModel.createFieldArray('arr');
  574. arrayField!.append('a');
  575. arrayField!.append('b');
  576. arrayField!.append('c');
  577. formModel.init({});
  578. expect(() => arrayField.move(-1, 1)).toThrowError();
  579. expect(() => arrayField.move(1, -1)).toThrowError();
  580. expect(() => arrayField.move(1, 3)).toThrowError();
  581. expect(() => arrayField.move(3, 1)).toThrowError();
  582. });
  583. it('can move from 0 to middle index', () => {
  584. const arrayField = formModel.createFieldArray('arr');
  585. arrayField!.append('a');
  586. arrayField!.append('b');
  587. arrayField!.append('c');
  588. formModel.init({});
  589. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c'] });
  590. arrayField.move(0, 1);
  591. expect(formModel.values).toEqual({ arr: ['b', 'a', 'c'] });
  592. });
  593. it('can move from 0 to last index', () => {
  594. const arrayField = formModel.createFieldArray('arr');
  595. arrayField!.append('a');
  596. arrayField!.append('b');
  597. arrayField!.append('c');
  598. formModel.init({});
  599. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c'] });
  600. arrayField.move(0, 2);
  601. expect(formModel.values).toEqual({ arr: ['b', 'c', 'a'] });
  602. });
  603. it('can move from middle index to last index', () => {
  604. const arrayField = formModel.createFieldArray('arr');
  605. arrayField!.append('a');
  606. arrayField!.append('b');
  607. arrayField!.append('c');
  608. formModel.init({});
  609. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c'] });
  610. arrayField.move(1, 2);
  611. expect(formModel.values).toEqual({ arr: ['a', 'c', 'b'] });
  612. });
  613. it('can move from middle index to another middle index', () => {
  614. const arrayField = formModel.createFieldArray('arr');
  615. arrayField!.append('a');
  616. arrayField!.append('b');
  617. arrayField!.append('c');
  618. arrayField!.append('d');
  619. formModel.init({});
  620. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c', 'd'] });
  621. arrayField.move(1, 2);
  622. expect(formModel.values).toEqual({ arr: ['a', 'c', 'b', 'd'] });
  623. });
  624. it('can move from middle index to another middle index with more elements', () => {
  625. const arrayField = formModel.createFieldArray('arr');
  626. arrayField!.append('a');
  627. arrayField!.append('b');
  628. arrayField!.append('c');
  629. arrayField!.append('d');
  630. arrayField!.append('e');
  631. arrayField!.append('f');
  632. formModel.init({});
  633. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c', 'd', 'e', 'f'] });
  634. arrayField.move(1, 4);
  635. expect(formModel.values).toEqual({ arr: ['a', 'c', 'd', 'e', 'b', 'f'] });
  636. });
  637. it('can move from middle index to another middle index with more elements when to is greater than from', () => {
  638. const arrayField = formModel.createFieldArray('arr');
  639. arrayField!.append('a');
  640. arrayField!.append('b');
  641. arrayField!.append('c');
  642. arrayField!.append('d');
  643. arrayField!.append('e');
  644. arrayField!.append('f');
  645. formModel.init({});
  646. expect(formModel.values).toEqual({ arr: ['a', 'b', 'c', 'd', 'e', 'f'] });
  647. arrayField.move(4, 1);
  648. expect(formModel.values).toEqual({ arr: ['a', 'e', 'b', 'c', 'd', 'f'] });
  649. });
  650. it('should trigger array effect and child effect', () => {
  651. const arrayField = formModel.createFieldArray('arr');
  652. const fieldA = arrayField!.append('a');
  653. arrayField!.append('b');
  654. arrayField!.append('c');
  655. const arrayEffect = vi.fn();
  656. arrayField.onValueChange(arrayEffect);
  657. const fieldAEffect = vi.fn();
  658. fieldA.onValueChange(fieldAEffect);
  659. formModel.init({});
  660. arrayField.move(1, 2);
  661. expect(arrayEffect).toHaveBeenCalledOnce();
  662. expect(fieldAEffect).toHaveBeenCalledOnce();
  663. });
  664. });
  665. });