@webalternatif/js-core 1.0.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 (118) hide show
  1. package/.babelrc +12 -0
  2. package/.idea/inspectionProfiles/Project_Default.xml +12 -0
  3. package/.idea/js-core.iml +8 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/php.xml +20 -0
  6. package/.idea/vcs.xml +6 -0
  7. package/LICENSE +21 -0
  8. package/README.md +7 -0
  9. package/dist/cjs/array.js +116 -0
  10. package/dist/cjs/dom.js +466 -0
  11. package/dist/cjs/eventDispatcher.js +96 -0
  12. package/dist/cjs/i18n.js +50 -0
  13. package/dist/cjs/index.js +55 -0
  14. package/dist/cjs/is.js +85 -0
  15. package/dist/cjs/math.js +92 -0
  16. package/dist/cjs/random.js +38 -0
  17. package/dist/cjs/string.js +474 -0
  18. package/dist/cjs/stringPrototype.js +16 -0
  19. package/dist/cjs/traversal.js +110 -0
  20. package/dist/cjs/utils.js +118 -0
  21. package/dist/esm/array.js +116 -0
  22. package/dist/esm/dom.js +466 -0
  23. package/dist/esm/eventDispatcher.js +96 -0
  24. package/dist/esm/i18n.js +50 -0
  25. package/dist/esm/index.js +55 -0
  26. package/dist/esm/is.js +85 -0
  27. package/dist/esm/math.js +92 -0
  28. package/dist/esm/random.js +38 -0
  29. package/dist/esm/string.js +474 -0
  30. package/dist/esm/stringPrototype.js +16 -0
  31. package/dist/esm/traversal.js +110 -0
  32. package/dist/esm/utils.js +118 -0
  33. package/i18n/agenda/en.js +73 -0
  34. package/i18n/agenda/fr.js +73 -0
  35. package/i18n/agenda/index.js +2 -0
  36. package/i18n/ajaxform/en.js +5 -0
  37. package/i18n/ajaxform/fr.js +5 -0
  38. package/i18n/ajaxform/index.js +2 -0
  39. package/i18n/ajaxupload/en.js +12 -0
  40. package/i18n/ajaxupload/fr.js +12 -0
  41. package/i18n/ajaxupload/index.js +2 -0
  42. package/i18n/autocomplete/en.js +3 -0
  43. package/i18n/autocomplete/fr.js +3 -0
  44. package/i18n/autocomplete/index.js +2 -0
  45. package/i18n/confirm/en.js +5 -0
  46. package/i18n/confirm/fr.js +5 -0
  47. package/i18n/confirm/index.js +2 -0
  48. package/i18n/core/en.js +4 -0
  49. package/i18n/core/fr.js +4 -0
  50. package/i18n/core/index.js +2 -0
  51. package/i18n/datagrid/en.js +8 -0
  52. package/i18n/datagrid/fr.js +8 -0
  53. package/i18n/datagrid/index.js +2 -0
  54. package/i18n/date/en.js +51 -0
  55. package/i18n/date/fr.js +51 -0
  56. package/i18n/date/index.js +2 -0
  57. package/i18n/datetimepicker/en.js +30 -0
  58. package/i18n/datetimepicker/fr.js +30 -0
  59. package/i18n/datetimepicker/index.js +2 -0
  60. package/i18n/dialog/en.js +3 -0
  61. package/i18n/dialog/fr.js +3 -0
  62. package/i18n/dialog/index.js +2 -0
  63. package/i18n/fulldayeventagenda/en.js +73 -0
  64. package/i18n/fulldayeventagenda/fr.js +73 -0
  65. package/i18n/fulldayeventagenda/index.js +2 -0
  66. package/i18n/index.d.ts +4 -0
  67. package/i18n/index.js +15 -0
  68. package/i18n/richtexteditor/en.js +58 -0
  69. package/i18n/richtexteditor/fr.js +58 -0
  70. package/i18n/richtexteditor/index.js +2 -0
  71. package/i18n/select/en.js +3 -0
  72. package/i18n/select/fr.js +3 -0
  73. package/i18n/select/index.js +2 -0
  74. package/i18n/timepicker/en.js +3 -0
  75. package/i18n/timepicker/fr.js +3 -0
  76. package/i18n/timepicker/index.js +2 -0
  77. package/i18n/useragenda/en.js +74 -0
  78. package/i18n/useragenda/fr.js +73 -0
  79. package/i18n/useragenda/index.js +2 -0
  80. package/jest.config.js +14 -0
  81. package/package.json +33 -0
  82. package/src/array.js +124 -0
  83. package/src/dom.js +569 -0
  84. package/src/eventDispatcher.js +118 -0
  85. package/src/i18n.js +55 -0
  86. package/src/index.js +33 -0
  87. package/src/is.js +89 -0
  88. package/src/math.js +109 -0
  89. package/src/random.js +40 -0
  90. package/src/string.js +576 -0
  91. package/src/stringPrototype.js +15 -0
  92. package/src/traversal.js +134 -0
  93. package/src/utils.js +130 -0
  94. package/tests/array.test.js +326 -0
  95. package/tests/dom.test.js +239 -0
  96. package/tests/eventdispatcher.test.js +177 -0
  97. package/tests/i18n.test.js +132 -0
  98. package/tests/index.test.js +29 -0
  99. package/tests/is.test.js +354 -0
  100. package/tests/math.test.js +221 -0
  101. package/tests/random.test.js +72 -0
  102. package/tests/string.test.js +1106 -0
  103. package/tests/traversal.test.js +517 -0
  104. package/tests/utils.test.js +371 -0
  105. package/tsconfig.json +16 -0
  106. package/types/array.d.ts +8 -0
  107. package/types/dom.d.ts +241 -0
  108. package/types/eventDispatcher.d.ts +12 -0
  109. package/types/i18n.d.ts +4 -0
  110. package/types/index.d.ts +139 -0
  111. package/types/is.d.ts +16 -0
  112. package/types/math.d.ts +7 -0
  113. package/types/random.d.ts +7 -0
  114. package/types/string.d.ts +37 -0
  115. package/types/stringPrototype.d.ts +1 -0
  116. package/types/traversal.d.ts +7 -0
  117. package/types/utils.d.ts +7 -0
  118. package/webpack.config.cjs +31 -0
@@ -0,0 +1,239 @@
1
+ import dom, {isWindow, isDomElement, getStyle} from "../src/dom.js";
2
+
3
+ describe('dom methods', () => {
4
+ describe('isWindow()', () => {
5
+ it('should returns true for window object', () => {
6
+ expect(isWindow(window)).toBe(true);
7
+ });
8
+
9
+ it('should return false for a DOM element', () => {
10
+ const div = document.createElement('div');
11
+ expect(isWindow(div)).toBe(false);
12
+ });
13
+
14
+ it('should return false for primitives', () => {
15
+ expect(isWindow(123)).toBe(false);
16
+ expect(isWindow(null)).toBe(false);
17
+ expect(isWindow('string')).toBe(false);
18
+ });
19
+ });
20
+
21
+ describe('isDomElement()', () => {
22
+ it('should return true for a DOM element', () => {
23
+ const div = document.createElement('div');
24
+ expect(isDomElement(div)).toBe(true);
25
+ });
26
+
27
+ it('should return false for non-DOM objects', () => {
28
+ expect(isDomElement({})).toBe(false);
29
+ expect(isDomElement(null)).toBe(false);
30
+ expect(isDomElement(undefined)).toBe(false);
31
+ expect(isDomElement(42)).toBe(false);
32
+ expect(isDomElement('string')).toBe(false);
33
+ expect(isDomElement({ nodeType: 1 })).toBe(false);
34
+ });
35
+ })
36
+
37
+ describe('getStyle', () => {
38
+ const mockElement = (styles = {}) => {
39
+ const element = document.createElement('div');
40
+ Object.assign(element.style, styles);
41
+ return element;
42
+ };
43
+
44
+ const mockNonDomElement = () => ({});
45
+
46
+ it('should return empty string for non-DOM elements', () => {
47
+ const nonDomElement = mockNonDomElement();
48
+ expect(getStyle(nonDomElement, 'color')).toBe('');
49
+ });
50
+
51
+ it('should return the computed style for a valid DOM element', () => {
52
+ const elem = mockElement({color: 'red'});
53
+ document.body.appendChild(elem);
54
+
55
+ expect(getStyle(elem, 'color')).toBe('red');
56
+ document.body.removeChild(elem);
57
+ });
58
+
59
+ it('should return inline style when no computed style is available', () => {
60
+ const elem = mockElement({ backgroundColor: 'blue' });
61
+ expect(getStyle(elem, 'background-color')).toBe('blue');
62
+ });
63
+
64
+ it('should return camelCase style for kebab-case properties', () => {
65
+ const elem = mockElement({ backgroundColor: 'green' });
66
+ expect(getStyle(elem, 'background-color')).toBe('green');
67
+ });
68
+
69
+ it('should return empty string for undefined styles', () => {
70
+ const elem = mockElement();
71
+ expect(getStyle(elem, 'color')).toBe('');
72
+ });
73
+
74
+ it('should return empty string for invalid cssRule', () => {
75
+ const elem = mockElement({ color: 'red' });
76
+ expect(getStyle(elem, 'invalid-rule')).toBe('');
77
+ });
78
+
79
+ it('should handle cases where getComputedStyle is unavailable', () => {
80
+ const originalGetComputedStyle = window.getComputedStyle;
81
+ delete window.getComputedStyle;
82
+
83
+ const elem = mockElement({ color: 'blue' });
84
+ expect(getStyle(elem, 'color')).toBe('blue');
85
+ expect(getStyle(elem, 'notexists')).toBe('');
86
+
87
+ window.getComputedStyle = originalGetComputedStyle;
88
+ });
89
+ });
90
+ });
91
+
92
+ describe('dom manipulation', () => {
93
+ /** @type {HTMLDivElement} */
94
+ let el;
95
+
96
+ beforeEach(() => {
97
+ el = document.createElement('div');
98
+ el.id = 'root';
99
+
100
+ for (let i = 0; i < 3; i++) {
101
+ const child = document.createElement('div');
102
+ child.classList.add('child');
103
+ child.classList.add(`child${i}`);
104
+ el.append(child);
105
+
106
+ for (let j = 0; j < 3; j++) {
107
+ const grandchild = document.createElement('div');
108
+ grandchild.classList.add('grandchild');
109
+ grandchild.classList.add(`grandchild${j}`);
110
+ child.append(grandchild);
111
+ }
112
+ }
113
+
114
+ document.body.append(el);
115
+ })
116
+
117
+ afterEach(() => document.body.innerHTML = '');
118
+
119
+ describe('children()', () => {
120
+ it('should return children of element', () => {
121
+ expect(dom.children(el).length).toBe(3);
122
+ expect(dom.children(el, '.child1').length).toBe(1);
123
+ expect(dom.children(el, '.notexists').length).toBe(0);
124
+ });
125
+ })
126
+
127
+ describe('child()', () => {
128
+ it('should return one child of element', () => {
129
+ const firstChild = el.children[0];
130
+ const secondChild = el.children[1];
131
+
132
+ expect(dom.child(el)).toBe(firstChild);
133
+ expect(dom.child(el, '.child1')).toBe(secondChild);
134
+ expect(dom.child(el, '.notexists')).toBe(null);
135
+ })
136
+ })
137
+
138
+ describe('find()', () => {
139
+ it('should return descendants of element', () => {
140
+ expect(dom.find(el).length).toBe(12);
141
+ expect(dom.find(el, '.grandchild').length).toBe(9);
142
+ expect(dom.find('.grandchild').length).toBe(9);
143
+ })
144
+
145
+ it('should return an empty list on invalid selector', () => {
146
+ expect(dom.find(el, '!!!').length).toBe(0);
147
+ })
148
+ })
149
+
150
+ describe('findOne()', () => {
151
+ it('should return one descendant of element', () => {
152
+ const child = el.children[0];
153
+ const grandChild = child.children[0];
154
+
155
+ expect(dom.findOne(el)).toBe(child);
156
+ expect(dom.findOne(el, '.grandchild')).toBe(grandChild);
157
+ expect(dom.findOne('.grandchild')).toBe(grandChild);
158
+ })
159
+
160
+ it('should return null on invalid selector', () => {
161
+ expect(dom.findOne(el, '!!!')).toBe(null);
162
+ })
163
+ })
164
+
165
+ describe('addClass()', () => {
166
+ it('should add classes to element', () => {
167
+ dom.addClass(el, 'perceval karadoc');
168
+
169
+ expect(el.classList.contains('perceval')).toBe(true);
170
+ expect(el.classList.contains('karadoc')).toBe(true);
171
+ })
172
+
173
+ it('should handle empty string', () => {
174
+ const before = [...el.classList];
175
+ dom.addClass(el, '');
176
+ expect([...el.classList]).toEqual(before);
177
+ })
178
+ })
179
+
180
+ describe('removeClass()', () => {
181
+ it('should remove classes to element', () => {
182
+ const child = el.children[0];
183
+ dom.removeClass(child, 'child child1');
184
+
185
+ expect(el.classList.contains('child')).toBe(false);
186
+ expect(el.classList.contains('child1')).toBe(false);
187
+ })
188
+
189
+ it('should handle empty string', () => {
190
+ el.classList.add('root');
191
+
192
+ const before = [...el.classList];
193
+ dom.removeClass(el, '');
194
+ expect([...el.classList]).toEqual(before);
195
+ })
196
+ })
197
+
198
+ describe('toggleClass()', () => {
199
+ it('should toggle classes to element', () => {
200
+ const child = el.children[0];
201
+
202
+ dom.toggleClass(child, 'foo');
203
+ expect(child.classList.contains('foo')).toBe(true);
204
+
205
+ dom.toggleClass(child, 'foo child1');
206
+ expect(child.classList.contains('foo')).toBe(false);
207
+ expect(child.classList.contains('child1')).toBe(false);
208
+ })
209
+
210
+ it('should handle empty string', () => {
211
+ el.classList.add('root');
212
+
213
+ const before = [...el.classList];
214
+ dom.toggleClass(el, '');
215
+ expect([...el.classList]).toEqual(before);
216
+ })
217
+ })
218
+ })
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
@@ -0,0 +1,177 @@
1
+ import eventDispatcher from '../src/eventDispatcher';
2
+ import {sizeOf} from "../src/utils.js";
3
+
4
+ describe('eventDispatcher', () => {
5
+ beforeEach(() => {
6
+ eventDispatcher.reset();
7
+ })
8
+
9
+ it('should add a listener and trigger the event', () => {
10
+ const callback = jest.fn();
11
+ eventDispatcher.addListener('testEvent', callback);
12
+
13
+ eventDispatcher.dispatch('testEvent', 'arg1', 'arg2');
14
+
15
+ expect(callback).toHaveBeenCalledTimes(1);
16
+ expect(callback).toHaveBeenCalledWith('testEvent', 'arg1', 'arg2');
17
+ })
18
+
19
+ it('should handle multiple listeners for the same event', () => {
20
+ const callback1 = jest.fn();
21
+ const callback2 = jest.fn();
22
+
23
+ eventDispatcher.addListener('testEvent', callback1);
24
+ eventDispatcher.addListener('testEvent', callback2);
25
+
26
+ eventDispatcher.dispatch('testEvent', 'arg1');
27
+
28
+ expect(callback1).toHaveBeenCalledTimes(1);
29
+ expect(callback2).toHaveBeenCalledTimes(1);
30
+ })
31
+
32
+ it('should add a listener that triggers only once', () => {
33
+ const callback = jest.fn();
34
+
35
+ eventDispatcher.addListenerOnce('testEvent', callback);
36
+
37
+ eventDispatcher.dispatch('testEvent', 'arg1');
38
+ eventDispatcher.dispatch('testEvent', 'arg2');
39
+
40
+ expect(callback).toHaveBeenCalledTimes(1);
41
+ expect(callback).toHaveBeenCalledWith('testEvent', 'arg1');
42
+ })
43
+
44
+ it('should remove a specific listener', () => {
45
+ const callback = jest.fn();
46
+
47
+ eventDispatcher.addListener('testEvent', callback);
48
+ eventDispatcher.removeListener('testEvent', callback);
49
+
50
+ eventDispatcher.dispatch('testEvent', 'arg1');
51
+
52
+ expect(callback).not.toHaveBeenCalled();
53
+ })
54
+
55
+ it('should remove all listeners for an event', () => {
56
+ const callback1 = jest.fn();
57
+ const callback2 = jest.fn();
58
+
59
+ eventDispatcher.addListener('testEvent', callback1);
60
+ eventDispatcher.addListener('testEvent', callback2);
61
+
62
+ eventDispatcher.removeListener('testEvent');
63
+
64
+ eventDispatcher.dispatch('testEvent', 'arg1');
65
+
66
+ expect(callback1).not.toHaveBeenCalled();
67
+ expect(callback2).not.toHaveBeenCalled();
68
+ })
69
+
70
+ it('should check if a listener exists', () => {
71
+ const callback = jest.fn();
72
+
73
+ eventDispatcher.addListener('testEvent', callback);
74
+
75
+ expect(eventDispatcher.hasListener('testEvent')).toBe(true);
76
+ expect(eventDispatcher.hasListener('nonExistentEvent')).toBe(false);
77
+ })
78
+
79
+ it('should handle events with multiple names', () => {
80
+ const callback1 = jest.fn();
81
+ const callback2 = jest.fn();
82
+
83
+ eventDispatcher.addListener('event1, event2', callback1);
84
+ eventDispatcher.addListener('event2, event3', callback2);
85
+
86
+ eventDispatcher.dispatch('event2', 'arg');
87
+
88
+ expect(callback1).toHaveBeenCalledTimes(1);
89
+ expect(callback2).toHaveBeenCalledTimes(1);
90
+ })
91
+
92
+ it('should handle an empty event name gracefully', () => {
93
+ const callback = jest.fn();
94
+
95
+ eventDispatcher.addListener('', callback);
96
+ eventDispatcher.dispatch('');
97
+
98
+ expect(callback).not.toHaveBeenCalled();
99
+ })
100
+
101
+ it('should handle undefined or null event names', () => {
102
+ expect(() => eventDispatcher.addListener(undefined, jest.fn())).toThrow();
103
+ expect(() => eventDispatcher.addListener(null, jest.fn())).toThrow();
104
+ })
105
+
106
+ it('should not fail if dispatching an event with no listeners', () => {
107
+ expect(() => eventDispatcher.dispatch('nonExistentEvent')).not.toThrow();
108
+ })
109
+
110
+ it('should maintain listener context when specified', () => {
111
+ const context = { value: 42 };
112
+ const callback = jest.fn(function () {
113
+ expect(this).toBe(context);
114
+ })
115
+
116
+ eventDispatcher.addListener('testEvent', callback, context);
117
+ eventDispatcher.dispatch('testEvent');
118
+ expect(callback).toHaveBeenCalledTimes(1);
119
+ })
120
+
121
+ it('should throw an error if callback is not a function', () => {
122
+ expect(() => eventDispatcher.addListener('testEvent', 'not a function')).toThrow();
123
+ })
124
+
125
+ it('should throw an error if events name are not a string', () => {
126
+ expect(() => eventDispatcher.dispatch(null)).toThrow();
127
+ })
128
+
129
+ it('should do nothing if an empty event name is provided', () => {
130
+ const mockCallback = jest.fn();
131
+ eventDispatcher.addListenerOnce('', mockCallback);
132
+
133
+ eventDispatcher.dispatch('someEvent');
134
+ expect(mockCallback).not.toHaveBeenCalled();
135
+ })
136
+
137
+ it('should return false if callback and context do not match any listener', () => {
138
+ const callback = jest.fn();
139
+ const context = { some: 'context' };
140
+ eventDispatcher.addListener('testEvent', callback, { other: 'context' })
141
+
142
+ const result = eventDispatcher.hasListener('testEvent', callback, context);
143
+
144
+ expect(result).toBe(false);
145
+ })
146
+
147
+ it('should do nothing if listener does not exists', () => {
148
+ const callback = jest.fn();
149
+ const result = eventDispatcher.removeListener('nonExistentEvent', callback);
150
+
151
+ expect(result).toBe(eventDispatcher);
152
+ expect(eventDispatcher.hasListener('nonExistentEvent')).toBe(false);
153
+ })
154
+
155
+ it('should not remove a listener if callback or context does not match', () => {
156
+ const callback1 = jest.fn();
157
+ const callback2 = jest.fn();
158
+ const context1 = { some: 'context1' };
159
+ const context2 = { some: 'context2' };
160
+
161
+ eventDispatcher.addListener('testEvent', callback1, context1);
162
+ eventDispatcher.addListener('testEvent', callback2, context2);
163
+
164
+ eventDispatcher.removeListener('testEvent', callback1, context2);
165
+
166
+ const allListeners = eventDispatcher.getListeners();
167
+ const listeners = eventDispatcher.getListeners('testEvent');
168
+
169
+ expect(sizeOf(allListeners)).toBe(1);
170
+ expect(eventDispatcher.getListeners('notExists')).toHaveLength(0);
171
+ expect(listeners).toHaveLength(2);
172
+ expect(listeners[0].callback).toBe(callback1);
173
+ expect(listeners[0].context).toBe(context1);
174
+ expect(listeners[1].callback).toBe(callback2);
175
+ expect(listeners[1].context).toBe(context2);
176
+ });
177
+ })
@@ -0,0 +1,132 @@
1
+ import {translate, _, setLang, getLang} from '../src/i18n.js';
2
+
3
+ jest.mock('../i18n/index', () => {
4
+ return {
5
+ core: {
6
+ en: { hello: 'Hello', goodbye: 'Goodbye' },
7
+ fr: { hello: 'Bonjour' }
8
+ },
9
+ date: {
10
+ en: { today: 'Today', tomorrow: () => 'Tomorrow' },
11
+ fr: { today: 'Aujourd\'hui', tomorrow: () => 'Demain' }
12
+ }
13
+ };
14
+ })
15
+
16
+ describe('Translation system', () => {
17
+ beforeEach(() => {
18
+ Object.defineProperty(global, 'navigator', {
19
+ value: { language: 'fr-FR' },
20
+ configurable: true
21
+ })
22
+ process.env.LANG = 'fr-FR.UTF-8';
23
+ process.env.LC_ALL = 'fr-FR.UTF-8';
24
+ process.env.LC_MESSAGES = 'fr-FR.UTF-8';
25
+ })
26
+
27
+ it('should check process.env and navigator.language', () => {
28
+ expect(process.env.LANG).toBe('fr-FR.UTF-8');
29
+ expect(navigator.language).toBe('fr-FR');
30
+ })
31
+
32
+ it('should return the correct translation for a given language and namespace', () => {
33
+ expect(translate('fr', 'core', 'hello')).toBe('Bonjour');
34
+ expect(translate('en', 'date', 'today')).toBe('Today');
35
+ expect(translate('fr', 'date', 'tomorrow')).toBe('Demain');
36
+ expect(translate('en', 'date', 'tomorrow')).toBe('Tomorrow');
37
+ })
38
+
39
+ it('should fallback to English if translation is missing in the given language', () => {
40
+ expect(translate('fr', 'core', 'goodbye')).toBe('Goodbye');
41
+ })
42
+
43
+ it('should fallback to the label if the translation is missing in all languages', () => {
44
+ expect(translate('fr', 'core', 'unknown')).toBe('unknown');
45
+ })
46
+
47
+ it('should default to the current language when no language is specified', () => {
48
+ expect(getLang()).toBe('fr');
49
+ expect(translate('core', 'hello')).toBe('Bonjour');
50
+ })
51
+
52
+ it('should fallback to namespace "core" if no namespace is specified', () => {
53
+ expect(translate('hello')).toBe('Bonjour');
54
+ })
55
+
56
+ it('should return the same result for translate and _', () => {
57
+ expect(translate('en', 'core', 'hello')).toBe(_('en', 'core', 'hello'));
58
+ })
59
+ })
60
+
61
+ describe('Language detection and setting', () => {
62
+ let originalNavigator, originalProcess;
63
+
64
+ beforeEach(() => {
65
+ originalProcess = process;
66
+ process.env.LANG = 'fr-FR.UTF-8';
67
+
68
+ originalNavigator = { ...global.navigator };
69
+ Object.defineProperty(global.navigator, 'language', {
70
+ value: 'fr-FR',
71
+ configurable: true
72
+ })
73
+ })
74
+
75
+ afterEach(() => {
76
+ process = originalProcess;
77
+ Object.defineProperty(global.navigator, 'language', {
78
+ value: originalNavigator.language,
79
+ configurable: true
80
+ })
81
+ })
82
+
83
+ it('should return the explicitly set language', () => {
84
+ setLang('fr');
85
+ expect(getLang()).toBe('fr');
86
+ })
87
+
88
+ it('should detect language from navigator.language', () => {
89
+ Object.defineProperty(global.navigator, 'language', {
90
+ value: 'es-ES',
91
+ configurable: true
92
+ })
93
+ setLang();
94
+ expect(getLang()).toBe('es');
95
+ })
96
+
97
+ it('should detect language from process.env if navigator.language is undefined', () => {
98
+ Object.defineProperty(global.navigator, 'language', {
99
+ value: null,
100
+ configurable: true
101
+ })
102
+ setLang();
103
+ expect(getLang()).toBe('fr');
104
+ })
105
+
106
+ it('should fallback to "en" if no language is detected', () => {
107
+ Object.defineProperty(global.navigator, 'language', {
108
+ value: null,
109
+ configurable: true
110
+ })
111
+ process = undefined;
112
+
113
+ setLang(); expect(getLang()).toBe('en');
114
+
115
+ process = originalProcess;
116
+ process.env.LANG = '';
117
+ process.env.LC_ALL = 'en-US.UTF-8';
118
+
119
+ setLang(); expect(getLang()).toBe('en');
120
+
121
+ process.env.LANG = '';
122
+ process.env.LC_ALL = '';
123
+ process.env.LC_MESSAGES = 'en-US.UTF-8';
124
+
125
+ setLang(); expect(getLang()).toBe('en');
126
+ })
127
+
128
+ it('should trim and normalize the language code', () => {
129
+ setLang(' FR-fr ');
130
+ expect(getLang()).toBe('fr');
131
+ })
132
+ })
@@ -0,0 +1,29 @@
1
+ import '../src/index.js';
2
+ import {isFunction} from "../src/is.js";
3
+ import {trim, numberFormat} from "../src/string.js";
4
+
5
+ describe('index', () => {
6
+ const origTrim = String.prototype.trim;
7
+
8
+ describe('String.prototype extensions', () => {
9
+ it('should add custom methods to String.prototype', () => {
10
+ expect(isFunction(String.prototype.numberFormat)).toBe(true);
11
+ });
12
+
13
+ it('should call the original method if available and arguments match', () => {
14
+ const origTrim = String.prototype.trim;
15
+
16
+ String.prototype.trim = jest.fn(origTrim);
17
+
18
+ const result = ' hello '.trim();
19
+ expect(result).toBe('hello');
20
+ expect(String.prototype.trim).toHaveBeenCalled();
21
+
22
+ String.prototype.trim = origTrim;
23
+ });
24
+
25
+ it('should call the custom method if arguments do not match the original', () => {
26
+ expect('!hello!'.trim('!')).toBe('hello');
27
+ });
28
+ });
29
+ })