@nativerent/js-utils 1.0.1 → 1.0.2

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.
@@ -0,0 +1,5 @@
1
+ export default {
2
+ preset: "ts-jest",
3
+ testEnvironment: "jsdom",
4
+ testMatch: ["**/tests/**"],
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativerent/js-utils",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -12,11 +12,15 @@
12
12
  "author": "NativeRent",
13
13
  "license": "GPL-3.0-or-later",
14
14
  "devDependencies": {
15
+ "@types/jest": "^29.5.11",
15
16
  "eslint": "^8.56.0",
17
+ "jest": "^29.7.0",
16
18
  "prettier": "^3.1.1",
17
19
  "rollup": "^4.9.4",
18
20
  "rollup-plugin-dts": "^6.1.0",
19
21
  "rollup-plugin-esbuild": "^6.1.0",
20
- "typescript": "^5.3.3"
22
+ "ts-jest": "^29.1.1",
23
+ "typescript": "^5.3.3",
24
+ "jest-environment-jsdom": "^29.7.0"
21
25
  }
22
26
  }
package/src/index.ts CHANGED
@@ -3,9 +3,9 @@ import { FlattenableObject, Primitive, SimpleObject } from "./types";
3
3
  export function debounce(fn: Function, delay: number): () => void {
4
4
  let timeout: ReturnType<typeof setTimeout>;
5
5
 
6
- return function (this: any, ...args: any[]) {
6
+ return function (...args: any[]) {
7
7
  clearTimeout(timeout);
8
- timeout = setTimeout(() => fn.apply(this, args), delay);
8
+ timeout = setTimeout(() => fn.apply(null, args), delay);
9
9
  };
10
10
  }
11
11
 
@@ -38,6 +38,13 @@ export function isString(str: any): str is string {
38
38
  return typeof str === "string";
39
39
  }
40
40
 
41
+ /**
42
+ * Check if the given argument is a string which is not empty
43
+ */
44
+ export function isNotEmptyString(str: any) {
45
+ return typeof str === "string" && str.length;
46
+ }
47
+
41
48
  export function isHTMLElement(el: any): el is HTMLElement {
42
49
  return el instanceof HTMLElement || el instanceof SVGElement;
43
50
  }
@@ -117,15 +124,24 @@ export function getObjectKeys(object: object): string[] {
117
124
  }
118
125
 
119
126
  /**
120
- * Works with primitive objects, see JSDoc @param
127
+ * Convert an object to a query string
128
+ * Works with primitive objects
121
129
  */
122
130
  export function objectToQueryString(object: {
123
- [key: string]: SimpleObject | { [key: string]: SimpleObject };
131
+ [key: string | number]:
132
+ | Primitive
133
+ | Array<Primitive | null | undefined>
134
+ | SimpleObject
135
+ | { [key: string | number]: SimpleObject }
136
+ | undefined
137
+ | null;
124
138
  }): string {
125
139
  return Object.entries(object)
126
140
  .map(([k, v]) => {
127
141
  if (Array.isArray(v)) {
128
- return v.map((item) => `${k}[]=${encodeURIComponent(item)}`).join("&");
142
+ return v
143
+ .map((item) => `${k}[]=${encodeURIComponent(item || "")}`)
144
+ .join("&");
129
145
  } else if (isObject(v)) {
130
146
  return `${k}=${encodeURIComponent(JSON.stringify(v))}`;
131
147
  }
@@ -155,10 +171,7 @@ export function countObjectInnerLength(object: {
155
171
  /**
156
172
  * Check if the element is in viewport
157
173
  */
158
- export const isElementVisible = (
159
- element: HTMLElement,
160
- minVisiblePercent = 50,
161
- ) => {
174
+ export function isElementVisible(element: HTMLElement, minVisiblePercent = 50) {
162
175
  const elementCoords = element.getBoundingClientRect();
163
176
  const elementHeight = elementCoords.height;
164
177
 
@@ -178,13 +191,6 @@ export const isElementVisible = (
178
191
  elementBottom;
179
192
 
180
193
  return (elementVisibleHeight / elementHeight) * 100 >= minVisiblePercent;
181
- };
182
-
183
- /**
184
- * Find a DOM element where an ad unit should be rendered to
185
- */
186
- export function getHtmlElement(id: string) {
187
- return document.getElementById(id);
188
194
  }
189
195
 
190
196
  export function decodeSafeURL(url: string): string {
@@ -195,7 +201,8 @@ export function decodeSafeURL(url: string): string {
195
201
  }
196
202
  }
197
203
 
198
- export function getSafeURL(url: string): string {
204
+ export function getSafeURL(url?: string): string {
205
+ url = url ? url : location.href;
199
206
  return encodeURI(decodeSafeURL(url));
200
207
  }
201
208
 
@@ -236,6 +243,13 @@ export function toBinaryStr(str: string) {
236
243
  return String.fromCharCode(...charCodes);
237
244
  }
238
245
 
246
+ /**
247
+ * Find a DOM element where an ad unit should be rendered to
248
+ */
249
+ export function getHtmlElement(id: string) {
250
+ return document.getElementById(id);
251
+ }
252
+
239
253
  /**
240
254
  * Convert an HTML string into a list of nodes
241
255
  */
@@ -269,7 +283,7 @@ export function createHtmlElement(
269
283
  */
270
284
  export function insertHtmlElements(
271
285
  nodes: NodeListOf<Node>,
272
- appendTo: Element | null = null,
286
+ appendTo?: Element,
273
287
  ) {
274
288
  appendTo = appendTo || document.body;
275
289
 
@@ -295,7 +309,11 @@ export function insertHtmlElements(
295
309
  flatHtmlAttributes(node.attributes),
296
310
  );
297
311
  } else {
298
- newNode = createScriptElement(node.innerHTML, true);
312
+ newNode = createScriptElement(
313
+ node.innerHTML,
314
+ true,
315
+ flatHtmlAttributes(node.attributes),
316
+ );
299
317
  }
300
318
  break;
301
319
 
@@ -426,6 +444,21 @@ export function createSvgElement(
426
444
  return element;
427
445
  }
428
446
 
447
+ /**
448
+ * Create an image element to use as a tracking pixel
449
+ */
450
+ export function createPixelElement(src: string) {
451
+ const image = document.createElement("img");
452
+
453
+ image.src = src;
454
+ image.width = 1;
455
+ image.height = 1;
456
+ image.style.position = "absolute";
457
+ image.style.left = "-99999px";
458
+
459
+ return image;
460
+ }
461
+
429
462
  /**
430
463
  * Append a new script element to the DOM head
431
464
  */
@@ -438,11 +471,7 @@ export function insertJs(js: string, inline = false) {
438
471
  /**
439
472
  * Append a new style element to the DOM head
440
473
  */
441
- export const insertCss = (
442
- css: string,
443
- className?: string,
444
- external = false,
445
- ) => {
474
+ export function insertCss(css: string, className?: string, external = false) {
446
475
  const element = external ? createLinkElement(css) : createStyleElement(css);
447
476
 
448
477
  if (className) {
@@ -450,7 +479,7 @@ export const insertCss = (
450
479
  }
451
480
 
452
481
  document.head.appendChild(element);
453
- };
482
+ }
454
483
 
455
484
  /**
456
485
  * Get screen sizes
@@ -482,21 +511,6 @@ export function getRandomInt(min = 0, max = 4294967295) {
482
511
  return Math.floor(Math.random() * (max - min)) + min;
483
512
  }
484
513
 
485
- /**
486
- * Create an image element to use as a tracking pixel
487
- */
488
- export function createPixelElement(src: string) {
489
- const image = document.createElement("img");
490
-
491
- image.src = src;
492
- image.width = 1;
493
- image.height = 1;
494
- image.style.position = "absolute";
495
- image.style.left = "-99999px";
496
-
497
- return image;
498
- }
499
-
500
514
  /**
501
515
  * Implementation of Java's String.hashCode() method
502
516
  */
package/src/types.d.ts CHANGED
@@ -3,5 +3,5 @@ export type FlattenableObject = { [key: string]: any };
3
3
  export type Primitive = boolean | number | string;
4
4
 
5
5
  export type SimpleObject = {
6
- [key: string]: Primitive | Primitive[] | null | undefined;
6
+ [key: string | number]: Primitive | Primitive[] | null | undefined;
7
7
  };
package/tests/tests.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  import {
2
+ areDiff,
2
3
  arrayDiff,
4
+ capitalize,
3
5
  debounce,
6
+ filterObjectKeysByValues,
7
+ formatNumber,
8
+ formatNumberWithSign,
9
+ formatWithSign,
10
+ getFromObject,
11
+ getRandomItem,
12
+ getRandomStr,
4
13
  isBool,
5
14
  isDefined,
6
15
  isFn,
@@ -11,9 +20,208 @@ import {
11
20
  isUndef,
12
21
  objectHasProp,
13
22
  objectToQueryString,
23
+ parseObjectPathStr,
14
24
  parseURL,
25
+ roundBigNum,
26
+ roundDown,
27
+ roundUp,
28
+ sortByAlphabet,
29
+ sumArray,
30
+ toCamelCase,
31
+ toPercent,
32
+ toSnakeCase,
15
33
  } from "../src";
16
34
 
35
+ test.each([
36
+ [20, 40, 50],
37
+ [12, 56, 21.428571428571427],
38
+ [489, 232, 210.77586206896552],
39
+ ])("toPercent", (a, b, expected) => {
40
+ const res = toPercent(a, b);
41
+ expect(res).toBe(expected);
42
+ expect(typeof res).toBe("number");
43
+ });
44
+
45
+ test.each([
46
+ ["camelcase", "camelcase"],
47
+ ["camelCase", "camelCase"],
48
+ ["camel-case", "camelCase"],
49
+ ["CAMEL_CASE", "CAMELCASE"],
50
+ ["CAMEL.CASE", "CAMELCASE"],
51
+ ["CAMELCASE", "CAMELCASE"],
52
+ ["fullScreenTGB", "fullScreenTGB"],
53
+ ["typesWith-POPUP_TEASER", "typesWithPOPUPTEASER"],
54
+ ["typesWith-ADVICE", "typesWithADVICE"],
55
+ ["typesWith-button_phone", "typesWithButtonPhone"],
56
+ ["typesWith-buttonPhone", "typesWithButtonPhone"],
57
+ ["item.button_phone", "itemButtonPhone"],
58
+ ["item.buttonPhone", "itemButtonPhone"],
59
+ ["camel.case-camel_CASE", "camelCaseCamelCASE"],
60
+ ])("toCamelCase", (str, expected) => {
61
+ expect(toCamelCase(str)).toBe(expected);
62
+ });
63
+
64
+ test.each([
65
+ ["camelcase", "camelcase"],
66
+ ["camelCase", "camel_case"],
67
+ ["camel-case", "camel_case"],
68
+ ["CAMEL_CASE", "c_a_m_e_l_c_a_s_e"],
69
+ ["CAMEL.CASE", "c_a_m_e_l_c_a_s_e"],
70
+ ["CAMELCASE", "c_a_m_e_l_c_a_s_e"],
71
+ ["fullScreenTGB", "full_screen_t_g_b"],
72
+ ["typesWith-POPUP_TEASER", "types_with_p_o_p_u_p_t_e_a_s_e_r"],
73
+ ["typesWith-ADVICE", "types_with_a_d_v_i_c_e"],
74
+ ["typesWith-button_phone", "types_with_button_phone"],
75
+ ["typesWith-buttonPhone", "types_with_button_phone"],
76
+ ["item.button_phone", "item_button_phone"],
77
+ ["item.buttonPhone", "item_button_phone"],
78
+ ["camel.case-camel_CASE", "camel_case_camel_c_a_s_e"],
79
+ ])("toSnakeCase", (str, expected) => {
80
+ expect(toSnakeCase(str)).toBe(expected);
81
+ });
82
+
83
+ test.each([
84
+ ["title", "Title"],
85
+ ["another title", "Another title"],
86
+ ["UPPER CASE TITLE", "UPPER CASE TITLE"],
87
+ ["CapitalizedTitle", "CapitalizedTitle"],
88
+ ["item.title", "Item.title"],
89
+ ["item-title", "Item-title"],
90
+ ["item_title", "Item_title"],
91
+ ])("capitalize", (str, expected) => {
92
+ expect(capitalize(str)).toBe(expected);
93
+ });
94
+
95
+ test.each([
96
+ ["title", "Title", -1],
97
+ ["another title", "title", -1],
98
+ ["simple title", "compound title", 1],
99
+ ["UPPER CASE TITLE", "TITLE", 1],
100
+ ["кириллица", "сортировка", -1],
101
+ ["ясень", "береза", 1],
102
+ ["Водолей", "Лев", -1],
103
+ ["питер", "мск", 1],
104
+ ["zebra", "зебра", -1],
105
+ ["аббревиатура", "York", 1],
106
+ ])("sortByAlphabet", (first, second, expected) => {
107
+ expect(sortByAlphabet(first, second)).toBe(expected);
108
+ });
109
+
110
+ test.each([
111
+ [undefined, 10],
112
+ [1, 1],
113
+ [43, 43],
114
+ [71, 71],
115
+ [100, 100],
116
+ [450, 100],
117
+ ])("getRandomStr", (len, expectedLen) => {
118
+ const str = getRandomStr(len);
119
+ expect(str.length).toBe(expectedLen);
120
+ expect(typeof str).toBe("string");
121
+ });
122
+
123
+ test.each([
124
+ [[3, "ff", 3434]],
125
+ [[423434234]],
126
+ [[{ ff: "dd" }, 7, ["rrr", "tt", "y"]]],
127
+ ])("getRandomItem", (arr) => {
128
+ expect(arr).toContain(getRandomItem(arr));
129
+ expect(arr).toEqual(arr);
130
+ });
131
+
132
+ test.each([
133
+ [[2, 2, 1, 2], 7],
134
+ [[52, 122, 17, 368], 559],
135
+ [[0.099999999, 1.3028475, 1556.54677, 1000000.02], 1001557.969617499],
136
+ [[undefined, 456, "99", 13, "qwerty", null, 0.46778], 568.46778],
137
+ ])("sumArray", (arr, expected) => {
138
+ expect(sumArray(arr)).toBe(expected);
139
+ });
140
+
141
+ test.each([
142
+ [{ wq: "qwqw", q: 22, add: { s: [1, 2] } }, [22, "qwqw"], ["add"]],
143
+ [{ wq: "qwqw", q: 22, add: { s: [1, 2] } }, ["q", 22], ["wq", "add"]],
144
+ [{ 1: 2, key: "value", t: [1, 2], tt: false }, [false], ["1", "key", "t"]],
145
+ [{ type: undefined, enabled: true, date: null }, [undefined, null, true], []],
146
+ ])("filterObjectKeysByValues", (array, value, expected) => {
147
+ expect(filterObjectKeysByValues(array, value)).toEqual(expected);
148
+ });
149
+
150
+ test.each([
151
+ [{ k: "d" }, "k", "d"],
152
+ [{ title: null }, "title", null],
153
+ [{ 2: null, POS: undefined, g: { f: "roubles" } }, "POS", undefined],
154
+ [{ 2: null, POS: undefined, g: { f: "roubles" } }, "g", { f: "roubles" }],
155
+ [
156
+ {
157
+ array: [5, 657, 7],
158
+ obj: { q: 3, fdf: "gdfg", d: null, e: undefined },
159
+ },
160
+ "obj",
161
+ { q: 3, fdf: "gdfg", d: null, e: undefined },
162
+ ],
163
+ [
164
+ {
165
+ array: [5, 657, 7],
166
+ obj: {
167
+ q: 3,
168
+ fdf: "gdfg",
169
+ d: null,
170
+ e: undefined,
171
+ g: () => {},
172
+ },
173
+ },
174
+ "array",
175
+ [5, 657, 7],
176
+ ],
177
+ ])("getFromObject", (obj, key, expected) => {
178
+ expect(getFromObject(obj, key)).toEqual(expected);
179
+ });
180
+
181
+ test.each([
182
+ ["item.title.value", ["item", "title", "value"]],
183
+ ["item[title][value]", ["item", "title", "value"]],
184
+ ["rules[1][fields.title]", ["rules", "1", "fields", "title"]],
185
+ ["rules[1][fields[title]]", ["rules", "1", "fields", "title"]],
186
+ ["rules.1[fields.title]", ["rules", "1", "fields", "title"]],
187
+ [
188
+ "rules.1[fields.title[regular][NR]]",
189
+ ["rules", "1", "fields", "title", "regular", "NR"],
190
+ ],
191
+ ])("parseObjectPathStr", (path, expected) => {
192
+ expect(parseObjectPathStr(path)).toEqual(expected);
193
+ });
194
+
195
+ test.each([
196
+ [34, 5, "34.00000"],
197
+ ["4546.77", 1, "4 546.8"],
198
+ ["1000.009", 4, "1 000.0090"],
199
+ ["1000.009", 2, "1 000.01"],
200
+ ])("formatNumber", (num, digits, expected) => {
201
+ expect(formatNumber(num, digits)).toBe(expected);
202
+ });
203
+
204
+ test.each([
205
+ [34, 5, "+34.00000"],
206
+ [-4545, 2, "-4 545.00"],
207
+ ["4546.77", 1, "+4 546.8"],
208
+ ["-1000.009", 4, "-1 000.0090"],
209
+ ["1000.009", 2, "+1 000.01"],
210
+ ])("formatNumberWithSign", (num, digits, expected) => {
211
+ expect(formatNumberWithSign(num, digits)).toBe(expected);
212
+ });
213
+
214
+ test.each([
215
+ [34, undefined, "+34"],
216
+ [4545, undefined, "+4545"],
217
+ [4546.77, "4 546.77 руб", "+4 546.77 руб"],
218
+ [-1000.009, "1 000.01&nbsp;₽", "1 000.01&nbsp;₽"],
219
+ [1000.009, "1000.01$", "+1000.01$"],
220
+ [-0.009, undefined, "-0.009"],
221
+ ])("formatWithSign", (num, numStr, expected) => {
222
+ expect(formatWithSign(num, numStr)).toBe(expected);
223
+ });
224
+
17
225
  test.each([
18
226
  [
19
227
  [1, 2, 3],
@@ -222,3 +430,92 @@ test.each([
222
430
  ])("objectHasProp", (obj, prop, expected) => {
223
431
  expect(objectHasProp(obj, prop)).toBe(expected);
224
432
  });
433
+
434
+ test.each([
435
+ ["str", null, true, true],
436
+ ["str", null, false, true],
437
+ ["", null, false, false],
438
+ ["", null, true, true],
439
+ ["", undefined, false, false],
440
+ ["", undefined, true, true],
441
+ ["", "", true, false],
442
+ ["", "", false, false],
443
+ ["1", "11", false, true],
444
+ ["1", "11", true, true],
445
+ ["{}", "object", true, true],
446
+ ["{}", "object", false, true],
447
+ ["{}", "[]", true, true],
448
+ ["{}", "[]", false, true],
449
+ ["{}", "[]", true, true],
450
+ ["title", "Title", false, true],
451
+ ["title", "Title", true, true],
452
+ ["title", "title", true, false],
453
+ [true, "true", true, true],
454
+ [true, "true", false, true],
455
+ ["4", 4, false, false],
456
+ ["4", 4, true, true],
457
+ [1, true, false, false],
458
+ ["1", true, false, false],
459
+ [1, true, true, true],
460
+ [0, false, false, false],
461
+ [0, false, true, true],
462
+ ["0", false, false, false],
463
+ ])("areDiff", (val1, val2, strict, expected) => {
464
+ expect(areDiff(val1, val2, strict)).toBe(expected);
465
+ });
466
+
467
+ test.each([
468
+ [0, 0],
469
+ [27, 27],
470
+ [99, 99],
471
+ [101, 100],
472
+ [277, 300],
473
+ [937, 900],
474
+ [967, 1000],
475
+ [1836, 1800],
476
+ [9284, 9300],
477
+ [29487, 30000],
478
+ [2569424, 2570000],
479
+ [11111111, 11110000],
480
+ [9999999000, 10000000000],
481
+ ])("roundBigNum", (num, expected) => {
482
+ expect(roundBigNum(num)).toBe(expected);
483
+ });
484
+
485
+ test.each([
486
+ [0.9999889999, 0, 1.0],
487
+ [0.9999889999, 1, 1.0],
488
+ [0.9999889999, 2, 1.0],
489
+ [0.0000000001, 0, 1],
490
+ [0.0000000001, 1, 0.1],
491
+ [0.0000000001, 2, 0.01],
492
+ [0.0000000001, 3, 0.001],
493
+ [0.0000000001, 14, 0.0000000001],
494
+ [78.3333333333, 1, 78.4],
495
+ [78.3333333333, 0, 79],
496
+ [10000.354, 3, 10000.354],
497
+ [10000.354, 2, 10000.36],
498
+ [10000.354, 1, 10000.4],
499
+ [10000.354, 0, 10001],
500
+ ])("roundUp", (num, precision, expected) => {
501
+ expect(roundUp(num, precision)).toBe(expected);
502
+ });
503
+
504
+ test.each([
505
+ [0.9999889999, 0, 0],
506
+ [0.9999889999, 1, 0.9],
507
+ [0.9999889999, 2, 0.99],
508
+ [0.0000000001, 0, 0],
509
+ [0.0000000001, 1, 0],
510
+ [0.0000000001, 2, 0],
511
+ [0.0000000001, 3, 0],
512
+ [0.0000000001, 14, 0.0000000001],
513
+ [78.3333333333, 1, 78.3],
514
+ [78.3333333333, 0, 78],
515
+ [10000.354, 3, 10000.354],
516
+ [10000.354, 2, 10000.35],
517
+ [10000.354, 1, 10000.3],
518
+ [10000.354, 0, 10000],
519
+ ])("roundDown", (num, precision, expected) => {
520
+ expect(roundDown(num, precision)).toBe(expected);
521
+ });