319198612dc7fb55e3586abfe80e05a8b759181305b20a54e29db60fb16658e2aad84b415857006544a527908f09faa86b8cdab5f647fd3f487b5415047a61 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621
  1. describe('Core_validate', () => {
  2. var id = 'testContainer';
  3. beforeEach(function() {
  4. this.$container = $(`<div id="${id}"></div>`).appendTo('body');
  5. });
  6. afterEach(function() {
  7. if (this.$container) {
  8. destroy();
  9. this.$container.remove();
  10. }
  11. });
  12. var arrayOfObjects = function() {
  13. return [
  14. {id: 1, name: 'Ted', lastName: 'Right'},
  15. {id: 2, name: 'Frank', lastName: 'Honest'},
  16. {id: 3, name: 'Joan', lastName: 'Well'},
  17. {id: 4, name: 'Sid', lastName: 'Strong'},
  18. {id: 5, name: 'Jane', lastName: 'Neat'},
  19. {id: 6, name: 'Chuck', lastName: 'Jackson'},
  20. {id: 7, name: 'Meg', lastName: 'Jansen'},
  21. {id: 8, name: 'Rob', lastName: 'Norris'},
  22. {id: 9, name: 'Sean', lastName: 'O\'Hara'},
  23. {id: 10, name: 'Eve', lastName: 'Branson'}
  24. ];
  25. };
  26. it('should call beforeValidate', () => {
  27. var fired = null;
  28. handsontable({
  29. data: arrayOfObjects(),
  30. columns: [
  31. {data: 'id', type: 'numeric'},
  32. {data: 'name'},
  33. {data: 'lastName'}
  34. ],
  35. beforeValidate() {
  36. fired = true;
  37. }
  38. });
  39. setDataAtCell(2, 0, 'test');
  40. expect(fired).toEqual(true);
  41. });
  42. it('should call beforeValidate when columns is a function', () => {
  43. var fired = null;
  44. handsontable({
  45. data: arrayOfObjects(),
  46. columns(column) {
  47. var colMeta = {};
  48. if (column === 0) {
  49. colMeta.data = 'id';
  50. colMeta.type = 'numeric';
  51. } else if (column === 1) {
  52. colMeta.data = 'name';
  53. } else if (column === 2) {
  54. colMeta.data = 'lastName';
  55. } else {
  56. colMeta = null;
  57. }
  58. return colMeta;
  59. },
  60. beforeValidate() {
  61. fired = true;
  62. }
  63. });
  64. setDataAtCell(2, 0, 'test');
  65. expect(fired).toBe(true);
  66. });
  67. it('should call afterValidate', (done) => {
  68. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  69. handsontable({
  70. data: arrayOfObjects(),
  71. columns: [
  72. {data: 'id', type: 'numeric'},
  73. {data: 'name'},
  74. {data: 'lastName'}
  75. ],
  76. afterValidate: onAfterValidate
  77. });
  78. setDataAtCell(2, 0, 'test');
  79. setTimeout(() => {
  80. expect(onAfterValidate.calls.count()).toBe(1);
  81. done();
  82. }, 200);
  83. });
  84. it('should call afterValidate when columns is a function', (done) => {
  85. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  86. handsontable({
  87. data: arrayOfObjects(),
  88. columns(column) {
  89. var colMeta = {};
  90. if (column === 0) {
  91. colMeta.data = 'id';
  92. colMeta.type = 'numeric';
  93. } else if (column === 1) {
  94. colMeta.data = 'name';
  95. } else if (column === 2) {
  96. colMeta.data = 'lastName';
  97. } else {
  98. colMeta = null;
  99. }
  100. return colMeta;
  101. },
  102. afterValidate: onAfterValidate
  103. });
  104. setDataAtCell(2, 0, 'test');
  105. setTimeout(() => {
  106. expect(onAfterValidate.calls.count()).toBe(1);
  107. done();
  108. }, 200);
  109. });
  110. it('beforeValidate can manipulate value', (done) => {
  111. var result = null;
  112. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  113. onAfterValidate.and.callFake((valid, value) => {
  114. result = value;
  115. });
  116. handsontable({
  117. data: arrayOfObjects(),
  118. columns: [
  119. {data: 'id', type: 'numeric'},
  120. {data: 'name'},
  121. {data: 'lastName'}
  122. ],
  123. beforeValidate(value) {
  124. value = 999;
  125. return value;
  126. },
  127. afterValidate: onAfterValidate
  128. });
  129. setDataAtCell(2, 0, 123);
  130. setTimeout(() => {
  131. expect(result).toBe(999);
  132. done();
  133. }, 200);
  134. });
  135. it('beforeValidate can manipulate value when columns is a function', (done) => {
  136. var result = null;
  137. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  138. onAfterValidate.and.callFake((valid, value) => {
  139. result = value;
  140. });
  141. handsontable({
  142. data: arrayOfObjects(),
  143. columns(column) {
  144. var colMeta = {};
  145. if (column === 0) {
  146. colMeta.data = 'id';
  147. colMeta.type = 'numeric';
  148. } else if (column === 1) {
  149. colMeta.data = 'name';
  150. } else if (column === 2) {
  151. colMeta.data = 'lastName';
  152. } else {
  153. colMeta = null;
  154. }
  155. return colMeta;
  156. },
  157. beforeValidate(value) {
  158. value = 999;
  159. return value;
  160. },
  161. afterValidate: onAfterValidate
  162. });
  163. setDataAtCell(2, 0, 123);
  164. setTimeout(() => {
  165. expect(result).toBe(999);
  166. done();
  167. }, 200);
  168. });
  169. it('should be able to define custom validator function', (done) => {
  170. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  171. handsontable({
  172. data: arrayOfObjects(),
  173. columns: [
  174. {data: 'id',
  175. validator(value, cb) {
  176. cb(true);
  177. }},
  178. {data: 'name'},
  179. {data: 'lastName'}
  180. ],
  181. afterValidate: onAfterValidate
  182. });
  183. setDataAtCell(2, 0, 123);
  184. setTimeout(() => {
  185. expect(onAfterValidate).toHaveBeenCalledWith(true, 123, 2, 'id', undefined, undefined);
  186. done();
  187. }, 200);
  188. });
  189. it('should be able to define custom validator function when columns is a function', (done) => {
  190. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  191. handsontable({
  192. data: arrayOfObjects(),
  193. columns(column) {
  194. var colMeta = null;
  195. if (column === 0) {
  196. colMeta = {
  197. data: 'id',
  198. validator(value, cb) {
  199. cb(true);
  200. }
  201. };
  202. } else if (column === 1) {
  203. colMeta = {data: 'name'};
  204. } else if (column === 2) {
  205. colMeta = {data: 'lastName'};
  206. }
  207. return colMeta;
  208. },
  209. afterValidate: onAfterValidate
  210. });
  211. setDataAtCell(2, 0, 123);
  212. setTimeout(() => {
  213. expect(onAfterValidate).toHaveBeenCalledWith(true, 123, 2, 'id', undefined, undefined);
  214. done();
  215. }, 200);
  216. });
  217. it('should be able to define custom validator RegExp', (done) => {
  218. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  219. handsontable({
  220. data: arrayOfObjects(),
  221. columns: [
  222. {data: 'id', validator: /^\d+$/ },
  223. {data: 'name'},
  224. {data: 'lastName'}
  225. ],
  226. afterValidate: onAfterValidate
  227. });
  228. setDataAtCell(2, 0, 'test');
  229. setTimeout(() => {
  230. expect(onAfterValidate).toHaveBeenCalledWith(false, 'test', 2, 'id', undefined, undefined);
  231. done();
  232. }, 200);
  233. });
  234. it('should be able to define custom validator RegExp when columns is a function', (done) => {
  235. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  236. handsontable({
  237. data: arrayOfObjects(),
  238. columns(column) {
  239. var colMeta = null;
  240. if (column === 0) {
  241. colMeta = {data: 'id', validator: /^\d+$/};
  242. } else if (column === 1) {
  243. colMeta = {data: 'name'};
  244. } else if (column === 2) {
  245. colMeta = {data: 'lastName'};
  246. }
  247. return colMeta;
  248. },
  249. afterValidate: onAfterValidate
  250. });
  251. setDataAtCell(2, 0, 'test');
  252. setTimeout(() => {
  253. expect(onAfterValidate).toHaveBeenCalledWith(false, 'test', 2, 'id', undefined, undefined);
  254. done();
  255. }, 200);
  256. });
  257. it('this in validator should point to cellProperties', (done) => {
  258. var result = null;
  259. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  260. handsontable({
  261. data: arrayOfObjects(),
  262. columns: [
  263. {
  264. data: 'id',
  265. validator(value, cb) {
  266. result = this;
  267. cb(true);
  268. }
  269. },
  270. {data: 'name'},
  271. {data: 'lastName'}
  272. ],
  273. afterValidate: onAfterValidate
  274. });
  275. setDataAtCell(2, 0, 123);
  276. setTimeout(() => {
  277. expect(result.instance).toEqual(getInstance());
  278. done();
  279. }, 200);
  280. });
  281. it('this in validator should point to cellProperties when columns is a function', (done) => {
  282. var result = null;
  283. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  284. handsontable({
  285. data: arrayOfObjects(),
  286. columns(column) {
  287. var colMeta = null;
  288. if (column === 0) {
  289. colMeta = {
  290. data: 'id',
  291. validator(value, cb) {
  292. result = this;
  293. cb(true);
  294. }
  295. };
  296. } else if (column === 1) {
  297. colMeta = {data: 'name'};
  298. } else if (column === 2) {
  299. colMeta = {data: 'lastName'};
  300. }
  301. return colMeta;
  302. },
  303. afterValidate: onAfterValidate
  304. });
  305. setDataAtCell(2, 0, 123);
  306. setTimeout(() => {
  307. expect(result.instance).toEqual(getInstance());
  308. done();
  309. }, 200);
  310. });
  311. it('should not throw error after calling validateCells without first argument', (done) => {
  312. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  313. var hot = handsontable({
  314. data: Handsontable.helper.createSpreadsheetData(2, 2),
  315. validator(value, callb) {
  316. if (value == 'B1') {
  317. callb(false);
  318. } else {
  319. callb(true);
  320. }
  321. },
  322. afterValidate: onAfterValidate
  323. });
  324. expect(hot.validateCells).not.toThrow();
  325. setTimeout(() => {
  326. expect(spec().$container.find('td.htInvalid').length).toEqual(1);
  327. expect(spec().$container.find('td:not(.htInvalid)').length).toEqual(3);
  328. done();
  329. }, 200);
  330. });
  331. it('should add class name `htInvalid` to an cell that does not validate - on validateCells', (done) => {
  332. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  333. var hot = handsontable({
  334. data: Handsontable.helper.createSpreadsheetData(2, 2),
  335. validator(value, callb) {
  336. if (value == 'B1') {
  337. callb(false);
  338. } else {
  339. callb(true);
  340. }
  341. },
  342. afterValidate: onAfterValidate
  343. });
  344. hot.validateCells(() => {
  345. hot.render();
  346. });
  347. setTimeout(() => {
  348. expect(spec().$container.find('td.htInvalid').length).toEqual(1);
  349. expect(spec().$container.find('td:not(.htInvalid)').length).toEqual(3);
  350. done();
  351. }, 200);
  352. });
  353. it('should add class name `htInvalid` to an cell that does not validate - when we trigger validateCell', function(done) {
  354. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  355. var hot = handsontable({
  356. data: Handsontable.helper.createSpreadsheetData(2, 2),
  357. validator(value, cb) {
  358. cb(false);
  359. },
  360. afterValidate: onAfterValidate
  361. });
  362. expect(this.$container.find('td:not(.htInvalid)').length).toEqual(4);
  363. hot.validateCell(hot.getDataAtCell(1, 1), hot.getCellMeta(1, 1), () => {});
  364. setTimeout(() => {
  365. expect(spec().$container.find('td.htInvalid').length).toEqual(1);
  366. expect(spec().$container.find('td:not(.htInvalid)').length).toEqual(3);
  367. done();
  368. }, 200);
  369. });
  370. it('should remove class name `htInvalid` from an cell that does validate - when we change validator rules', (done) => {
  371. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  372. var isValid = false;
  373. var validator = function() {
  374. return isValid;
  375. };
  376. var hot = handsontable({
  377. data: Handsontable.helper.createSpreadsheetData(2, 2),
  378. validator(value, cb) {
  379. cb(validator());
  380. },
  381. afterValidate: onAfterValidate
  382. });
  383. hot.validateCells(() => {});
  384. setTimeout(() => {
  385. expect(spec().$container.find('td.htInvalid').length).toEqual(4);
  386. expect(spec().$container.find('td:not(.htInvalid)').length).toEqual(0);
  387. isValid = true;
  388. onAfterValidate.calls.reset();
  389. hot.validateCell(hot.getDataAtCell(1, 1), hot.getCellMeta(1, 1), () => {});
  390. }, 200);
  391. setTimeout(() => {
  392. expect(spec().$container.find('td.htInvalid').length).toEqual(3);
  393. expect(spec().$container.find('td:not(.htInvalid)').length).toEqual(1);
  394. done();
  395. }, 400);
  396. });
  397. it('should add class name `htInvalid` to an cell that does not validate - on edit', (done) => {
  398. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  399. handsontable({
  400. data: Handsontable.helper.createSpreadsheetData(2, 2),
  401. validator(value, callb) {
  402. if (value == 'test') {
  403. callb(false);
  404. } else {
  405. callb(true);
  406. }
  407. },
  408. afterValidate: onAfterValidate
  409. });
  410. setDataAtCell(0, 0, 'test');
  411. setTimeout(() => {
  412. expect(spec().$container.find('td.htInvalid').length).toEqual(1);
  413. expect(spec().$container.find('tr:eq(0) td:eq(0)').hasClass('htInvalid')).toEqual(true);
  414. done();
  415. }, 200);
  416. });
  417. it('should add class name `htInvalid` to a cell without removing other classes', (done) => {
  418. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  419. var validator = jasmine.createSpy('validator');
  420. validator.and.callFake((value, callb) => {
  421. if (value == 123) {
  422. callb(false);
  423. } else {
  424. callb(true);
  425. }
  426. });
  427. handsontable({
  428. data: Handsontable.helper.createSpreadsheetData(2, 2),
  429. type: 'numeric',
  430. validator,
  431. afterValidate: onAfterValidate
  432. });
  433. setDataAtCell(0, 0, 123);
  434. setTimeout(() => {
  435. expect(validator.calls.count()).toEqual(1);
  436. expect(spec().$container.find('tr:eq(0) td:eq(0)').hasClass('htInvalid')).toEqual(true);
  437. expect(spec().$container.find('tr:eq(0) td:eq(0)').hasClass('htNumeric')).toEqual(true);
  438. onAfterValidate.calls.reset();
  439. setDataAtCell(0, 0, 124);
  440. }, 200);
  441. setTimeout(() => {
  442. expect(spec().$container.find('tr:eq(0) td:eq(0)').hasClass('htInvalid')).toEqual(false);
  443. expect(spec().$container.find('tr:eq(0) td:eq(0)').hasClass('htNumeric')).toEqual(true);
  444. done();
  445. }, 400);
  446. });
  447. it('should add class name `htInvalid` to an cell that does not validate - after validateCells', (done) => {
  448. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  449. var hot = handsontable({
  450. data: Handsontable.helper.createSpreadsheetData(2, 2),
  451. afterValidate: onAfterValidate
  452. });
  453. setDataAtCell(0, 0, 'test');
  454. setTimeout(() => {
  455. expect(spec().$container.find('td.htInvalid').length).toEqual(0);
  456. updateSettings({
  457. validator(value, callb) {
  458. if (value == 'test') {
  459. callb(false);
  460. } else {
  461. callb(true);
  462. }
  463. }
  464. });
  465. onAfterValidate.calls.reset();
  466. hot.validateCells(() => {});
  467. }, 200);
  468. setTimeout(() => {
  469. expect(spec().$container.find('td.htInvalid').length).toEqual(1);
  470. expect(spec().$container.find('tr:eq(0) td:eq(0)').hasClass('htInvalid')).toEqual(true);
  471. done();
  472. }, 400);
  473. });
  474. it('should remove class name `htInvalid` when cell is edited to validate', (done) => {
  475. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  476. var hot = handsontable({
  477. data: Handsontable.helper.createSpreadsheetData(2, 2),
  478. validator(value, callb) {
  479. if (value == 'A1') {
  480. callb(false);
  481. } else {
  482. callb(true);
  483. }
  484. },
  485. afterValidate: onAfterValidate
  486. });
  487. hot.validateCells(() => {
  488. hot.render();
  489. });
  490. setTimeout(() => {
  491. expect(spec().$container.find('tr:eq(0) td:eq(0)').hasClass('htInvalid')).toEqual(true);
  492. onAfterValidate.calls.reset();
  493. setDataAtCell(0, 0, 'test');
  494. }, 200);
  495. setTimeout(() => {
  496. expect(spec().$container.find('tr:eq(0) td:eq(0)').hasClass('htInvalid')).toEqual(false);
  497. done();
  498. }, 400);
  499. });
  500. it('should call callback with first argument as `true` if all cells are valid', (done) => {
  501. var onValidate = jasmine.createSpy('onValidate');
  502. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  503. var hot = handsontable({
  504. data: Handsontable.helper.createSpreadsheetData(2, 2),
  505. validator(value, callback) {
  506. callback(true);
  507. },
  508. afterValidate: onAfterValidate
  509. });
  510. hot.validateCells(onValidate);
  511. setTimeout(() => {
  512. expect(onValidate).toHaveBeenCalledWith(true);
  513. done();
  514. }, 200);
  515. });
  516. it('should call callback with first argument as `false` if one of cells is invalid', (done) => {
  517. var onValidate = jasmine.createSpy('onValidate');
  518. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  519. var hot = handsontable({
  520. data: Handsontable.helper.createSpreadsheetData(2, 2),
  521. validator(value, callback) {
  522. callback(false);
  523. },
  524. afterValidate: onAfterValidate
  525. });
  526. hot.validateCells(onValidate);
  527. setTimeout(() => {
  528. expect(onValidate).toHaveBeenCalledWith(false);
  529. done();
  530. }, 200);
  531. });
  532. it('should not allow for changes where data is invalid (multiple changes, async)', (done) => {
  533. var validatedChanges;
  534. handsontable({
  535. data: Handsontable.helper.createSpreadsheetData(5, 2),
  536. allowInvalid: false,
  537. validator(value, callb) {
  538. setTimeout(() => {
  539. if (value === 'fail') {
  540. callb(false);
  541. } else {
  542. callb(true);
  543. }
  544. }, 10);
  545. },
  546. afterChange(changes, source) {
  547. if (source !== 'loadData') {
  548. validatedChanges = changes;
  549. }
  550. }
  551. });
  552. populateFromArray(0, 0, [
  553. ['A1-new'],
  554. ['fail'],
  555. ['A3-new']
  556. ]);
  557. setTimeout(() => {
  558. expect(validatedChanges.length).toEqual(2);
  559. expect(validatedChanges[0]).toEqual([0, 0, 'A1', 'A1-new']);
  560. expect(validatedChanges[1]).toEqual([2, 0, 'A3', 'A3-new']);
  561. expect(getDataAtCell(0, 0)).toEqual('A1-new');
  562. expect(getDataAtCell(1, 0)).toEqual('A2');
  563. expect(getDataAtCell(2, 0)).toEqual('A3-new');
  564. expect(getCellMeta(0, 0).valid).toBe(true);
  565. expect(getCellMeta(1, 0).valid).toBe(true);
  566. expect(getCellMeta(2, 0).valid).toBe(true);
  567. done();
  568. }, 200);
  569. });
  570. it('should call beforeChange exactly once after cell value edit and validator is synchronous', (done) => {
  571. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  572. var onBeforeChange = jasmine.createSpy('onBeforeChange');
  573. var hot = handsontable({
  574. data: Handsontable.helper.createSpreadsheetData(5, 2),
  575. allowInvalid: false,
  576. validator(value, callback) {
  577. callback(true);
  578. },
  579. beforeChange: onBeforeChange,
  580. afterValidate: onAfterValidate
  581. });
  582. expect(onBeforeChange.calls.count()).toEqual(0);
  583. hot.setDataAtCell(0, 0, 10);
  584. setTimeout(() => {
  585. expect(onBeforeChange.calls.count()).toEqual(1);
  586. done();
  587. }, 200);
  588. });
  589. it('should call beforeChange exactly once after cell value edit and validator is asynchronous', (done) => {
  590. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  591. var onBeforeChange = jasmine.createSpy('onBeforeChange');
  592. var hot = handsontable({
  593. data: Handsontable.helper.createSpreadsheetData(5, 2),
  594. allowInvalid: false,
  595. validator(value, callback) {
  596. setTimeout(() => {
  597. callback(true);
  598. }, 10);
  599. },
  600. beforeChange: onBeforeChange,
  601. afterValidate: onAfterValidate
  602. });
  603. expect(onBeforeChange.calls.count()).toEqual(0);
  604. hot.setDataAtCell(0, 0, 10);
  605. setTimeout(() => {
  606. expect(onBeforeChange.calls.count()).toEqual(1);
  607. done();
  608. }, 200);
  609. });
  610. it('should call afterChange exactly once after cell value edit and validator is synchronous', (done) => {
  611. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  612. var onAfterChange = jasmine.createSpy('onAfterChange');
  613. var hot = handsontable({
  614. data: Handsontable.helper.createSpreadsheetData(5, 2),
  615. allowInvalid: false,
  616. validator(value, callback) {
  617. callback(true);
  618. },
  619. afterChange: onAfterChange,
  620. afterValidate: onAfterValidate
  621. });
  622. expect(onAfterChange.calls.count()).toEqual(1); // loadData
  623. hot.setDataAtCell(0, 0, 10);
  624. setTimeout(() => {
  625. expect(onAfterChange.calls.count()).toEqual(2);
  626. done();
  627. }, 200);
  628. });
  629. it('should call afterChange exactly once after cell value edit and validator is asynchronous', (done) => {
  630. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  631. var onAfterChange = jasmine.createSpy('onAfterChange');
  632. var hot = handsontable({
  633. data: Handsontable.helper.createSpreadsheetData(5, 2),
  634. allowInvalid: false,
  635. validator(value, callback) {
  636. setTimeout(() => {
  637. callback(true);
  638. }, 10);
  639. },
  640. afterChange: onAfterChange,
  641. afterValidate: onAfterValidate
  642. });
  643. expect(onAfterChange.calls.count()).toEqual(1); // loadData
  644. hot.setDataAtCell(0, 0, 10);
  645. setTimeout(() => {
  646. expect(onAfterChange.calls.count()).toEqual(2);
  647. done();
  648. }, 200);
  649. });
  650. it('edited cell should stay on screen until value is validated', (done) => {
  651. var isEditorVisibleBeforeChange;
  652. var isEditorVisibleAfterChange;
  653. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  654. var onAfterChange = jasmine.createSpy('onAfterChange');
  655. onAfterValidate.and.callFake(() => {
  656. isEditorVisibleBeforeChange = isEditorVisible();
  657. });
  658. onAfterChange.and.callFake(() => {
  659. isEditorVisibleAfterChange = isEditorVisible();
  660. });
  661. handsontable({
  662. data: Handsontable.helper.createSpreadsheetData(5, 2),
  663. allowInvalid: false,
  664. afterValidate: onAfterValidate,
  665. afterChange: onAfterChange,
  666. validator(value, callback) {
  667. setTimeout(() => {
  668. callback(true);
  669. }, 100);
  670. }
  671. });
  672. selectCell(0, 0);
  673. keyDown('enter');
  674. document.activeElement.value = 'Ted';
  675. onAfterValidate.calls.reset();
  676. onAfterChange.calls.reset();
  677. keyDown('enter');
  678. expect(document.activeElement.nodeName).toEqual('TEXTAREA');
  679. setTimeout(() => {
  680. expect(isEditorVisibleBeforeChange).toBe(true);
  681. expect(isEditorVisibleAfterChange).toBe(true);
  682. expect(isEditorVisible()).toBe(false);
  683. done();
  684. }, 200);
  685. });
  686. it('should validate edited cell after selecting another cell', (done) => {
  687. var validated = false;
  688. var validatedValue;
  689. handsontable({
  690. data: Handsontable.helper.createSpreadsheetData(5, 2),
  691. allowInvalid: false,
  692. validator(value, callback) {
  693. setTimeout(() => {
  694. validated = true;
  695. validatedValue = value;
  696. callback(true);
  697. }, 100);
  698. }
  699. });
  700. selectCell(0, 0);
  701. keyDown('enter');
  702. document.activeElement.value = 'Ted';
  703. selectCell(0, 1);
  704. setTimeout(() => {
  705. expect(validatedValue).toEqual('Ted');
  706. done();
  707. }, 200);
  708. });
  709. it('should leave the new value in editor if it does not validate (async validation), after hitting ENTER', (done) => {
  710. var validated = false;
  711. var validationResult;
  712. handsontable({
  713. data: Handsontable.helper.createSpreadsheetData(5, 2),
  714. allowInvalid: false,
  715. validator(value, callback) {
  716. setTimeout(() => {
  717. validated = true;
  718. validationResult = value.length == 2;
  719. callback(validationResult);
  720. }, 100);
  721. }
  722. });
  723. selectCell(0, 0);
  724. keyDown('enter');
  725. document.activeElement.value = 'Ted';
  726. keyDown('enter');
  727. setTimeout(() => {
  728. expect(validationResult).toBe(false);
  729. expect(document.activeElement.value).toEqual('Ted');
  730. done();
  731. }, 200);
  732. });
  733. it('should leave the new value in editor if it does not validate (sync validation), after hitting ENTER', (done) => {
  734. var validated = false;
  735. var validationResult;
  736. handsontable({
  737. data: Handsontable.helper.createSpreadsheetData(5, 2),
  738. allowInvalid: false,
  739. validator(value, callback) {
  740. validated = true;
  741. validationResult = value.length == 2;
  742. callback(validationResult);
  743. }
  744. });
  745. selectCell(0, 0);
  746. keyDown('enter');
  747. document.activeElement.value = 'Ted';
  748. keyDown('enter');
  749. setTimeout(() => {
  750. expect(validationResult).toBe(false);
  751. expect(document.activeElement.value).toEqual('Ted');
  752. done();
  753. }, 200);
  754. });
  755. it('should leave the new value in editor if it does not validate (async validation), after selecting another cell', (done) => {
  756. var validated = false;
  757. var validationResult;
  758. handsontable({
  759. data: Handsontable.helper.createSpreadsheetData(5, 2),
  760. allowInvalid: false,
  761. validator(value, callback) {
  762. setTimeout(() => {
  763. setTimeout(() => {
  764. validated = true;
  765. }, 0);
  766. validationResult = value.length == 2;
  767. callback(validationResult);
  768. }, 100);
  769. }
  770. });
  771. selectCell(0, 0);
  772. keyDown('enter');
  773. document.activeElement.value = 'Ted';
  774. selectCell(1, 0);
  775. setTimeout(() => {
  776. expect(validationResult).toBe(false);
  777. expect(document.activeElement.value).toEqual('Ted');
  778. done();
  779. }, 200);
  780. });
  781. it('should leave the new value in editor if it does not validate (sync validation), after selecting another cell', (done) => {
  782. var validated = false;
  783. var validationResult;
  784. handsontable({
  785. data: Handsontable.helper.createSpreadsheetData(5, 2),
  786. allowInvalid: false,
  787. validator(value, callback) {
  788. validationResult = value.length == 2;
  789. callback(validationResult);
  790. /* Setting this variable has to be async, because we are not interested in when the validation happens, but when
  791. the callback is being called. Since internally all the callbacks are processed asynchronously (even if they are
  792. synchronous) end of validator function is not the equivalent of whole validation routine end.
  793. If it still sounds weird, take a look at HandsontableTextEditorClass.prototype.finishEditing method.
  794. */
  795. setTimeout(() => {
  796. validated = true;
  797. }, 0);
  798. }
  799. });
  800. selectCell(0, 0);
  801. keyDown('enter');
  802. document.activeElement.value = 'Ted';
  803. selectCell(1, 0);
  804. setTimeout(() => {
  805. expect(validationResult).toBe(false);
  806. expect(document.activeElement.value).toEqual('Ted');
  807. done();
  808. }, 200);
  809. });
  810. it('should close the editor and save the new value if validation fails and allowInvalid is set to "true"', (done) => {
  811. var validated = false;
  812. var validationResult;
  813. handsontable({
  814. data: Handsontable.helper.createSpreadsheetData(5, 2),
  815. allowInvalid: true,
  816. validator(value, callback) {
  817. setTimeout(() => {
  818. validated = true;
  819. validationResult = value.length == 2;
  820. callback(validationResult);
  821. }, 100);
  822. }
  823. });
  824. selectCell(0, 0);
  825. keyDown('enter');
  826. document.activeElement.value = 'Ted';
  827. selectCell(1, 0);
  828. setTimeout(() => {
  829. expect(validationResult).toBe(false);
  830. expect(getDataAtCell(0, 0)).toEqual('Ted');
  831. expect(getCell(0, 0).className).toMatch(/htInvalid/);
  832. done();
  833. }, 200);
  834. });
  835. it('should close the editor and save the new value after double clicking on a cell, if the previously edited cell validated correctly', (done) => {
  836. var validated = false;
  837. var validationResult;
  838. handsontable({
  839. data: Handsontable.helper.createSpreadsheetData(5, 2),
  840. allowInvalid: false,
  841. validator(value, callback) {
  842. setTimeout(() => {
  843. validated = true;
  844. validationResult = value.length == 2;
  845. callback(validationResult);
  846. }, 100);
  847. }
  848. });
  849. selectCell(0, 0);
  850. keyDown('enter');
  851. var editor = $('.handsontableInputHolder');
  852. expect(editor.is(':visible')).toBe(true);
  853. document.activeElement.value = 'AA';
  854. expect(document.activeElement.value).toEqual('AA');
  855. var cell = $(getCell(1, 0));
  856. var clicks = 0;
  857. setTimeout(() => {
  858. mouseDown(cell);
  859. mouseUp(cell);
  860. clicks++;
  861. }, 0);
  862. setTimeout(() => {
  863. mouseDown(cell);
  864. mouseUp(cell);
  865. clicks++;
  866. }, 100);
  867. setTimeout(() => {
  868. expect(editor.is(':visible')).toBe(false);
  869. expect(validationResult).toBe(true);
  870. expect(getDataAtCell(0, 0)).toEqual('AA');
  871. done();
  872. }, 300);
  873. });
  874. it('should close the editor and restore the original value after double clicking on a cell, if the previously edited cell have not validated', (done) => {
  875. var validated = false;
  876. var validationResult;
  877. handsontable({
  878. data: Handsontable.helper.createSpreadsheetData(5, 2),
  879. allowInvalid: false,
  880. validator(value, callback) {
  881. setTimeout(() => {
  882. validated = true;
  883. validationResult = value.length == 2;
  884. callback(validationResult);
  885. }, 100);
  886. }
  887. });
  888. selectCell(0, 0);
  889. keyDown('enter');
  890. document.activeElement.value = 'AAA';
  891. expect(document.activeElement.value).toEqual('AAA');
  892. var cell = $(getCell(1, 0));
  893. var clicks = 0;
  894. setTimeout(() => {
  895. mouseDown(cell);
  896. mouseUp(cell);
  897. clicks++;
  898. }, 0);
  899. setTimeout(() => {
  900. mouseDown(cell);
  901. mouseUp(cell);
  902. clicks++;
  903. }, 100);
  904. setTimeout(() => {
  905. expect(validationResult).toBe(false);
  906. expect(getDataAtCell(0, 0)).toEqual('A1');
  907. done();
  908. }, 300);
  909. });
  910. it('should listen to key changes after cell is corrected (allowInvalid: false)', (done) => {
  911. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  912. handsontable({
  913. data: arrayOfObjects(),
  914. allowInvalid: false,
  915. columns: [
  916. {data: 'id',
  917. type: 'numeric',
  918. validator(val, cb) {
  919. cb(parseInt(val, 10) > 100);
  920. }},
  921. {data: 'name'},
  922. {data: 'lastName'}
  923. ],
  924. afterValidate: onAfterValidate
  925. });
  926. selectCell(2, 0);
  927. keyDownUp('enter');
  928. document.activeElement.value = '99';
  929. onAfterValidate.calls.reset();
  930. keyDownUp('enter'); // should be ignored
  931. setTimeout(() => {
  932. expect(isEditorVisible()).toBe(true);
  933. document.activeElement.value = '999';
  934. onAfterValidate.calls.reset();
  935. keyDownUp('enter'); // should be accepted
  936. }, 200);
  937. setTimeout(() => {
  938. expect(isEditorVisible()).toBe(false);
  939. expect(getSelected()).toEqual([3, 0, 3, 0]);
  940. keyDownUp('arrow_up');
  941. expect(getSelected()).toEqual([2, 0, 2, 0]);
  942. done();
  943. }, 400);
  944. });
  945. it('should allow keyboard movement when cell is being validated (move DOWN)', (done) => {
  946. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  947. handsontable({
  948. data: arrayOfObjects(),
  949. allowInvalid: false,
  950. columns: [
  951. {data: 'id',
  952. type: 'numeric',
  953. validator(val, cb) {
  954. setTimeout(() => {
  955. cb(parseInt(val, 10) > 100);
  956. }, 100);
  957. }},
  958. {data: 'name'},
  959. {data: 'lastName'}
  960. ],
  961. afterValidate: onAfterValidate
  962. });
  963. selectCell(2, 0);
  964. keyDownUp('enter');
  965. document.activeElement.value = '999';
  966. keyDownUp('enter');
  967. expect(getSelected()).toEqual([3, 0, 3, 0]);
  968. keyDownUp('arrow_down');
  969. keyDownUp('arrow_down');
  970. expect(isEditorVisible()).toBe(true);
  971. expect(getSelected()).toEqual([5, 0, 5, 0]);
  972. setTimeout(() => {
  973. expect(isEditorVisible()).toBe(false);
  974. expect(getSelected()).toEqual([5, 0, 5, 0]); // only enterMove and first arrow_down is performed
  975. done();
  976. }, 200);
  977. });
  978. it('should not allow keyboard movement until cell is validated (move UP)', (done) => {
  979. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  980. handsontable({
  981. data: arrayOfObjects(),
  982. allowInvalid: false,
  983. columns: [
  984. {data: 'id',
  985. type: 'numeric',
  986. validator(val, cb) {
  987. setTimeout(() => {
  988. cb(parseInt(val, 10) > 100);
  989. }, 100);
  990. }},
  991. {data: 'name'},
  992. {data: 'lastName'}
  993. ],
  994. afterValidate: onAfterValidate
  995. });
  996. selectCell(2, 0);
  997. keyDownUp('enter');
  998. document.activeElement.value = '999';
  999. keyDownUp('enter');
  1000. expect(getSelected()).toEqual([3, 0, 3, 0]);
  1001. keyDownUp('arrow_up');
  1002. keyDownUp('arrow_up');
  1003. expect(isEditorVisible()).toBe(true);
  1004. expect(getSelected()).toEqual([1, 0, 1, 0]);
  1005. setTimeout(() => {
  1006. expect(isEditorVisible()).toBe(false);
  1007. expect(getSelected()).toEqual([1, 0, 1, 0]);
  1008. done();
  1009. }, 200);
  1010. });
  1011. it('should not allow keyboard movement until cell is validated (move RIGHT)', (done) => {
  1012. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1013. handsontable({
  1014. data: arrayOfObjects(),
  1015. allowInvalid: false,
  1016. columns: [
  1017. {data: 'id',
  1018. type: 'numeric',
  1019. validator(val, cb) {
  1020. setTimeout(() => {
  1021. cb(parseInt(val, 10) > 100);
  1022. }, 100);
  1023. }},
  1024. {data: 'name'},
  1025. {data: 'lastName'}
  1026. ],
  1027. afterValidate: onAfterValidate
  1028. });
  1029. selectCell(2, 0);
  1030. keyDownUp('enter');
  1031. document.activeElement.value = '999';
  1032. keyDownUp('enter'); // should be accepted but only after 100 ms
  1033. expect(getSelected()).toEqual([3, 0, 3, 0]);
  1034. keyDownUp('arrow_right');
  1035. keyDownUp('arrow_right');
  1036. expect(isEditorVisible()).toBe(true);
  1037. expect(getSelected()).toEqual([3, 2, 3, 2]);
  1038. setTimeout(() => {
  1039. expect(isEditorVisible()).toBe(false);
  1040. expect(getSelected()).toEqual([3, 2, 3, 2]);
  1041. done();
  1042. }, 200);
  1043. });
  1044. it('should not allow keyboard movement until cell is validated (move LEFT)', function(done) {
  1045. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1046. hot = handsontable({
  1047. data: arrayOfObjects(),
  1048. allowInvalid: false,
  1049. columns: [
  1050. {data: 'name'},
  1051. {data: 'lastName'},
  1052. {data: 'id',
  1053. type: 'numeric',
  1054. validator(val, cb) {
  1055. setTimeout(() => {
  1056. cb(parseInt(val, 10) > 100);
  1057. }, 100);
  1058. }}
  1059. ],
  1060. afterValidate: onAfterValidate
  1061. });
  1062. selectCell(2, 2);
  1063. keyDownUp('enter');
  1064. document.activeElement.value = '999';
  1065. keyDownUp('enter'); // should be accepted but only after 100 ms
  1066. expect(getSelected()).toEqual([3, 2, 3, 2]);
  1067. this.$container.simulate('keydown', {keyCode: Handsontable.helper.KEY_CODES.ARROW_LEFT});
  1068. this.$container.simulate('keyup', {keyCode: Handsontable.helper.KEY_CODES.ARROW_LEFT});
  1069. this.$container.simulate('keydown', {keyCode: Handsontable.helper.KEY_CODES.ARROW_LEFT});
  1070. this.$container.simulate('keyup', {keyCode: Handsontable.helper.KEY_CODES.ARROW_LEFT});
  1071. expect(isEditorVisible()).toBe(true);
  1072. expect(getSelected()).toEqual([3, 0, 3, 0]);
  1073. setTimeout(() => {
  1074. expect(isEditorVisible()).toBe(false);
  1075. expect(getSelected()).toEqual([3, 0, 3, 0]);
  1076. done();
  1077. }, 200);
  1078. });
  1079. it('should not validate cell if editing has been canceled', (done) => {
  1080. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1081. handsontable({
  1082. data: arrayOfObjects(),
  1083. columns: [
  1084. {data: 'id'},
  1085. {data: 'name'},
  1086. {data: 'lastName'}
  1087. ],
  1088. afterValidate: onAfterValidate
  1089. });
  1090. selectCell(0, 0);
  1091. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // open editor
  1092. keyDownUp(Handsontable.helper.KEY_CODES.ESCAPE); // cancel editing
  1093. setTimeout(() => {
  1094. expect(onAfterValidate).not.toHaveBeenCalled();
  1095. done();
  1096. }, 100);
  1097. });
  1098. it('should not validate cell if editing has been canceled when columns is a function', (done) => {
  1099. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1100. handsontable({
  1101. data: arrayOfObjects(),
  1102. columns(column) {
  1103. var colMeta = null;
  1104. if (column === 0) {
  1105. colMeta = {data: 'id'};
  1106. } else if (column === 1) {
  1107. colMeta = {data: 'name'};
  1108. } else if (column === 2) {
  1109. colMeta = {data: 'lastName'};
  1110. }
  1111. return colMeta;
  1112. },
  1113. afterValidate: onAfterValidate
  1114. });
  1115. selectCell(0, 0);
  1116. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // open editor
  1117. keyDownUp(Handsontable.helper.KEY_CODES.ESCAPE); // cancel editing
  1118. setTimeout(() => {
  1119. expect(onAfterValidate).not.toHaveBeenCalled();
  1120. done();
  1121. }, 100);
  1122. });
  1123. it('should leave cell invalid if editing has been canceled', (done) => {
  1124. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1125. handsontable({
  1126. data: arrayOfObjects(),
  1127. columns: [
  1128. {data: 'id',
  1129. validator(value, cb) {
  1130. cb(false);
  1131. }},
  1132. {data: 'name'},
  1133. {data: 'lastName'}
  1134. ],
  1135. afterValidate: onAfterValidate
  1136. });
  1137. setDataAtCell(0, 0, 'foo');
  1138. setTimeout(() => {
  1139. expect(getCellMeta(0, 0).valid).toBe(false);
  1140. selectCell(0, 0);
  1141. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // open editor
  1142. keyDownUp(Handsontable.helper.KEY_CODES.ESCAPE); // cancel editing
  1143. expect(getCellMeta(0, 0).valid).toBe(false);
  1144. done();
  1145. }, 200);
  1146. });
  1147. it('should leave cell invalid if editing has been canceled when columns is a function', (done) => {
  1148. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1149. handsontable({
  1150. data: arrayOfObjects(),
  1151. columns(column) {
  1152. var colMeta = null;
  1153. if (column === 0) {
  1154. colMeta = {
  1155. data: 'id',
  1156. validator(value, cb) {
  1157. cb(false);
  1158. }
  1159. };
  1160. } else if (column === 1) {
  1161. colMeta = {data: 'name'};
  1162. } else if (column === 2) {
  1163. colMeta = {data: 'lastName'};
  1164. }
  1165. return colMeta;
  1166. },
  1167. afterValidate: onAfterValidate
  1168. });
  1169. setDataAtCell(0, 0, 'foo');
  1170. setTimeout(() => {
  1171. expect(getCellMeta(0, 0).valid).toBe(false);
  1172. selectCell(0, 0);
  1173. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // open editor
  1174. keyDownUp(Handsontable.helper.KEY_CODES.ESCAPE); // cancel editing
  1175. expect(getCellMeta(0, 0).valid).toBe(false);
  1176. done();
  1177. }, 200);
  1178. });
  1179. it('should open an appropriate editor after cell value is valid again', (done) => {
  1180. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1181. var hot = handsontable({
  1182. data: arrayOfObjects(),
  1183. columns: [
  1184. {
  1185. data: 'id',
  1186. validator(value, cb) {
  1187. cb(value == parseInt(value, 10));
  1188. },
  1189. allowInvalid: false
  1190. },
  1191. {data: 'name'},
  1192. {data: 'lastName'}
  1193. ],
  1194. afterValidate: onAfterValidate
  1195. });
  1196. selectCell(0, 0);
  1197. var activeEditor = hot.getActiveEditor();
  1198. expect(activeEditor.row).toEqual(0);
  1199. expect(activeEditor.col).toEqual(0);
  1200. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // open editor
  1201. activeEditor.setValue('foo');
  1202. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // save changes, close editor
  1203. setTimeout(() => {
  1204. onAfterValidate.calls.reset();
  1205. activeEditor = hot.getActiveEditor();
  1206. expect(activeEditor.isOpened()).toBe(true); // value is invalid, so editor stays opened
  1207. expect(activeEditor.row).toEqual(0);
  1208. expect(activeEditor.col).toEqual(0);
  1209. activeEditor.setValue(2);
  1210. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // save changes and move to cell below (row: 1, col: ś0)
  1211. }, 200);
  1212. setTimeout(() => {
  1213. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // open editor
  1214. activeEditor = hot.getActiveEditor();
  1215. expect(activeEditor.row).toEqual(1);
  1216. expect(activeEditor.col).toEqual(0);
  1217. done();
  1218. }, 400);
  1219. });
  1220. it('should open an appropriate editor after cell value is valid again when columns is a function', (done) => {
  1221. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1222. var hot = handsontable({
  1223. data: arrayOfObjects(),
  1224. columns(column) {
  1225. var colMeta = null;
  1226. if (column === 0) {
  1227. colMeta = {
  1228. data: 'id',
  1229. validator(value, cb) {
  1230. cb(value == parseInt(value, 10));
  1231. },
  1232. allowInvalid: false
  1233. };
  1234. } else if (column === 1) {
  1235. colMeta = {data: 'name'};
  1236. } else if (column === 2) {
  1237. colMeta = {data: 'lastName'};
  1238. }
  1239. return colMeta;
  1240. },
  1241. afterValidate: onAfterValidate
  1242. });
  1243. selectCell(0, 0);
  1244. var activeEditor = hot.getActiveEditor();
  1245. expect(activeEditor.row).toEqual(0);
  1246. expect(activeEditor.col).toEqual(0);
  1247. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // open editor
  1248. activeEditor.setValue('foo');
  1249. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // save changes, close editor
  1250. setTimeout(() => {
  1251. onAfterValidate.calls.reset();
  1252. activeEditor = hot.getActiveEditor();
  1253. expect(activeEditor.isOpened()).toBe(true); // value is invalid, so editor stays opened
  1254. expect(activeEditor.row).toEqual(0);
  1255. expect(activeEditor.col).toEqual(0);
  1256. activeEditor.setValue(2);
  1257. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // save changes and move to cell below (row: 1, col: ś0)
  1258. }, 200);
  1259. setTimeout(() => {
  1260. keyDownUp(Handsontable.helper.KEY_CODES.ENTER); // open editor
  1261. activeEditor = hot.getActiveEditor();
  1262. expect(activeEditor.row).toEqual(1);
  1263. expect(activeEditor.col).toEqual(0);
  1264. done();
  1265. }, 400);
  1266. });
  1267. it('should call the validation callback only once, when using the validateCells method on a mixed set of data', (done) => {
  1268. var onValidate = jasmine.createSpy('onValidate');
  1269. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1270. var hot = handsontable({
  1271. data: [
  1272. {id: 'sth', name: 'Steve'},
  1273. {id: 'sth else', name: 'Bob'}
  1274. ],
  1275. columns: [
  1276. {
  1277. data: 'id',
  1278. validator(value, cb) {
  1279. cb(value == parseInt(value, 10));
  1280. }
  1281. },
  1282. {data: 'name'}
  1283. ],
  1284. afterValidate: onAfterValidate
  1285. });
  1286. hot.validateCells(onValidate);
  1287. setTimeout(() => {
  1288. expect(onValidate).toHaveBeenCalledWith(false);
  1289. expect(onValidate.calls.count()).toEqual(1);
  1290. done();
  1291. }, 200);
  1292. });
  1293. it('should call the validation callback only once, when using the validateCells method on a mixed set of data and when columns is a function', (done) => {
  1294. var onValidate = jasmine.createSpy('onValidate');
  1295. var onAfterValidate = jasmine.createSpy('onAfterValidate');
  1296. var hot = handsontable({
  1297. data: [
  1298. {id: 'sth', name: 'Steve'},
  1299. {id: 'sth else', name: 'Bob'}
  1300. ],
  1301. columns(column) {
  1302. var colMeta = null;
  1303. if (column === 0) {
  1304. colMeta = {
  1305. data: 'id',
  1306. validator(value, cb) {
  1307. cb(value == parseInt(value, 10));
  1308. }
  1309. };
  1310. } else if (column === 1) {
  1311. colMeta = {data: 'name'};
  1312. }
  1313. return colMeta;
  1314. },
  1315. afterValidate: onAfterValidate
  1316. });
  1317. hot.validateCells(onValidate);
  1318. setTimeout(() => {
  1319. expect(onValidate).toHaveBeenCalledWith(false);
  1320. expect(onValidate.calls.count()).toEqual(1);
  1321. done();
  1322. }, 200);
  1323. });
  1324. });