data-validators.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. import _test from 'tape';
  2. import { showAggregate } from '../src/util/sugar.js';
  3. import {
  4. // Basic types
  5. isBoolean,
  6. isCountingNumber,
  7. isNumber,
  8. isString,
  9. isStringNonEmpty,
  10. // Complex types
  11. isArray,
  12. isObject,
  13. validateArrayItems,
  14. // Wiki data
  15. isDimensions,
  16. isDirectory,
  17. isDuration,
  18. isFileExtension,
  19. validateReference,
  20. validateReferenceList,
  21. // Compositional utilities
  22. oneOf,
  23. } from '../src/data/validators.js';
  24. function test(msg, fn) {
  25. _test(msg, t => {
  26. try {
  27. fn(t);
  28. } catch (error) {
  29. if (error instanceof AggregateError) {
  30. showAggregate(error);
  31. }
  32. throw error;
  33. }
  34. });
  35. }
  36. test.skip = _test.skip;
  37. // Basic types
  38. test('isBoolean', t => {
  39. t.plan(4);
  40. t.ok(isBoolean(true));
  41. t.ok(isBoolean(false));
  42. t.throws(() => isBoolean(1), TypeError);
  43. t.throws(() => isBoolean('yes'), TypeError);
  44. });
  45. test('isNumber', t => {
  46. t.plan(6);
  47. t.ok(isNumber(123));
  48. t.ok(isNumber(0.05));
  49. t.ok(isNumber(0));
  50. t.ok(isNumber(-10));
  51. t.throws(() => isNumber('413'), TypeError);
  52. t.throws(() => isNumber(true), TypeError);
  53. });
  54. test('isCountingNumber', t => {
  55. t.plan(6);
  56. t.ok(isCountingNumber(3));
  57. t.ok(isCountingNumber(1));
  58. t.throws(() => isCountingNumber(1.75), TypeError);
  59. t.throws(() => isCountingNumber(0), TypeError);
  60. t.throws(() => isCountingNumber(-1), TypeError);
  61. t.throws(() => isCountingNumber('612'), TypeError);
  62. });
  63. test('isString', t => {
  64. t.plan(3);
  65. t.ok(isString('hello!'));
  66. t.ok(isString(''));
  67. t.throws(() => isString(100), TypeError);
  68. });
  69. test('isStringNonEmpty', t => {
  70. t.plan(4);
  71. t.ok(isStringNonEmpty('hello!'));
  72. t.throws(() => isStringNonEmpty(''), TypeError);
  73. t.throws(() => isStringNonEmpty(' '), TypeError);
  74. t.throws(() => isStringNonEmpty(100), TypeError);
  75. });
  76. // Complex types
  77. test('isArray', t => {
  78. t.plan(3);
  79. t.ok(isArray([]));
  80. t.throws(() => isArray({}), TypeError);
  81. t.throws(() => isArray('1, 2, 3'), TypeError);
  82. });
  83. test.skip('isDate', t => {
  84. // TODO
  85. });
  86. test('isObject', t => {
  87. t.plan(3);
  88. t.ok(isObject({}));
  89. t.ok(isObject([]));
  90. t.throws(() => isObject(null), TypeError);
  91. });
  92. test('validateArrayItems', t => {
  93. t.plan(6);
  94. t.ok(validateArrayItems(isNumber)([3, 4, 5]));
  95. t.ok(validateArrayItems(validateArrayItems(isNumber))([[3, 4], [4, 5], [6, 7]]));
  96. let caughtError = null;
  97. try {
  98. validateArrayItems(isNumber)([10, 20, 'one hundred million consorts', 30]);
  99. } catch (err) {
  100. caughtError = err;
  101. }
  102. t.isNot(caughtError, null);
  103. t.true(caughtError instanceof AggregateError);
  104. t.is(caughtError.errors.length, 1);
  105. t.true(caughtError.errors[0] instanceof TypeError);
  106. });
  107. // Wiki data
  108. test.skip('isColor', t => {
  109. // TODO
  110. });
  111. test.skip('isCommentary', t => {
  112. // TODO
  113. });
  114. test.skip('isContribution', t => {
  115. // TODO
  116. });
  117. test.skip('isContributionList', t => {
  118. // TODO
  119. });
  120. test('isDimensions', t => {
  121. t.plan(6);
  122. t.ok(isDimensions([1, 1]));
  123. t.ok(isDimensions([50, 50]));
  124. t.ok(isDimensions([5000, 1]));
  125. t.throws(() => isDimensions([1]), TypeError);
  126. t.throws(() => isDimensions([413, 612, 1025]), TypeError);
  127. t.throws(() => isDimensions('800x200'), TypeError);
  128. });
  129. test('isDirectory', t => {
  130. t.plan(6);
  131. t.ok(isDirectory('savior-of-the-waking-world'));
  132. t.ok(isDirectory('MeGaLoVania'));
  133. t.ok(isDirectory('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'));
  134. t.throws(() => isDirectory(123), TypeError);
  135. t.throws(() => isDirectory(''), TypeError);
  136. t.throws(() => isDirectory('troll saint nicholas and the quest for the holy pail'), TypeError);
  137. });
  138. test('isDuration', t => {
  139. t.plan(5);
  140. t.ok(isDuration(60));
  141. t.ok(isDuration(0.02));
  142. t.ok(isDuration(0));
  143. t.throws(() => isDuration(-1), TypeError);
  144. t.throws(() => isDuration('10:25'), TypeError);
  145. });
  146. test('isFileExtension', t => {
  147. t.plan(6);
  148. t.ok(isFileExtension('png'));
  149. t.ok(isFileExtension('jpg'));
  150. t.ok(isFileExtension('sub_loc'));
  151. t.throws(() => isFileExtension(''), TypeError);
  152. t.throws(() => isFileExtension('.jpg'), TypeError);
  153. t.throws(() => isFileExtension('just an image bro!!!!'), TypeError);
  154. });
  155. test.skip('isName', t => {
  156. // TODO
  157. });
  158. test.skip('isURL', t => {
  159. // TODO
  160. });
  161. test('validateReference', t => {
  162. t.plan(16);
  163. const typeless = validateReference();
  164. const track = validateReference('track');
  165. const album = validateReference('album');
  166. t.ok(track('track:doctor'));
  167. t.ok(track('track:MeGaLoVania'));
  168. t.ok(track('Showtime (Imp Strife Mix)'));
  169. t.throws(() => track('track:troll saint nic'), TypeError);
  170. t.throws(() => track('track:'), TypeError);
  171. t.throws(() => track('album:homestuck-vol-1'), TypeError);
  172. t.ok(album('album:sburb'));
  173. t.ok(album('album:the-wanderers'));
  174. t.ok(album('Homestuck Vol. 8'));
  175. t.throws(() => album('album:Hiveswap Friendsim'), TypeError);
  176. t.throws(() => album('album:'), TypeError);
  177. t.throws(() => album('track:showtime-piano-refrain'), TypeError);
  178. t.ok(typeless('Hopes and Dreams'));
  179. t.ok(typeless('track:snowdin-town'));
  180. t.throws(() => typeless(''), TypeError);
  181. t.throws(() => typeless('album:undertale-soundtrack'));
  182. });
  183. test('validateReferenceList', t => {
  184. const track = validateReferenceList('track');
  185. const artist = validateReferenceList('artist');
  186. t.plan(9);
  187. t.ok(track(['track:fallen-down', 'Once Upon a Time']));
  188. t.ok(artist(['artist:toby-fox', 'Mark Hadley']));
  189. t.ok(track(['track:amalgam']));
  190. t.ok(track([]));
  191. let caughtError = null;
  192. try {
  193. track(['Dog', 'album:vaporwave-2016', 'Cat', 'artist:john-madden']);
  194. } catch (err) {
  195. caughtError = err;
  196. }
  197. t.isNot(caughtError, null);
  198. t.true(caughtError instanceof AggregateError);
  199. t.is(caughtError.errors.length, 2);
  200. t.true(caughtError.errors[0] instanceof TypeError);
  201. t.true(caughtError.errors[1] instanceof TypeError);
  202. });
  203. test('oneOf', t => {
  204. t.plan(11);
  205. const isStringOrNumber = oneOf(isString, isNumber);
  206. t.ok(isStringOrNumber('hello world'));
  207. t.ok(isStringOrNumber(42));
  208. t.throws(() => isStringOrNumber(false));
  209. const mockError = new Error();
  210. const neverSucceeds = () => {
  211. throw mockError;
  212. };
  213. const isStringOrGetRekt = oneOf(isString, neverSucceeds);
  214. t.ok(isStringOrGetRekt('phew!'));
  215. let caughtError = null;
  216. try {
  217. isStringOrGetRekt(0xdeadbeef);
  218. } catch (err) {
  219. caughtError = err;
  220. }
  221. t.isNot(caughtError, null);
  222. t.true(caughtError instanceof AggregateError);
  223. t.is(caughtError.errors.length, 2);
  224. t.true(caughtError.errors[0] instanceof TypeError);
  225. t.is(caughtError.errors[0].check, isString);
  226. t.is(caughtError.errors[1], mockError);
  227. t.is(caughtError.errors[1].check, neverSucceeds);
  228. });