@vbarbarosh/node-helpers 3.74.1 → 3.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/README.md +29 -3
  2. package/dist/browser.js +1 -2504
  3. package/package.json +31 -11
  4. package/src/HeartbeatServer.js +24 -3
  5. package/src/HeartbeatServer.md +33 -0
  6. package/src/array_chunk.js +1 -1
  7. package/src/array_chunk.md +8 -0
  8. package/src/array_chunk_balanced.md +14 -0
  9. package/src/array_fps.md +12 -0
  10. package/src/array_gcd.js +4 -2
  11. package/src/array_gcd.md +2 -1
  12. package/src/array_group.js +3 -3
  13. package/src/array_group.md +14 -0
  14. package/src/array_group_map.js +3 -3
  15. package/src/array_group_map.md +13 -0
  16. package/src/array_index.js +2 -2
  17. package/src/array_index.md +9 -0
  18. package/src/array_lcm.js +4 -2
  19. package/src/array_lcm.md +2 -1
  20. package/src/array_max.js +11 -4
  21. package/src/array_max.md +1 -1
  22. package/src/array_min.js +11 -4
  23. package/src/array_min.md +1 -1
  24. package/src/array_permutations.js +4 -0
  25. package/src/array_permutations.md +13 -0
  26. package/src/array_sort.js +7 -2
  27. package/src/array_sort.md +13 -0
  28. package/src/array_sort_other.js +8 -5
  29. package/src/array_sort_other.md +11 -0
  30. package/src/array_sum.js +2 -2
  31. package/src/array_sum.md +8 -0
  32. package/src/array_unique.js +2 -2
  33. package/src/array_unique_last.js +2 -2
  34. package/src/assert_valid_expires_at.md +10 -0
  35. package/src/clamp.md +9 -0
  36. package/src/cli-apps/watchdog.js +5 -0
  37. package/src/cli.js +3 -1
  38. package/src/cli.md +15 -0
  39. package/src/const_stream.md +7 -0
  40. package/src/const_type.md +5 -0
  41. package/src/countdown.js +22 -5
  42. package/src/countdown.md +21 -0
  43. package/src/crypto_decrypt_aes256.md +13 -0
  44. package/src/crypto_encrypt_aes256.md +13 -0
  45. package/src/crypto_hash_md5.md +10 -0
  46. package/src/crypto_hash_sha256.md +10 -0
  47. package/src/crypto_hmac_sha256.md +11 -0
  48. package/src/date_add_hours.md +8 -0
  49. package/src/date_add_milliseconds.md +4 -0
  50. package/src/date_add_minutes.md +4 -0
  51. package/src/date_add_months.md +13 -0
  52. package/src/date_add_seconds.md +4 -0
  53. package/src/date_diff_seconds.md +4 -0
  54. package/src/date_is_leap_year.md +9 -0
  55. package/src/escape_content_disposition.js +6 -3
  56. package/src/escape_content_disposition.md +13 -0
  57. package/src/escape_regexp.md +7 -0
  58. package/src/eta.js +7 -1
  59. package/src/eta.md +18 -0
  60. package/src/factorize_ms.js +3 -0
  61. package/src/factorize_ms.md +10 -0
  62. package/src/fastdl.js +14 -3
  63. package/src/fcmp_dates.md +12 -0
  64. package/src/fcmp_default.js +7 -0
  65. package/src/fcmp_default.md +29 -0
  66. package/src/fcmp_default_desc.js +4 -7
  67. package/src/fcmp_default_desc.md +10 -0
  68. package/src/fcmp_from_spec.md +13 -0
  69. package/src/fcmp_numbers.md +11 -0
  70. package/src/fcmp_tuples.md +17 -0
  71. package/src/fcmp_utf8_bin.md +14 -0
  72. package/src/fcmp_utf8_ci.md +14 -0
  73. package/src/fcmp_utf8_cs.md +15 -0
  74. package/src/fcmp_utf8_natural_bin.js +38 -0
  75. package/src/fcmp_utf8_natural_bin.md +8 -0
  76. package/src/fcmp_utf8_natural_ci.md +13 -0
  77. package/src/fcmp_utf8_natural_cs.md +13 -0
  78. package/src/fcmpx.md +26 -9
  79. package/src/filter1_from_spec.js +3 -3
  80. package/src/format_bytes.md +15 -0
  81. package/src/format_date.md +7 -0
  82. package/src/format_date_fs.md +7 -0
  83. package/src/format_date_human.md +6 -0
  84. package/src/format_date_ymd.md +6 -0
  85. package/src/format_error_report.js +3 -3
  86. package/src/format_error_report.md +12 -0
  87. package/src/format_kilo.md +13 -0
  88. package/src/format_ms.js +3 -2
  89. package/src/format_ms.md +12 -0
  90. package/src/format_ms2.js +6 -3
  91. package/src/format_ms2.md +12 -0
  92. package/src/format_ms3.js +3 -2
  93. package/src/format_ms3.md +12 -0
  94. package/src/format_percents.md +10 -0
  95. package/src/format_progress_bytes.md +13 -0
  96. package/src/format_progress_kilo.md +10 -0
  97. package/src/format_seconds.md +11 -0
  98. package/src/format_thousands.md +9 -0
  99. package/src/fs_append.md +7 -0
  100. package/src/fs_append_json.md +8 -0
  101. package/src/fs_append_utf8.md +6 -0
  102. package/src/fs_assert_file_exists.md +3 -0
  103. package/src/fs_assert_file_readable.js +1 -1
  104. package/src/fs_assert_file_readable.md +3 -0
  105. package/src/fs_assert_file_socket.md +2 -0
  106. package/src/fs_copy.md +7 -0
  107. package/src/fs_copy_excl.md +2 -0
  108. package/src/fs_exists.md +6 -0
  109. package/src/fs_fclose.md +5 -0
  110. package/src/fs_fi.js +13 -1
  111. package/src/fs_fi.md +11 -0
  112. package/src/fs_file_md5.md +8 -0
  113. package/src/fs_find.md +7 -0
  114. package/src/fs_fopen.md +6 -0
  115. package/src/fs_fread.js +2 -2
  116. package/src/fs_fread.md +11 -0
  117. package/src/fs_fsize.md +2 -0
  118. package/src/fs_fstat.md +2 -0
  119. package/src/fs_fwrite.js +2 -2
  120. package/src/fs_fwrite.md +9 -0
  121. package/src/fs_list.md +5 -0
  122. package/src/fs_list_deep.md +5 -0
  123. package/src/fs_ls.md +2 -0
  124. package/src/fs_lsr.md +4 -0
  125. package/src/fs_lstat.md +3 -0
  126. package/src/fs_mkdir.md +3 -0
  127. package/src/fs_mkdirp.js +3 -30
  128. package/src/fs_mkdirp.md +7 -0
  129. package/src/fs_path_basename.md +2 -0
  130. package/src/fs_path_dirname.md +2 -0
  131. package/src/fs_path_extname.md +9 -0
  132. package/src/fs_path_join.md +2 -0
  133. package/src/fs_path_relative.md +2 -0
  134. package/src/fs_path_resolve.md +2 -0
  135. package/src/fs_path_tempdir.md +2 -0
  136. package/src/fs_path_untildify.md +8 -0
  137. package/src/fs_read.md +3 -0
  138. package/src/fs_read_buffer.md +5 -0
  139. package/src/fs_read_json.md +6 -0
  140. package/src/fs_read_lines.md +7 -0
  141. package/src/fs_read_stream.md +2 -0
  142. package/src/fs_read_utf8.md +5 -0
  143. package/src/fs_readdir.md +3 -0
  144. package/src/fs_readlink.md +6 -0
  145. package/src/fs_rename.md +6 -0
  146. package/src/fs_rm.md +4 -0
  147. package/src/fs_rmdir.md +3 -0
  148. package/src/fs_rmf.md +2 -0
  149. package/src/fs_rmrf.md +8 -0
  150. package/src/fs_sha256.js +16 -0
  151. package/src/fs_sha256.md +9 -0
  152. package/src/fs_size.js +2 -2
  153. package/src/fs_size.md +6 -0
  154. package/src/fs_stat.md +2 -0
  155. package/src/fs_tempdir.js +20 -5
  156. package/src/fs_touch.md +6 -0
  157. package/src/fs_walk.js +2 -2
  158. package/src/fs_walk.md +14 -0
  159. package/src/fs_write.md +3 -0
  160. package/src/fs_write_json.md +7 -0
  161. package/src/fs_write_stream.md +2 -0
  162. package/src/gettype.md +13 -0
  163. package/src/http_delete.md +11 -0
  164. package/src/http_get_blob.md +8 -0
  165. package/src/http_get_buffer.md +10 -0
  166. package/src/http_get_file.js +8 -8
  167. package/src/http_get_file.md +14 -0
  168. package/src/http_get_json.md +10 -0
  169. package/src/http_get_stream.md +14 -0
  170. package/src/http_get_stream_range.js +27 -9
  171. package/src/http_get_stream_range.md +22 -0
  172. package/src/http_get_utf8.md +9 -0
  173. package/src/http_head.md +9 -0
  174. package/src/http_patch_json.md +10 -0
  175. package/src/http_post_json.md +11 -0
  176. package/src/http_post_multipart.md +15 -0
  177. package/src/http_post_urlencoded.md +11 -0
  178. package/src/http_put_buffer.md +9 -0
  179. package/src/http_put_file.js +9 -6
  180. package/src/http_put_file.md +16 -0
  181. package/src/http_put_json.md +11 -0
  182. package/src/http_put_utf8.md +9 -0
  183. package/src/http_range_parse.js +6 -3
  184. package/src/http_range_parse.md +18 -0
  185. package/src/http_stream_range.js +35 -22
  186. package/src/http_stream_range.md +29 -0
  187. package/src/identity.md +2 -0
  188. package/src/ignore.md +2 -0
  189. package/src/json_stringify_safe.md +10 -0
  190. package/src/json_stringify_stable.md +9 -0
  191. package/src/logger.js +2 -3
  192. package/src/logger.md +15 -0
  193. package/src/make_progress.js +8 -5
  194. package/src/make_progress.md +20 -0
  195. package/src/math_gcd.js +2 -1
  196. package/src/mongo_stream_upsert.js +10 -3
  197. package/src/mongo_stream_upsert.md +17 -0
  198. package/src/mongo_stream_write.js +10 -3
  199. package/src/mongo_stream_write.md +16 -0
  200. package/src/msval.md +10 -0
  201. package/src/now_fs.md +7 -0
  202. package/src/now_human.md +6 -0
  203. package/src/object_defaults.md +11 -0
  204. package/src/object_schema.js +2 -1
  205. package/src/object_schema.md +12 -0
  206. package/src/object_walk_preorder.md +16 -0
  207. package/src/parallel.js +23 -10
  208. package/src/parse_bytes.js +4 -2
  209. package/src/parse_bytes.md +15 -0
  210. package/src/parse_hms.js +1 -1
  211. package/src/parse_http_content_range.js +9 -5
  212. package/src/parse_http_content_range.md +16 -0
  213. package/src/perf_end_human.md +10 -0
  214. package/src/perf_end_ms.md +8 -0
  215. package/src/perf_measure.md +10 -0
  216. package/src/perf_measure_human.md +8 -0
  217. package/src/perf_start.md +9 -0
  218. package/src/pgid_exists.md +6 -0
  219. package/src/pgid_kill_grace.js +1 -1
  220. package/src/pgid_kill_grace.md +15 -0
  221. package/src/pid_exists.md +5 -0
  222. package/src/pid_kill_grace.js +1 -1
  223. package/src/pid_kill_grace.md +14 -0
  224. package/src/plural.md +13 -0
  225. package/src/random_hex.md +9 -0
  226. package/src/random_int.md +8 -0
  227. package/src/range.js +4 -2
  228. package/src/range.md +2 -0
  229. package/src/redis_connect.js +1 -1
  230. package/src/redis_connect.md +10 -0
  231. package/src/redis_lshift.md +14 -0
  232. package/src/redis_poll.js +1 -0
  233. package/src/redis_poll.md +17 -0
  234. package/src/redis_poll_zshift_callback_rpush.js +1 -2
  235. package/src/redis_poll_zshift_callback_rpush.md +17 -0
  236. package/src/redis_poll_zshift_spawn_rpush.js +2 -1
  237. package/src/redis_poll_zshift_spawn_rpush.md +20 -0
  238. package/src/redis_zshift.md +14 -0
  239. package/src/round.js +7 -8
  240. package/src/round.md +15 -0
  241. package/src/sanitize_dash_name.md +13 -0
  242. package/src/sanitize_filename.md +9 -0
  243. package/src/sanitize_var_name.md +14 -0
  244. package/src/sftp_get_file_info.js +2 -2
  245. package/src/sftp_get_file_info.md +14 -0
  246. package/src/sftp_get_stream_range.js +32 -22
  247. package/src/sftp_get_stream_range.md +20 -0
  248. package/src/shell_curl_progress.js +2 -1
  249. package/src/shell_curl_progress.md +17 -0
  250. package/src/shell_lines.js +2 -2
  251. package/src/shell_lines.md +1 -1
  252. package/src/shell_spawn.js +7 -0
  253. package/src/shell_ytdlp_progress.js +2 -1
  254. package/src/shell_ytdlp_progress.md +17 -0
  255. package/src/str_camel_to_snake.md +11 -0
  256. package/src/str_parse_kv.md +11 -0
  257. package/src/stream_curl_progress.md +14 -0
  258. package/src/stream_data_ln.js +7 -1
  259. package/src/stream_discard.md +8 -0
  260. package/src/stream_group.md +3 -0
  261. package/src/stream_gunzip.md +7 -0
  262. package/src/stream_hash.md +10 -0
  263. package/src/stream_map.js +8 -1
  264. package/src/stream_map_parallel.js +9 -1
  265. package/src/stream_md5.md +9 -0
  266. package/src/stream_multiplex.js +11 -0
  267. package/src/stream_multiplex.md +16 -0
  268. package/src/stream_through.md +3 -0
  269. package/src/stream_transform.md +2 -0
  270. package/src/stream_xml_analyze.js +9 -1
  271. package/src/stream_xml_parse.js +9 -1
  272. package/src/stream_ytdlp_progress.js +1 -1
  273. package/src/stream_ytdlp_progress.md +18 -0
  274. package/src/strtr.js +15 -5
  275. package/src/strtr.md +11 -0
  276. package/src/urlclean.js +9 -2
  277. package/src/urlmod.js +5 -0
  278. package/src/urlparts.md +17 -0
  279. package/src/wait_while.js +1 -1
  280. package/src/wait_while.md +1 -1
  281. package/src/HeartbeatServer.test.js +0 -31
  282. package/src/array_chunk.test.js +0 -24
  283. package/src/array_chunk_balanced.test.js +0 -37
  284. package/src/array_fps.test.js +0 -72
  285. package/src/array_gcd.test.js +0 -10
  286. package/src/array_index.test.js +0 -14
  287. package/src/array_lcm.test.js +0 -12
  288. package/src/array_max.test.js +0 -20
  289. package/src/array_min.test.js +0 -20
  290. package/src/array_permutations.test.js +0 -20
  291. package/src/array_sort.test.js +0 -85
  292. package/src/array_sort_other.test.js +0 -9
  293. package/src/array_sum.test.js +0 -11
  294. package/src/cli-apps/watchdog.d/connect_close.js +0 -17
  295. package/src/cli-apps/watchdog.d/connect_close2.js +0 -20
  296. package/src/cli-apps/watchdog.d/connect_close3.js +0 -25
  297. package/src/cli-apps/watchdog.d/connect_no_ping.js +0 -13
  298. package/src/cli-apps/watchdog.d/early_exit.js +0 -7
  299. package/src/cli-apps/watchdog.d/flood.js +0 -13
  300. package/src/cli-apps/watchdog.d/flood2.js +0 -15
  301. package/src/cli-apps/watchdog.d/hang.js +0 -8
  302. package/src/cli-apps/watchdog.d/huge_delay.js +0 -18
  303. package/src/cli-apps/watchdog.d/ignore_sigterm.js +0 -29
  304. package/src/cli-apps/watchdog.d/late_heartbeat.js +0 -21
  305. package/src/cli-apps/watchdog.d/my-exit-after-2-pings.js +0 -16
  306. package/src/cli-apps/watchdog.d/my-happy-path.js +0 -16
  307. package/src/cli-apps/watchdog.d/my-ignore-sigterm.js +0 -22
  308. package/src/cli-apps/watchdog.d/my-never-ping.js +0 -15
  309. package/src/cli-apps/watchdog.d/no_heartbeat.js +0 -5
  310. package/src/cli-apps/watchdog.d/ok.js +0 -17
  311. package/src/cli-apps/watchdog.d/quick-exit-after-2-pings.js +0 -15
  312. package/src/cli-apps/watchdog.d/quick-hang-after-1-ping.js +0 -15
  313. package/src/cli-apps/watchdog.d/spawn_children.js +0 -14
  314. package/src/cli-apps/watchdog.test.js +0 -48
  315. package/src/crypto_encrypt_aes256.test.js +0 -10
  316. package/src/crypto_hash_sha256.test.js +0 -10
  317. package/src/date_add_months.test.js +0 -40
  318. package/src/date_is_leap_year.test.js +0 -14
  319. package/src/escape_content_disposition.test.js +0 -22
  320. package/src/fcmp_from_spec.test.js +0 -91
  321. package/src/fcmpx.test.js +0 -173
  322. package/src/filter1_from_spec.test.js +0 -31
  323. package/src/format_bytes.test.js +0 -22
  324. package/src/format_error_report.d/src/1.js +0 -26
  325. package/src/format_error_report.d/src/2.js +0 -29
  326. package/src/format_kilo.test.js +0 -25
  327. package/src/format_ms.test.js +0 -33
  328. package/src/format_ms2.test.js +0 -33
  329. package/src/format_ms3.test.js +0 -33
  330. package/src/format_percents.test.js +0 -22
  331. package/src/format_progress_bytes.test.js +0 -19
  332. package/src/format_progress_kilo.test.js +0 -19
  333. package/src/format_seconds.test.js +0 -42
  334. package/src/format_thousands.test.js +0 -28
  335. package/src/fs_file_md5.test.js +0 -11
  336. package/src/fs_mkdirp.test.js +0 -24
  337. package/src/fs_path_extname.test.js +0 -22
  338. package/src/gettype.test.js +0 -27
  339. package/src/object_schema.test.js +0 -15
  340. package/src/object_walk_preorder.test.js +0 -17
  341. package/src/parallel.test.js +0 -11
  342. package/src/parse_bytes.test.js +0 -21
  343. package/src/parse_hms.test.js +0 -32
  344. package/src/parse_http_content_range.test.js +0 -14
  345. package/src/pid_kill_grace.d/ignore-sigterm-for-10ms.js +0 -33
  346. package/src/pid_kill_grace.d/ignore-sigterm.js +0 -22
  347. package/src/pid_kill_grace.test.js +0 -63
  348. package/src/ping_socket.test.js +0 -58
  349. package/src/plural.test.js +0 -20
  350. package/src/range.test.js +0 -41
  351. package/src/redis_lshift.test.js +0 -57
  352. package/src/redis_zshift.test.js +0 -58
  353. package/src/round.test.js +0 -56
  354. package/src/sanitize_dash_name.test.js +0 -59
  355. package/src/sanitize_var_name.test.js +0 -39
  356. package/src/shell_spawn.test.js +0 -30
  357. package/src/str_camel_to_snake.test.js +0 -17
  358. package/src/stream_curl_progress.d/basic +0 -3
  359. package/src/stream_curl_progress.d/basic.json +0 -42
  360. package/src/stream_curl_progress.test.js +0 -17
  361. package/src/stream_data_ln.test.js +0 -24
  362. package/src/stream_map_parallel.test.js +0 -26
  363. package/src/stream_multiplex.test.js +0 -67
  364. package/src/stream_strpbrk.test.js +0 -29
  365. package/src/urlclean.test.js +0 -18
  366. package/src/urlmod.test.js +0 -40
  367. package/src/urlparts.test.js +0 -294
@@ -1,22 +0,0 @@
1
- const assert = require('assert');
2
- const escape_content_disposition = require('./escape_content_disposition');
3
-
4
- const items = [
5
- ['foo.bar', 'foo.bar'],
6
- ['foo"bar', 'foo\\"bar'],
7
- ['foo;bar', '"foo;bar"'],
8
- ['foo%bar', 'foo\\%bar'],
9
- ['foo\\bar', 'foo\\\\bar'],
10
- ['foo\nbar', 'foo%x0Abar'],
11
- ['foo\0bar', 'foo%x00bar'],
12
- ['foo bar', '"foo bar"'],
13
- ['foo "bar".txt', '"foo \\"bar\\".txt"'],
14
- ];
15
-
16
- describe('escape_content_disposition', function () {
17
- items.forEach(function ([input, expected]) {
18
- it(`${input} → ${expected}`, function () {
19
- assert.strictEqual(escape_content_disposition(input), expected);
20
- });
21
- });
22
- });
@@ -1,91 +0,0 @@
1
- const assert = require('assert');
2
- const fcmp_from_spec = require('./fcmp_from_spec');
3
-
4
- describe('fcmp_from_spec', function () {
5
- it('basic [id] • sort by id (asc)', function () {
6
- const actual = [
7
- {id: 3, name: 'Banana', price: 8.25},
8
- {id: 1, name: 'Apple', price: 10.99},
9
- {id: 2, name: 'Cherry', price: 15.49},
10
- ];
11
- actual.sort(fcmp_from_spec(['id']));
12
- const expected = [
13
- {id: 1, name: 'Apple', price: 10.99},
14
- {id: 2, name: 'Cherry', price: 15.49},
15
- {id: 3, name: 'Banana', price: 8.25},
16
- ];
17
- assert.deepStrictEqual(actual, expected);
18
- });
19
- it('basic [-id] • sort by id (desc)', function () {
20
- const actual = [
21
- {id: 3, name: 'Banana', price: 8.25},
22
- {id: 1, name: 'Apple', price: 10.99},
23
- {id: 2, name: 'Cherry', price: 15.49},
24
- ];
25
- actual.sort(fcmp_from_spec(['-id']));
26
- const expected = [
27
- {id: 3, name: 'Banana', price: 8.25},
28
- {id: 2, name: 'Cherry', price: 15.49},
29
- {id: 1, name: 'Apple', price: 10.99},
30
- ];
31
- assert.deepStrictEqual(actual, expected);
32
- });
33
- it('basic [name] • sort by name (asc)', function () {
34
- const actual = [
35
- {id: 3, name: 'Banana', price: 8.25},
36
- {id: 1, name: 'Apple', price: 10.99},
37
- {id: 2, name: 'Cherry', price: 15.49},
38
- ];
39
- actual.sort(fcmp_from_spec(['name']));
40
- const expected = [
41
- {id: 1, name: 'Apple', price: 10.99},
42
- {id: 3, name: 'Banana', price: 8.25},
43
- {id: 2, name: 'Cherry', price: 15.49},
44
- ];
45
- assert.deepStrictEqual(actual, expected);
46
- });
47
- it('basic [-name] • sort by name (desc)', function () {
48
- const actual = [
49
- {id: 3, name: 'Banana', price: 8.25},
50
- {id: 1, name: 'Apple', price: 10.99},
51
- {id: 2, name: 'Cherry', price: 15.49},
52
- ];
53
- actual.sort(fcmp_from_spec(['-name']));
54
- const expected = [
55
- {id: 2, name: 'Cherry', price: 15.49},
56
- {id: 3, name: 'Banana', price: 8.25},
57
- {id: 1, name: 'Apple', price: 10.99},
58
- ];
59
- assert.deepStrictEqual(actual, expected);
60
- });
61
- it('compound [price,name] • sort by price (asc), name (asc)', function () {
62
- const actual = [
63
- {id: 1, name: 'Apple', price: 10.99},
64
- {id: 2, name: 'Cherry', price: 10.99},
65
- {id: 3, name: 'Banana', price: 8.25},
66
- ];
67
- actual.sort(fcmp_from_spec(['price', 'name']));
68
- const expected = [
69
- {id: 3, name: 'Banana', price: 8.25},
70
- {id: 1, name: 'Apple', price: 10.99},
71
- {id: 2, name: 'Cherry', price: 10.99},
72
- ];
73
- assert.deepStrictEqual(actual, expected);
74
- });
75
- it('compound [-price,name,id] • sort by price (desc), name (asc), id (asc)', function () {
76
- const actual = [
77
- {id: 1, name: 'Apple', price: 10.99},
78
- {id: 2, name: 'Cherry', price: 15.49},
79
- {id: 3, name: 'Banana', price: 8.25},
80
- {id: 4, name: 'Banana', price: 8.25},
81
- ];
82
- actual.sort(fcmp_from_spec(['-price', 'name', 'id']));
83
- const expected = [
84
- {id: 2, name: 'Cherry', price: 15.49},
85
- {id: 1, name: 'Apple', price: 10.99},
86
- {id: 3, name: 'Banana', price: 8.25},
87
- {id: 4, name: 'Banana', price: 8.25},
88
- ];
89
- assert.deepStrictEqual(actual, expected);
90
- });
91
- });
package/src/fcmpx.test.js DELETED
@@ -1,173 +0,0 @@
1
- const assert = require('assert');
2
- const fcmpx = require('./fcmpx');
3
-
4
- const users = [
5
- {id: 'fred|48|100', user: {name: 'fred', age: 48, balance: 100}},
6
- {id: 'barney|36|-200', user: {name: 'barney', age: 36, balance: -200}},
7
- {id: 'wilma|-|-', user: {name: 'wilma', tag: {short: 'foo', long: 'foofoo'}}},
8
- {id: 'betty|32|-', user: {name: 'betty', tag: {short: 'bar', long: 'barbar'}, age: 32}},
9
- ];
10
-
11
- // https://dummyjson.com/docs/products
12
-
13
- const products = [
14
- {id: 1, name: 'Yoga Mat', category: 'Yoga', price: 25.99, brand: 'BalanceFrom', rating: 4.6, discount: 0.1, availability: 'in_stock'},
15
- {id: 2, name: 'Adjustable Dumbbells', category: 'Strength', price: 199.99, brand: 'Bowflex', rating: 4.8, availability: 'preorder'},
16
- {id: 3, name: 'Resistance Bands Set', category: 'Strength', price: 32.5, brand: 'Fit Simplify', rating: 4.5, discount: 0.15, availability: 'in_stock'},
17
- {id: 4, name: 'Treadmill', category: 'Cardio', price: 899, brand: 'NordicTrack', rating: 4.7, availability: 'out_of_stock'},
18
- {id: 5, name: 'Kettlebell 20lb', category: 'Strength', price: 45, brand: 'CAP Barbell', rating: 4.4, discount: 0.05, availability: 'in_stock'},
19
- {id: 6, name: 'Foam Roller', category: 'Recovery', price: 19.99, brand: 'TriggerPoint', rating: 4.6, availability: 'limited'},
20
- {id: 7, name: 'Exercise Bike', category: 'Cardio', price: 599, brand: 'Peloton', rating: 4.9, discount: 0.2, availability: 'in_stock'},
21
- {id: 8, name: 'Pull-Up Bar', category: 'Strength', price: 29.95, brand: 'Iron Gym', rating: 4.3, availability: 'in_stock'},
22
- {id: 9, name: 'Jump Rope', category: 'Cardio', price: 12.99, brand: 'WOD Nation', rating: 4.2, discount: 0.25, availability: 'limited'},
23
- {id: 10, name: 'Ab Roller', category: 'Core', price: 21.49, brand: 'Perfect Fitness', rating: 4.5, availability: 'in_stock'},
24
- ];
25
-
26
- const tests = [
27
- {
28
- description: 'missing: should move undefined values to the bottom',
29
- items: [
30
- {id: 'user1', user: {age: 48}},
31
- {id: 'user2', user: {age: 36}},
32
- {id: 'user3'},
33
- {id: 'user4', user: {age: 32}},
34
- ],
35
- extract: v => v.id,
36
- expressions: [
37
- 'user.age',
38
- v => v.user?.age,
39
- {read: 'user.age'},
40
- {read: v => v.user?.age},
41
- ['user.age'],
42
- [v => v.user?.age],
43
- [{read: 'user.age'}],
44
- [{read: v => v.user?.age}],
45
- ],
46
- expected: ['user4', 'user2', 'user1', 'user3'],
47
- },
48
- {
49
- description: 'missing: should move undefined values to the bottom #2',
50
- items: [
51
- {id: 'user1', user: {age: 48}},
52
- {id: 'user2', user: {age: 36}},
53
- {id: 'user3', user: {tag: {short: 'foo'}}},
54
- {id: 'user4', user: {tag: {short: 'bar'}, age: 32}},
55
- ],
56
- extract: v => v.id,
57
- expressions: [
58
- 'user.age,user.tag.short',
59
- ['user.age', 'user.tag.short'],
60
- [v => v.user.age, v => v.user.tag?.short],
61
- [{read: 'user.age'}, {read: 'user.tag.short'}],
62
- [{read: v => v.user.age}, {read: v => v.user.tag?.short}],
63
- ],
64
- expected: ['user4', 'user2', 'user1', 'user3'],
65
- },
66
- {
67
- description: 'missing: should move undefined values to the bottom and keep their initial order',
68
- items: [
69
- {id: 'user1', user: {balance: 100}},
70
- {id: 'user2', user: {balance: -200}},
71
- {id: 'user3'},
72
- {id: 'user4'},
73
- ],
74
- extract: v => v.id,
75
- expressions: [
76
- 'user.balance',
77
- v => v.user?.balance,
78
- [v => v.user?.balance],
79
- ],
80
- expected: ['user2', 'user1', 'user3', 'user4'],
81
- },
82
- {
83
- description: 'move undefined value to the bottom, keep their initial order',
84
- items: users,
85
- extract: v => v.id,
86
- expressions: [
87
- 'user.balance,user.age',
88
- [v => v.user.balance, v => v.user.age],
89
- ],
90
- expected: ['barney|36|-200', 'fred|48|100', 'betty|32|-', 'wilma|-|-'],
91
- },
92
- {
93
- description: 'move undefined value to the bottom, keep their initial order',
94
- items: users,
95
- extract: v => v.id,
96
- expressions: [
97
- 'user.balance,user.age',
98
- [v => v.user.balance, v => v.user.age],
99
- ],
100
- expected: ['barney|36|-200', 'fred|48|100', 'betty|32|-', 'wilma|-|-'],
101
- },
102
- {
103
- description: 'user.tag.short',
104
- items: [
105
- {user: {name: 'fred', age: 48}},
106
- {user: {name: 'barney', age: 36}},
107
- {user: {name: 'wilma', tag: {short: 'foo'}}},
108
- {user: {name: 'betty', tag: {short: 'bar'}, age: 32}},
109
- ],
110
- extract: v => `${v.user.name}|${v.user.tag?.short ?? '-'}`,
111
- expressions: [
112
- 'user.tag.short',
113
- v => v.user.tag?.short,
114
- [v => v.user.tag?.short],
115
- ],
116
- expected: ['betty|bar', 'wilma|foo', 'fred|-', 'barney|-'],
117
- },
118
- {
119
- description: 'user.tag.short,user.age',
120
- items: [
121
- {user: {name: 'fred', age: 48}},
122
- {user: {name: 'barney', age: 36}},
123
- {user: {name: 'wilma', tag: {short: 'foo'}}},
124
- {user: {name: 'betty', tag: {short: 'bar'}, age: 32}},
125
- ],
126
- extract: v => `${v.user.name}|${v.user.tag?.short ?? ''}|${v.user.age ?? ''}`,
127
- expressions: [
128
- 'user.tag.short,user.age',
129
- ['user.tag.short', 'user.age'],
130
- [v => v.user.tag?.short, v => v.user.age],
131
- ],
132
- expected: ['betty|bar|32', 'wilma|foo|', 'barney||36', 'fred||48'],
133
- },
134
- {
135
- description: 'top: move specified values to the top',
136
- items: [
137
- {id: 'task1', priority: 'normal'},
138
- {id: 'task2', priority: 'low'},
139
- {id: 'task3', priority: 'high'},
140
- {id: 'task4', priority: 'normal'},
141
- ],
142
- extract: v => `${v.id}:${v.priority}`,
143
- expressions: [
144
- {read: 'priority', top: ['high']},
145
- ],
146
- expected: ['task3:high', 'task1:normal', 'task2:low', 'task4:normal'],
147
- },
148
- {
149
- description: 'top: move specified values to the top #2',
150
- items: [
151
- {id: 'task1', priority: 'normal'},
152
- {id: 'task2', priority: 'low'},
153
- {id: 'task3', priority: 'high'},
154
- {id: 'task4', priority: 'normal'},
155
- ],
156
- extract: v => `${v.id}:${v.priority}`,
157
- expressions: [
158
- {read: 'priority', top: ['high', 'normal', 'low']},
159
- ],
160
- expected: ['task3:high', 'task1:normal', 'task4:normal', 'task2:low'],
161
- },
162
- ];
163
-
164
- describe('fcmpx', function () {
165
- tests.forEach(function (test) {
166
- test.expressions.forEach(function (expr, i) {
167
- it(`${test.description}: #${i + 1} | ${test.description}`, function () {
168
- const actual = test.items.slice().sort(fcmpx(expr)).map(test.extract);
169
- assert.deepStrictEqual(actual, test.expected);
170
- });
171
- });
172
- });
173
- });
@@ -1,31 +0,0 @@
1
- const assert = require('assert');
2
- const filter1_from_spec = require('./filter1_from_spec');
3
-
4
- const scenarios = [
5
- // Format: [spec, input, expected, description]
6
- ['substr', 'this is a substr example', true, 'Single substring match'],
7
- ['^start', 'start of the string', true, 'Starts with substring'],
8
- ['end$', 'this is the end', true, 'Ends with substring'],
9
- ['!foo', 'this string does not contain it', true, 'Does not contain substring'],
10
- ['^start/end$', 'start and end', true, 'Starts and ends with substrings'],
11
- ['substr1/substr2', 'contains substr1 and substr2', true, 'Two matches'],
12
- ['substr1/substr2/substr3', 'contains substr1, substr2, and substr3', true, 'Three matches'],
13
- ['substr1/substr2/substr3/substr4', 'contains substr1, substr2, substr3, and substr4', true, 'Four matches'],
14
- ['substr1/substr2/substr3/substr4/substr5', 'contains substr1, substr2, substr3, substr4, substr5', true, 'Five matches'],
15
- ['!not1/!not2', 'this string avoids nota and notb', true, 'Does not contain multiple substrings'],
16
- ['^start1/^start2', 'start1 followed by start2', false, 'Conflicting start substrings'],
17
- ['end1$/end2$', 'ends with end1 or end2', false, 'Conflicting end substrings'],
18
- ['^start1/!substr1', 'start1 without substr', true, 'Starts with start1 and have no substr1'],
19
- ['substr/!not', 'contains substr but no ...', true, 'Combination of positive and negative substrings'],
20
- ['!missing', 'this string does not mention it', true, 'Negative substring absent'],
21
- ['missing', 'this string does not mention it', false, 'Substring missing']
22
- ];
23
-
24
- describe('filter1_from_spec', function () {
25
- scenarios.forEach(function ([spec, input, expected, description]) {
26
- it(`${description || spec}`, function () {
27
- const filter = filter1_from_spec(spec);
28
- assert.strictEqual(filter(input), expected, description);
29
- });
30
- });
31
- });
@@ -1,22 +0,0 @@
1
- const assert = require('assert');
2
- const format_bytes = require('./format_bytes');
3
-
4
- const items = [
5
- ['', 'n/a'],
6
- [null, 'n/a'],
7
- [undefined, 'n/a'],
8
- [0, '0KB'],
9
- [1, '1KB'],
10
- [1023, '1KB'],
11
- [2048, '2KB'],
12
- [35.45*1024*1024, '35.45MB'],
13
- [3.92*1024*1024*1024, '3.92GB'],
14
- ];
15
-
16
- describe('format_bytes', function () {
17
- items.forEach(function ([input, expected]) {
18
- it(`${input} → ${expected}`, function () {
19
- assert.strictEqual(format_bytes(input), expected);
20
- });
21
- });
22
- });
@@ -1,26 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const axios = require('axios');
4
- const cli = require('../../cli');
5
- const express = require('express');
6
- const fs_path_resolve = require('../../fs_path_resolve');
7
- const fs_write = require('../../fs_write');
8
- const json_stringify_safe = require('../../json_stringify_safe');
9
-
10
- cli(main);
11
-
12
- async function main()
13
- {
14
- const app = express();
15
- app.get('/', (req, res) => {});
16
- const server = app.listen(3000);
17
-
18
- try {
19
- await axios('http://127.0.0.1:3000', {timeout: 1});
20
- }
21
- catch (error) {
22
- await fs_write(fs_path_resolve(__dirname, '../1.json'), json_stringify_safe(error, null, 4).replaceAll(fs_path_resolve(__dirname, '../../../..'), '/app'));
23
- }
24
-
25
- server.close();
26
- }
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const axios = require('axios');
4
- const cli = require('../../cli');
5
- const express = require('express');
6
- const fs_path_resolve = require('../../fs_path_resolve');
7
- const fs_write = require('../../fs_write');
8
- const json_stringify_safe = require('../../json_stringify_safe');
9
-
10
- cli(main);
11
-
12
- async function main()
13
- {
14
- const app = express();
15
- app.post('/fail', function (req, res) {
16
- throw new Error('Some Error Here');
17
- });
18
- const server = app.listen(3000);
19
-
20
- try {
21
- await axios.post('http://127.0.0.1:3000/fail');
22
- }
23
- catch (error) {
24
- console.log(error);
25
- await fs_write(fs_path_resolve(__dirname, '../2.json'), json_stringify_safe(error, null, 4).replaceAll(fs_path_resolve(__dirname, '../../../..'), '/app'));
26
- }
27
-
28
- server.close();
29
- }
@@ -1,25 +0,0 @@
1
- const assert = require('assert');
2
- const format_kilo = require('./format_kilo');
3
-
4
- const items = [
5
- ['', 'n/a'],
6
- [null, 'n/a'],
7
- [undefined, 'n/a'],
8
- [0, '0'],
9
- [1, '1'],
10
- [55.45, '55'],
11
- [55.55, '56'],
12
- [900, '900'],
13
- [1500, '1.50K'],
14
- [2000, '2.00K'],
15
- [35.45*1000*1000, '35.45M'],
16
- [3.92*1000*1000*1000, '3.92G'],
17
- ];
18
-
19
- describe('format_kilo', function () {
20
- items.forEach(function ([input, expected]) {
21
- it(`${input} → ${expected}`, function () {
22
- assert.strictEqual(format_kilo(input), expected);
23
- });
24
- });
25
- });
@@ -1,33 +0,0 @@
1
- const assert = require('assert');
2
- const format_ms = require('./format_ms');
3
- const msval = require('./msval');
4
-
5
- const items = [
6
- ['00:00', ''],
7
- ['00:00', null],
8
- ['00:00', undefined],
9
- ['00:00', msval(0, 0, 0, 0)],
10
- ['00:00', msval(0, 0, 0, 1)],
11
- ['00:00', msval(0, 0, 0, 900)],
12
- ['00:01', msval(0, 0, 1, 0)],
13
- ['00:01', msval(0, 0, 1, 500)],
14
- ['00:02', msval(0, 0, 2, 0)],
15
- ['00:05', msval(0, 0, 5, 445)],
16
- ['01:00', msval(0, 1, 0, 0)],
17
- ['12:00', msval(0, 12, 0, 0)],
18
- ['12:12', msval(0, 12, 12, 12)],
19
- ['01:00:00', msval(1, 0, 0, 0)],
20
- ['01:01:00', msval(1, 1, 0, 0)],
21
- ['01:01:01', msval(1, 1, 1, 0)],
22
- ['01:01:01', msval(1, 1, 1, 1)],
23
- ['12:12:12', msval(12, 12, 12, 12)],
24
- ['999:00:00', msval(999, 0, 0, 0)],
25
- ];
26
-
27
- describe('format_ms', function () {
28
- items.forEach(function ([expected, input]) {
29
- it(`${input} → ${expected}`, function () {
30
- assert.strictEqual(format_ms(input), expected);
31
- });
32
- });
33
- });
@@ -1,33 +0,0 @@
1
- const assert = require('assert');
2
- const format_ms2 = require('./format_ms2');
3
- const msval = require('./msval');
4
-
5
- const items = [
6
- ['00:00.00', ''],
7
- ['00:00.00', null],
8
- ['00:00.00', undefined],
9
- ['00:00.00', msval(0, 0, 0, 0)],
10
- ['00:00.00', msval(0, 0, 0, 1)],
11
- ['00:00.90', msval(0, 0, 0, 900)],
12
- ['00:01.00', msval(0, 0, 1, 0)],
13
- ['00:01.50', msval(0, 0, 1, 500)],
14
- ['00:02.00', msval(0, 0, 2, 0)],
15
- ['00:05.45', msval(0, 0, 5, 445)],
16
- ['01:00.00', msval(0, 1, 0, 0)],
17
- ['12:00.00', msval(0, 12, 0, 0)],
18
- ['12:12.01', msval(0, 12, 12, 12)],
19
- ['01:00:00.00', msval(1, 0, 0, 0)],
20
- ['01:01:00.00', msval(1, 1, 0, 0)],
21
- ['01:01:01.00', msval(1, 1, 1, 0)],
22
- ['01:01:01.00', msval(1, 1, 1, 1)],
23
- ['12:12:12.01', msval(12, 12, 12, 12)],
24
- ['999:00:00.00', msval(999, 0, 0, 0)],
25
- ];
26
-
27
- describe('format_ms2', function () {
28
- items.forEach(function ([expected, input]) {
29
- it(`${input} → ${expected}`, function () {
30
- assert.strictEqual(format_ms2(input), expected);
31
- });
32
- });
33
- });
@@ -1,33 +0,0 @@
1
- const assert = require('assert');
2
- const format_ms3 = require('./format_ms3');
3
- const msval = require('./msval');
4
-
5
- const items = [
6
- ['00:00.000', ''],
7
- ['00:00.000', null],
8
- ['00:00.000', undefined],
9
- ['00:00.000', msval(0, 0, 0, 0)],
10
- ['00:00.001', msval(0, 0, 0, 1)],
11
- ['00:00.900', msval(0, 0, 0, 900)],
12
- ['00:01.000', msval(0, 0, 1, 0)],
13
- ['00:01.500', msval(0, 0, 1, 500)],
14
- ['00:02.000', msval(0, 0, 2, 0)],
15
- ['00:05.445', msval(0, 0, 5, 445)],
16
- ['01:00.000', msval(0, 1, 0, 0)],
17
- ['12:00.000', msval(0, 12, 0, 0)],
18
- ['12:12.012', msval(0, 12, 12, 12)],
19
- ['01:00:00.000', msval(1, 0, 0, 0)],
20
- ['01:01:00.000', msval(1, 1, 0, 0)],
21
- ['01:01:01.000', msval(1, 1, 1, 0)],
22
- ['01:01:01.001', msval(1, 1, 1, 1)],
23
- ['12:12:12.012', msval(12, 12, 12, 12)],
24
- ['999:00:00.000', msval(999, 0, 0, 0)],
25
- ];
26
-
27
- describe('format_ms3', function () {
28
- items.forEach(function ([expected, input]) {
29
- it(`${input} → ${expected}`, function () {
30
- assert.strictEqual(format_ms3(input), expected);
31
- });
32
- });
33
- });
@@ -1,22 +0,0 @@
1
- const assert = require('assert');
2
- const format_percents = require('./format_percents');
3
-
4
- const items = [
5
- ['', '0%'],
6
- [0, '0%'],
7
- [0.01, '1.00%'],
8
- [0.0101, '1.01%'],
9
- [0.1, '10.00%'],
10
- [0.5, '50.00%'],
11
- [0.99, '99.00%'],
12
- [0.9901, '99.01%'],
13
- [1, '100%'],
14
- ];
15
-
16
- describe('format_percents', function () {
17
- items.forEach(function ([input, expected]) {
18
- it(`${input} → ${expected}`, function () {
19
- assert.strictEqual(format_percents(input), expected);
20
- });
21
- });
22
- });
@@ -1,19 +0,0 @@
1
- const assert = require('assert');
2
- const format_progress_bytes = require('./format_progress_bytes');
3
-
4
- const items = [
5
- ['~', {percents: 0, total: 0, done: 0, rate: 0, eta: 0, duration: 0}],
6
- ['~ duration=00:00:01', {percents: 0, total: 0, done: 0, rate: 0, eta: 0, duration: 1}],
7
- ['25.25% | 5.0MB of 15.0MB at ~ ETA ~ duration=00:00:01', {percents: 0.2525, total: 15*1024*1024, done: 5*1024*1024, rate: 0, eta: 0, duration: 1}],
8
- ['25.25% | 5.0MB of 15.0MB at 10.0MB/s ETA 00:00:05 duration=00:00:01', {percents: 0.2525, total: 15*1024*1024, done: 5*1024*1024, rate: 10*1024*1024, eta: 5, duration: 1}],
9
- // edge case: done > total
10
- ['15.0MB at 10.0MB/s duration=00:00:01', {percents: 5.2525, total: 5*1024*1024, done: 15*1024*1024, rate: 10*1024*1024, eta: 5, duration: 1}],
11
- ];
12
-
13
- describe('format_progress_bytes', function () {
14
- items.forEach(function ([expected, input]) {
15
- it(expected, function () {
16
- assert.strictEqual(format_progress_bytes(input), expected);
17
- });
18
- });
19
- });
@@ -1,19 +0,0 @@
1
- const assert = require('assert');
2
- const format_progress_kilo = require('./format_progress_kilo');
3
-
4
- const items = [
5
- ['~', {percents: 0, total: 0, done: 0, rate: 0, eta: 0, duration: 0}],
6
- ['~ duration=00:00:01', {percents: 0, total: 0, done: 0, rate: 0, eta: 0, duration: 1}],
7
- ['25.25% | 5.00M of 15.00M at ~ ETA ~ duration=00:00:01', {percents: 0.2525, total: 15*1000*1000, done: 5*1000*1000, rate: 0, eta: 0, duration: 1}],
8
- ['25.25% | 5.00M of 15.00M at 10.00M/s ETA 00:00:05 duration=00:00:01', {percents: 0.2525, total: 15*1000*1000, done: 5*1000*1000, rate: 10*1000*1000, eta: 5, duration: 1}],
9
- // edge case: done > total
10
- ['15.00M at 10.00M/s duration=00:00:01', {percents: 5.2525, total: 5*1000*1000, done: 15*1000*1000, rate: 10*1000*1000, eta: 5, duration: 1}],
11
- ];
12
-
13
- describe('format_progress_kilo', function () {
14
- items.forEach(function ([expected, input]) {
15
- it(expected, function () {
16
- assert.strictEqual(format_progress_kilo(input), expected);
17
- });
18
- });
19
- });
@@ -1,42 +0,0 @@
1
- const assert = require('assert');
2
- const format_seconds = require('./format_seconds');
3
-
4
- const items = [
5
- ['-00:00:01', -1],
6
- ['-00:00:10', -10],
7
- ['-00:01:40', -100],
8
- ['-00:16:40', -1000],
9
- ['-02:46:40', -10000],
10
- ['-27:46:40', -100000],
11
- ['-277:46:40', -1000000],
12
- ['00:00:00', 0],
13
- ['00:00:00', 0.1],
14
- ['00:00:00', 0.12345],
15
- ['00:00:00', 0.5],
16
- ['00:00:00', 0.6],
17
- ['00:00:00', 0.9],
18
- ['00:00:00', 0.99],
19
- ['00:00:00', 0.999],
20
- ['00:00:00', -0.1],
21
- ['00:00:00', -0.12345],
22
- ['00:00:00', -0.5],
23
- ['00:00:00', -0.6],
24
- ['00:00:00', -0.9],
25
- ['00:00:00', -0.99],
26
- ['00:00:00', -0.999],
27
- ['00:00:01', 1],
28
- ['00:00:10', 10],
29
- ['00:01:40', 100],
30
- ['00:16:40', 1000],
31
- ['02:46:40', 10000],
32
- ['27:46:40', 100000],
33
- ['277:46:40', 1000000],
34
- ];
35
-
36
- describe('format_seconds', function () {
37
- items.forEach(function ([expected, ...args]) {
38
- it(`${args} → ${expected}`, function () {
39
- assert.strictEqual(format_seconds(...args), expected);
40
- });
41
- });
42
- });
@@ -1,28 +0,0 @@
1
- const assert = require('assert');
2
- const format_thousands = require('./format_thousands');
3
-
4
- const items = [
5
- ['-1', -1],
6
- ['-100', -100],
7
- ['-1,000', -1000],
8
- ['-10,000', -10000],
9
- ['-100,000', -100000],
10
- ['-1,000,000', -1000000],
11
- ['1', 1],
12
- ['100', 100],
13
- ['1,000', 1000],
14
- ['10,000', 10000],
15
- ['100,000', 100000],
16
- ['1,000,000', 1000000],
17
- ['1.55', 1.55],
18
- ['1.1234512345', 1.1234512345],
19
- ['123,456,789.01234', 123456789.01234],
20
- ];
21
-
22
- describe('format_thousands', function () {
23
- items.forEach(function ([expected, ...args]) {
24
- it(`${args} → ${expected}`, function () {
25
- assert.strictEqual(format_thousands(...args), expected);
26
- });
27
- });
28
- });