utils.spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import {
  2. detectSelectionType,
  3. isValidCoord,
  4. normalizeSelectionFactory,
  5. transformSelectionToColumnDistance,
  6. // TODO: add tests for transformSelectionToRowDistance
  7. // transformSelectionToRowDistance,
  8. SELECTION_TYPE_ARRAY,
  9. SELECTION_TYPE_EMPTY,
  10. SELECTION_TYPE_OBJECT,
  11. SELECTION_TYPE_UNRECOGNIZED,
  12. } from 'handsontable/selection/utils';
  13. import { CellRange, CellCoords } from 'walkontable';
  14. describe('selection utils', () => {
  15. const coords = (row, column) => new CellCoords(row, column);
  16. const range = (
  17. rowC,
  18. columnC,
  19. rowFrom = rowC,
  20. columnFrom = columnC,
  21. rowEnd = rowC,
  22. columnEnd = columnC
  23. ) =>
  24. new CellRange(
  25. coords(rowC, columnC),
  26. coords(rowFrom, columnFrom),
  27. coords(rowEnd, columnEnd)
  28. );
  29. describe('detectSelectionType', () => {
  30. it('should throw an exception when the second argument is being overwritten', () => {
  31. expect(() => detectSelectionType([1, 1], true)).toThrow();
  32. expect(() => detectSelectionType([1, 1], false)).toThrow();
  33. expect(() => detectSelectionType([1, 1], null)).toThrow();
  34. expect(() => detectSelectionType([1, 1], Symbol(''))).toThrow();
  35. expect(() => detectSelectionType([1, 1], Symbol('root'))).toThrow();
  36. expect(() => detectSelectionType([1, 1], Symbol('child'))).toThrow();
  37. });
  38. it('should return EMPTY state when the passed data is empty', () => {
  39. expect(detectSelectionType([])).toBe(SELECTION_TYPE_EMPTY);
  40. expect(detectSelectionType([[]])).toBe(SELECTION_TYPE_EMPTY);
  41. });
  42. it('should return UNRECOGNIZED state when the schema is unrecognized', () => {
  43. expect(detectSelectionType(null)).toBe(SELECTION_TYPE_UNRECOGNIZED);
  44. expect(detectSelectionType(1)).toBe(SELECTION_TYPE_UNRECOGNIZED);
  45. expect(detectSelectionType('z')).toBe(SELECTION_TYPE_UNRECOGNIZED);
  46. expect(detectSelectionType(true)).toBe(SELECTION_TYPE_UNRECOGNIZED);
  47. expect(detectSelectionType([{}])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  48. expect(detectSelectionType({})).toBe(SELECTION_TYPE_UNRECOGNIZED);
  49. expect(detectSelectionType()).toBe(SELECTION_TYPE_UNRECOGNIZED);
  50. });
  51. it('should return UNRECOGNIZED state when the schema is valid but values are incorect or incomplete', () => {
  52. expect(detectSelectionType([1])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  53. expect(detectSelectionType([[1]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  54. expect(detectSelectionType(['prop1'])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  55. expect(detectSelectionType([['prop1']])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  56. expect(detectSelectionType(range(1, 2))).toBe(SELECTION_TYPE_UNRECOGNIZED);
  57. expect(detectSelectionType([[range(1, 2), range(1, 2)]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  58. expect(detectSelectionType([[range(1, 2), range(1, 2), range(1, 2)]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  59. expect(detectSelectionType([[range(1, 2), range(1, 2), range(1, 2), range(1, 2)]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  60. });
  61. it('should ignore nested structures', () => {
  62. expect(detectSelectionType([[[1, 1]]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  63. expect(detectSelectionType([[[1, 1, 2, 2]]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  64. expect(detectSelectionType([[[[1, 1]]]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  65. expect(detectSelectionType([[[[1, 1, 2, 2]]]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  66. expect(detectSelectionType([[[[[1, 1]]]]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  67. expect(detectSelectionType([[[[[1, 1, 2, 2]]]]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  68. expect(detectSelectionType([[[[[range(1, 2), range(1, 2)]]]]])).toBe(SELECTION_TYPE_UNRECOGNIZED);
  69. });
  70. it('should return ARRAY state on valid an array selection schema', () => {
  71. expect(detectSelectionType([1, 1])).toBe(SELECTION_TYPE_ARRAY);
  72. expect(detectSelectionType([1, 1, 2])).toBe(SELECTION_TYPE_ARRAY);
  73. expect(detectSelectionType([1, 1, 2, 2])).toBe(SELECTION_TYPE_ARRAY);
  74. expect(detectSelectionType([1, 'prop1'])).toBe(SELECTION_TYPE_ARRAY);
  75. expect(detectSelectionType([1, 'prop1', 2])).toBe(SELECTION_TYPE_ARRAY);
  76. expect(detectSelectionType([1, 'prop1', 2, 'prop2'])).toBe(SELECTION_TYPE_ARRAY);
  77. expect(detectSelectionType([[1, 1]])).toBe(SELECTION_TYPE_ARRAY);
  78. expect(detectSelectionType([[1, 1, 2]])).toBe(SELECTION_TYPE_ARRAY);
  79. expect(detectSelectionType([[1, 1, 2, 2]])).toBe(SELECTION_TYPE_ARRAY);
  80. expect(detectSelectionType([[1, 'prop1']])).toBe(SELECTION_TYPE_ARRAY);
  81. expect(detectSelectionType([[1, 'prop1', 2]])).toBe(SELECTION_TYPE_ARRAY);
  82. expect(detectSelectionType([[1, 'prop1', 2, 'prop2']])).toBe(SELECTION_TYPE_ARRAY);
  83. });
  84. it('should return OBJECT state on valid range selection schema', () => {
  85. expect(detectSelectionType([range(1, 2)])).toBe(SELECTION_TYPE_OBJECT);
  86. expect(detectSelectionType([range(1, 2), range(1, 2)])).toBe(SELECTION_TYPE_OBJECT);
  87. expect(detectSelectionType([range(1, 2), range(1, 2), range(1, 2)])).toBe(SELECTION_TYPE_OBJECT);
  88. expect(detectSelectionType([range(1, 2), range(1, 2), range(1, 2), range(1, 2)])).toBe(SELECTION_TYPE_OBJECT);
  89. });
  90. });
  91. describe('normalizeSelectionFactory', () => {
  92. it('should throw an exception on invalid type', () => {
  93. expect(() => normalizeSelectionFactory()).toThrow();
  94. expect(() => normalizeSelectionFactory(0)).toThrow();
  95. expect(() => normalizeSelectionFactory('0')).toThrow();
  96. expect(() => normalizeSelectionFactory(null)).toThrow();
  97. expect(() => normalizeSelectionFactory('foo')).toThrow();
  98. });
  99. it('should create normalizer function', () => {
  100. const normalizer = normalizeSelectionFactory(SELECTION_TYPE_ARRAY);
  101. expect(normalizer).toBeFunction();
  102. });
  103. it('should create ARRAY normalizer function with default options (it modifies coordinates direction)', () => {
  104. const normalizer = normalizeSelectionFactory(SELECTION_TYPE_ARRAY);
  105. expect(normalizer([1, 1])).toEqual([1, 1, 1, 1]);
  106. expect(normalizer([1, 1, 2])).toEqual([1, 1, 2, 1]);
  107. expect(normalizer([1, 1, 2, 2])).toEqual([1, 1, 2, 2]);
  108. expect(normalizer([2, 2, 1, 1])).toEqual([1, 1, 2, 2]);
  109. expect(normalizer([2, 'prop2', 1, 'prop1'])).toEqual([1, NaN, 2, NaN]);
  110. });
  111. it('should create OBJECT normalizer function with default options (it modifies coordinates direction)', () => {
  112. const normalizer = normalizeSelectionFactory(SELECTION_TYPE_OBJECT);
  113. expect(normalizer(range(1, 1))).toEqual([1, 1, 1, 1]);
  114. expect(normalizer(range(1, 1, 2, 2))).toEqual([1, 1, 2, 2]);
  115. expect(normalizer(range(2, 2, 1, 1))).toEqual([1, 1, 2, 2]);
  116. });
  117. it('should create ARRAY normalizer function which keep origin coordinates direction', () => {
  118. const normalizer = normalizeSelectionFactory(SELECTION_TYPE_ARRAY, { keepDirection: true });
  119. expect(normalizer([1, 1, 2, 2])).toEqual([1, 1, 2, 2]);
  120. expect(normalizer([2, 2, 1, 1])).toEqual([2, 2, 1, 1]);
  121. });
  122. it('should create OBJECT normalizer function which keep origin coordinates direction', () => {
  123. const normalizer = normalizeSelectionFactory(SELECTION_TYPE_OBJECT, { keepDirection: true });
  124. expect(normalizer(range(1, 1))).toEqual([1, 1, 1, 1]);
  125. expect(normalizer(range(1, 1, 2, 2))).toEqual([2, 2, 1, 1]);
  126. expect(normalizer(range(2, 2, 1, 1))).toEqual([1, 1, 2, 2]);
  127. });
  128. it('should create ARRAY normalizer function which translates column string coordinates to visual indexes', () => {
  129. const propToColMap = new Map([['prop0', 9], ['prop1', 8], ['prop2', 7], ['prop3', 6]]);
  130. const propToCol = prop => propToColMap.get(prop);
  131. const normalizer = normalizeSelectionFactory(SELECTION_TYPE_ARRAY, { propToCol });
  132. expect(normalizer([1, 1, 2, 2])).toEqual([1, 1, 2, 2]);
  133. expect(normalizer([1, 'prop1', 2, 3])).toEqual([1, 3, 2, 8]);
  134. expect(normalizer([1, 'prop1', 2, 'prop2'])).toEqual([1, 7, 2, 8]);
  135. });
  136. });
  137. describe('transformSelectionToColumnDistance', () => {
  138. it('should return an empty array when selection schema is unrecoginized', () => {
  139. expect(transformSelectionToColumnDistance()).toEqual([]);
  140. expect(transformSelectionToColumnDistance(0)).toEqual([]);
  141. expect(transformSelectionToColumnDistance(true)).toEqual([]);
  142. expect(transformSelectionToColumnDistance([])).toEqual([]);
  143. expect(transformSelectionToColumnDistance([1])).toEqual([]);
  144. expect(transformSelectionToColumnDistance([[1], [3, 3, 3, 5]])).toEqual([]);
  145. });
  146. it('should translate selection ranges passed as an array of arrays to column distances', () => {
  147. expect(transformSelectionToColumnDistance([[1, 1, 2, 2]])).toEqual([[1, 2]]);
  148. expect(transformSelectionToColumnDistance([[1, 1], [3, 3, 3, 5]])).toEqual([[1, 1], [3, 3]]);
  149. expect(transformSelectionToColumnDistance([[1, 1], [3, 3, 3, 5], [5, 1, 6, 3]])).toEqual([[1, 5]]);
  150. expect(transformSelectionToColumnDistance([[1, 1], [3, 3, 3, 5], [5, 1, 6, 3], [5, 7, 5, 7]])).toEqual([[1, 5], [7, 1]]);
  151. });
  152. it('should translate selection ranges passed as an array of CellRange objects to column distances', () => {
  153. expect(transformSelectionToColumnDistance([range(1, 1, 1, 1, 2, 2)])).toEqual([[1, 2]]);
  154. expect(transformSelectionToColumnDistance([range(1, 1, 1, 1, 2, 2), range(3, 3, 3, 3, 3, 5)])).toEqual([[1, 5]]);
  155. });
  156. });
  157. describe('isValidCoord', () => {
  158. it('should return `false` on invalid coordinates', () => {
  159. expect(isValidCoord()).toBe(false);
  160. expect(isValidCoord(-1)).toBe(false);
  161. expect(isValidCoord(null)).toBe(false);
  162. expect(isValidCoord(void 0)).toBe(false);
  163. expect(isValidCoord('0')).toBe(false);
  164. expect(isValidCoord('a')).toBe(false);
  165. expect(isValidCoord([1])).toBe(false);
  166. expect(isValidCoord({ foo: 1 })).toBe(false);
  167. });
  168. it('should return `true` on valid coordinates', () => {
  169. expect(isValidCoord(0)).toBe(true);
  170. expect(isValidCoord(1)).toBe(true);
  171. expect(isValidCoord(100)).toBe(true);
  172. expect(isValidCoord(10000000)).toBe(true);
  173. });
  174. it('should return `true` when coordinates are included in the range 0 to `maxItems`', () => {
  175. const maxItems = 100;
  176. expect(isValidCoord(0, maxItems)).toBe(true);
  177. expect(isValidCoord(1, maxItems)).toBe(true);
  178. expect(isValidCoord(99, maxItems)).toBe(true);
  179. expect(isValidCoord(99.999, maxItems)).toBe(true);
  180. });
  181. it('should return `false` when coordinates are not included in the range 0 to `maxItems`', () => {
  182. const maxItems = 100;
  183. expect(isValidCoord(100.000001, maxItems)).toBe(false);
  184. expect(isValidCoord(101, maxItems)).toBe(false);
  185. expect(isValidCoord(9999, maxItems)).toBe(false);
  186. });
  187. });
  188. });