@dxtmisha/functional-basic 0.1.0 → 0.1.1

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 (122) hide show
  1. package/package.json +5 -3
  2. package/src/classes/Api.ts +407 -0
  3. package/src/classes/ApiDefault.ts +83 -0
  4. package/src/classes/ApiHeaders.ts +52 -0
  5. package/src/classes/ApiPreparation.ts +114 -0
  6. package/src/classes/ApiResponse.ts +293 -0
  7. package/src/classes/ApiStatus.ts +173 -0
  8. package/src/classes/BroadcastMessage.ts +73 -0
  9. package/src/classes/Cache.ts +60 -0
  10. package/src/classes/CacheItem.ts +95 -0
  11. package/src/classes/CacheStatic.ts +30 -0
  12. package/src/classes/Cookie.ts +135 -0
  13. package/src/classes/CookieBlock.ts +31 -0
  14. package/src/classes/DataStorage.ts +194 -0
  15. package/src/classes/Datetime.ts +891 -0
  16. package/src/classes/EventItem.ts +373 -0
  17. package/src/classes/Geo.ts +320 -0
  18. package/src/classes/GeoFlag.ts +386 -0
  19. package/src/classes/GeoIntl.ts +839 -0
  20. package/src/classes/GeoPhone.ts +272 -0
  21. package/src/classes/Global.ts +32 -0
  22. package/src/classes/Hash.ts +142 -0
  23. package/src/classes/Icons.ts +165 -0
  24. package/src/classes/Loading.ts +90 -0
  25. package/src/classes/Meta.ts +284 -0
  26. package/src/classes/MetaManager.ts +200 -0
  27. package/src/classes/MetaOg.ts +147 -0
  28. package/src/classes/MetaTwitter.ts +154 -0
  29. package/src/classes/ScrollbarWidth.ts +86 -0
  30. package/src/classes/Translate.ts +293 -0
  31. package/src/classes/__tests__/Api.test.ts +728 -0
  32. package/src/classes/__tests__/ApiDefault.test.ts +222 -0
  33. package/src/classes/__tests__/ApiHeaders.test.ts +447 -0
  34. package/src/classes/__tests__/ApiPreparation.test.ts +257 -0
  35. package/src/classes/__tests__/ApiResponse.test.ts +547 -0
  36. package/src/classes/__tests__/ApiStatus.test.ts +403 -0
  37. package/src/classes/__tests__/Meta.test.ts +629 -0
  38. package/src/classes/__tests__/MetaManager.test.ts +836 -0
  39. package/src/classes/__tests__/MetaOg.test.ts +677 -0
  40. package/src/classes/__tests__/MetaTwitter.test.ts +423 -0
  41. package/src/functions/anyToString.ts +36 -0
  42. package/src/functions/applyTemplate.ts +63 -0
  43. package/src/functions/arrFill.ts +10 -0
  44. package/src/functions/copyObject.ts +10 -0
  45. package/src/functions/createElement.ts +40 -0
  46. package/src/functions/domQuerySelector.ts +15 -0
  47. package/src/functions/domQuerySelectorAll.ts +15 -0
  48. package/src/functions/encodeAttribute.ts +15 -0
  49. package/src/functions/eventStopPropagation.ts +10 -0
  50. package/src/functions/executeFunction.ts +13 -0
  51. package/src/functions/executePromise.ts +19 -0
  52. package/src/functions/forEach.ts +39 -0
  53. package/src/functions/frame.ts +38 -0
  54. package/src/functions/getAttributes.ts +27 -0
  55. package/src/functions/getClipboardData.ts +13 -0
  56. package/src/functions/getColumn.ts +18 -0
  57. package/src/functions/getElement.ts +35 -0
  58. package/src/functions/getElementId.ts +39 -0
  59. package/src/functions/getElementItem.ts +27 -0
  60. package/src/functions/getElementOrWindow.ts +23 -0
  61. package/src/functions/getExp.ts +21 -0
  62. package/src/functions/getItemByPath.ts +24 -0
  63. package/src/functions/getKey.ts +9 -0
  64. package/src/functions/getLengthOfAllArray.ts +13 -0
  65. package/src/functions/getMaxLengthAllArray.ts +13 -0
  66. package/src/functions/getMinLengthAllArray.ts +13 -0
  67. package/src/functions/getMouseClient.ts +17 -0
  68. package/src/functions/getMouseClientX.ts +9 -0
  69. package/src/functions/getMouseClientY.ts +9 -0
  70. package/src/functions/getObjectByKeys.ts +24 -0
  71. package/src/functions/getObjectNoUndefined.ts +23 -0
  72. package/src/functions/getObjectOrNone.ts +11 -0
  73. package/src/functions/getRandomText.ts +29 -0
  74. package/src/functions/getRequestString.ts +21 -0
  75. package/src/functions/getStepPercent.ts +19 -0
  76. package/src/functions/getStepValue.ts +19 -0
  77. package/src/functions/goScroll.ts +40 -0
  78. package/src/functions/inArray.ts +10 -0
  79. package/src/functions/initScrollbarOffset.ts +14 -0
  80. package/src/functions/intersectKey.ts +34 -0
  81. package/src/functions/isArray.ts +9 -0
  82. package/src/functions/isDifferent.ts +27 -0
  83. package/src/functions/isDomRuntime.ts +12 -0
  84. package/src/functions/isFilled.ts +49 -0
  85. package/src/functions/isFloat.ts +16 -0
  86. package/src/functions/isFunction.ts +11 -0
  87. package/src/functions/isInDom.ts +15 -0
  88. package/src/functions/isIntegerBetween.ts +11 -0
  89. package/src/functions/isNull.ts +11 -0
  90. package/src/functions/isNumber.ts +16 -0
  91. package/src/functions/isObject.ts +9 -0
  92. package/src/functions/isObjectNotArray.ts +11 -0
  93. package/src/functions/isSelected.ts +32 -0
  94. package/src/functions/isSelectedByList.ts +19 -0
  95. package/src/functions/isString.ts +9 -0
  96. package/src/functions/isWindow.ts +11 -0
  97. package/src/functions/random.ts +10 -0
  98. package/src/functions/replaceRecursive.ts +60 -0
  99. package/src/functions/replaceTemplate.ts +22 -0
  100. package/src/functions/secondToTime.ts +20 -0
  101. package/src/functions/setElementItem.ts +56 -0
  102. package/src/functions/setValues.ts +59 -0
  103. package/src/functions/splice.ts +59 -0
  104. package/src/functions/strFill.ts +12 -0
  105. package/src/functions/toArray.ts +19 -0
  106. package/src/functions/toCamelCase.ts +16 -0
  107. package/src/functions/toCamelCaseFirst.ts +12 -0
  108. package/src/functions/toDate.ts +44 -0
  109. package/src/functions/toKebabCase.ts +25 -0
  110. package/src/functions/toNumber.ts +35 -0
  111. package/src/functions/toNumberByMax.ts +33 -0
  112. package/src/functions/toPercent.ts +10 -0
  113. package/src/functions/toPercentBy100.ts +12 -0
  114. package/src/functions/transformation.ts +59 -0
  115. package/src/functions/uniqueArray.ts +9 -0
  116. package/src/functions/writeClipboardData.ts +17 -0
  117. package/src/library.ts +116 -0
  118. package/src/types/apiTypes.ts +143 -0
  119. package/src/types/basicTypes.ts +155 -0
  120. package/src/types/geoTypes.ts +109 -0
  121. package/src/types/metaTypes.ts +764 -0
  122. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,677 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
6
+ import { MetaOg } from '../MetaOg'
7
+ import { MetaOpenGraphType } from '../../types/metaTypes'
8
+
9
+ describe('MetaOg', () => {
10
+ let metaOg: MetaOg
11
+
12
+ beforeEach(() => {
13
+ // Очистка DOM перед каждым тестом
14
+ document.head.innerHTML = ''
15
+
16
+ metaOg = new MetaOg()
17
+ })
18
+
19
+ afterEach(() => {
20
+ // Очистка после тестов
21
+ document.head.innerHTML = ''
22
+ })
23
+
24
+ describe('constructor', () => {
25
+ it('should create MetaOg instance', () => {
26
+ expect(metaOg).toBeInstanceOf(MetaOg)
27
+ })
28
+
29
+ it('should use property attribute instead of name', () => {
30
+ metaOg.setTitle('Test Title')
31
+
32
+ const element = document.querySelector('meta[property="og:title"]')
33
+ expect(element).toBeTruthy()
34
+ expect(element?.getAttribute('property')).toBe('og:title')
35
+ expect(element?.getAttribute('name')).toBeNull()
36
+ })
37
+
38
+ it('should initialize with all Open Graph tags', () => {
39
+ const listMeta = metaOg.getListMeta()
40
+
41
+ expect(listMeta).toContain('og:title')
42
+ expect(listMeta).toContain('og:type')
43
+ expect(listMeta).toContain('og:url')
44
+ expect(listMeta).toContain('og:image')
45
+ expect(listMeta).toContain('og:description')
46
+ expect(listMeta).toContain('og:locale')
47
+ expect(listMeta).toContain('og:site_name')
48
+ })
49
+
50
+ it('should read existing OG tags from DOM on initialization', () => {
51
+ // Создаем OG теги в DOM
52
+ const titleMeta = document.createElement('meta')
53
+ titleMeta.setAttribute('property', 'og:title')
54
+ titleMeta.setAttribute('content', 'Existing Title')
55
+ document.head.appendChild(titleMeta)
56
+
57
+ const descMeta = document.createElement('meta')
58
+ descMeta.setAttribute('property', 'og:description')
59
+ descMeta.setAttribute('content', 'Existing Description')
60
+ document.head.appendChild(descMeta)
61
+
62
+ // Создаем новый экземпляр
63
+ const newMetaOg = new MetaOg()
64
+
65
+ // Должен прочитать существующие теги
66
+ expect(newMetaOg.getTitle()).toBe('Existing Title')
67
+ expect(newMetaOg.getDescription()).toBe('Existing Description')
68
+ })
69
+ })
70
+
71
+ describe('getTitle', () => {
72
+ it('should return empty string when title not set', () => {
73
+ expect(metaOg.getTitle()).toBe('')
74
+ })
75
+
76
+ it('should return title after setting', () => {
77
+ metaOg.setTitle('Open Graph Title')
78
+ expect(metaOg.getTitle()).toBe('Open Graph Title')
79
+ })
80
+ })
81
+
82
+ describe('getType', () => {
83
+ it('should return empty string when type not set', () => {
84
+ expect(metaOg.getType()).toBe('')
85
+ })
86
+
87
+ it('should return type after setting', () => {
88
+ metaOg.setType(MetaOpenGraphType.article)
89
+ expect(metaOg.getType()).toBe(MetaOpenGraphType.article)
90
+ })
91
+
92
+ it('should return correct type for different content types', () => {
93
+ metaOg.setType(MetaOpenGraphType.website)
94
+ expect(metaOg.getType()).toBe('website')
95
+
96
+ metaOg.setType(MetaOpenGraphType.videoMovie)
97
+ expect(metaOg.getType()).toBe('video.movie')
98
+
99
+ metaOg.setType(MetaOpenGraphType.musicSong)
100
+ expect(metaOg.getType()).toBe('music.song')
101
+ })
102
+ })
103
+
104
+ describe('getUrl', () => {
105
+ it('should return empty string when URL not set', () => {
106
+ expect(metaOg.getUrl()).toBe('')
107
+ })
108
+
109
+ it('should return URL after setting', () => {
110
+ metaOg.setUrl('https://example.com/page')
111
+ expect(metaOg.getUrl()).toBe('https://example.com/page')
112
+ })
113
+ })
114
+
115
+ describe('getImage', () => {
116
+ it('should return empty string when image not set', () => {
117
+ expect(metaOg.getImage()).toBe('')
118
+ })
119
+
120
+ it('should return image URL after setting', () => {
121
+ metaOg.setImage('https://example.com/image.jpg')
122
+ expect(metaOg.getImage()).toBe('https://example.com/image.jpg')
123
+ })
124
+ })
125
+
126
+ describe('getDescription', () => {
127
+ it('should return empty string when description not set', () => {
128
+ expect(metaOg.getDescription()).toBe('')
129
+ })
130
+
131
+ it('should return description after setting', () => {
132
+ metaOg.setDescription('Open Graph Description')
133
+ expect(metaOg.getDescription()).toBe('Open Graph Description')
134
+ })
135
+ })
136
+
137
+ describe('getLocale', () => {
138
+ it('should return empty string when locale not set', () => {
139
+ expect(metaOg.getLocale()).toBe('')
140
+ })
141
+
142
+ it('should return locale after setting', () => {
143
+ metaOg.setLocale('en_US')
144
+ expect(metaOg.getLocale()).toBe('en_US')
145
+ })
146
+ })
147
+
148
+ describe('getSiteName', () => {
149
+ it('should return empty string when site name not set', () => {
150
+ expect(metaOg.getSiteName()).toBe('')
151
+ })
152
+
153
+ it('should return site name after setting', () => {
154
+ metaOg.setSiteName('My Website')
155
+ expect(metaOg.getSiteName()).toBe('My Website')
156
+ })
157
+ })
158
+
159
+ describe('setTitle', () => {
160
+ it('should set OG title', () => {
161
+ metaOg.setTitle('Test OG Title')
162
+ expect(metaOg.getTitle()).toBe('Test OG Title')
163
+ })
164
+
165
+ it('should create meta tag with property attribute in DOM', () => {
166
+ metaOg.setTitle('DOM Title Test')
167
+
168
+ const element = document.querySelector('meta[property="og:title"]')
169
+ expect(element).toBeTruthy()
170
+ expect(element?.getAttribute('content')).toBe('DOM Title Test')
171
+ })
172
+
173
+ it('should update existing meta tag', () => {
174
+ metaOg.setTitle('First Title')
175
+ metaOg.setTitle('Second Title')
176
+
177
+ const elements = document.querySelectorAll('meta[property="og:title"]')
178
+ expect(elements.length).toBe(1)
179
+ expect(elements?.[0]?.getAttribute('content')).toBe('Second Title')
180
+ })
181
+
182
+ it('should return this for chaining', () => {
183
+ const result = metaOg.setTitle('Chain Test')
184
+ expect(result).toBe(metaOg)
185
+ })
186
+
187
+ it('should handle empty string', () => {
188
+ metaOg.setTitle('')
189
+ expect(metaOg.getTitle()).toBe('')
190
+ })
191
+
192
+ it('should handle special characters', () => {
193
+ metaOg.setTitle('Title with "quotes" & <tags>')
194
+ expect(metaOg.getTitle()).toBe('Title with "quotes" & <tags>')
195
+ })
196
+ })
197
+
198
+ describe('setType', () => {
199
+ it('should set OG type', () => {
200
+ metaOg.setType(MetaOpenGraphType.article)
201
+ expect(metaOg.getType()).toBe(MetaOpenGraphType.article)
202
+ })
203
+
204
+ it('should create meta tag in DOM', () => {
205
+ metaOg.setType(MetaOpenGraphType.website)
206
+
207
+ const element = document.querySelector('meta[property="og:type"]')
208
+ expect(element).toBeTruthy()
209
+ expect(element?.getAttribute('content')).toBe('website')
210
+ })
211
+
212
+ it('should handle all Open Graph types', () => {
213
+ const types = [
214
+ MetaOpenGraphType.website,
215
+ MetaOpenGraphType.article,
216
+ MetaOpenGraphType.video,
217
+ MetaOpenGraphType.videoMovie,
218
+ MetaOpenGraphType.videoEpisode,
219
+ MetaOpenGraphType.videoTvShow,
220
+ MetaOpenGraphType.musicSong,
221
+ MetaOpenGraphType.musicAlbum,
222
+ MetaOpenGraphType.musicPlaylist,
223
+ MetaOpenGraphType.product,
224
+ MetaOpenGraphType.book,
225
+ MetaOpenGraphType.profile
226
+ ]
227
+
228
+ types.forEach((type) => {
229
+ metaOg.setType(type)
230
+ expect(metaOg.getType()).toBe(type)
231
+ })
232
+ })
233
+
234
+ it('should return this for chaining', () => {
235
+ const result = metaOg.setType(MetaOpenGraphType.article)
236
+ expect(result).toBe(metaOg)
237
+ })
238
+ })
239
+
240
+ describe('setUrl', () => {
241
+ it('should set OG URL', () => {
242
+ metaOg.setUrl('https://example.com/article')
243
+ expect(metaOg.getUrl()).toBe('https://example.com/article')
244
+ })
245
+
246
+ it('should create meta tag in DOM', () => {
247
+ metaOg.setUrl('https://example.com/page')
248
+
249
+ const element = document.querySelector('meta[property="og:url"]')
250
+ expect(element).toBeTruthy()
251
+ expect(element?.getAttribute('content')).toBe('https://example.com/page')
252
+ })
253
+
254
+ it('should handle URLs with query parameters', () => {
255
+ const url = 'https://example.com/page?param=value&other=test'
256
+ metaOg.setUrl(url)
257
+ expect(metaOg.getUrl()).toBe(url)
258
+ })
259
+
260
+ it('should return this for chaining', () => {
261
+ const result = metaOg.setUrl('https://example.com')
262
+ expect(result).toBe(metaOg)
263
+ })
264
+ })
265
+
266
+ describe('setImage', () => {
267
+ it('should set OG image URL', () => {
268
+ metaOg.setImage('https://example.com/image.jpg')
269
+ expect(metaOg.getImage()).toBe('https://example.com/image.jpg')
270
+ })
271
+
272
+ it('should create meta tag in DOM', () => {
273
+ metaOg.setImage('https://example.com/og-image.png')
274
+
275
+ const element = document.querySelector('meta[property="og:image"]')
276
+ expect(element).toBeTruthy()
277
+ expect(element?.getAttribute('content')).toBe('https://example.com/og-image.png')
278
+ })
279
+
280
+ it('should handle different image formats', () => {
281
+ const formats = [
282
+ 'https://example.com/image.jpg',
283
+ 'https://example.com/image.png',
284
+ 'https://example.com/image.webp',
285
+ 'https://example.com/image.gif'
286
+ ]
287
+
288
+ formats.forEach((format) => {
289
+ metaOg.setImage(format)
290
+ expect(metaOg.getImage()).toBe(format)
291
+ })
292
+ })
293
+
294
+ it('should return this for chaining', () => {
295
+ const result = metaOg.setImage('https://example.com/img.jpg')
296
+ expect(result).toBe(metaOg)
297
+ })
298
+ })
299
+
300
+ describe('setDescription', () => {
301
+ it('should set OG description', () => {
302
+ metaOg.setDescription('Open Graph description text')
303
+ expect(metaOg.getDescription()).toBe('Open Graph description text')
304
+ })
305
+
306
+ it('should create meta tag in DOM', () => {
307
+ metaOg.setDescription('Test description')
308
+
309
+ const element = document.querySelector('meta[property="og:description"]')
310
+ expect(element).toBeTruthy()
311
+ expect(element?.getAttribute('content')).toBe('Test description')
312
+ })
313
+
314
+ it('should handle long descriptions', () => {
315
+ const longDesc = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '.repeat(10)
316
+ metaOg.setDescription(longDesc)
317
+ expect(metaOg.getDescription()).toBe(longDesc)
318
+ })
319
+
320
+ it('should return this for chaining', () => {
321
+ const result = metaOg.setDescription('Chain description')
322
+ expect(result).toBe(metaOg)
323
+ })
324
+ })
325
+
326
+ describe('setLocale', () => {
327
+ it('should set OG locale', () => {
328
+ metaOg.setLocale('ru_RU')
329
+ expect(metaOg.getLocale()).toBe('ru_RU')
330
+ })
331
+
332
+ it('should create meta tag in DOM', () => {
333
+ metaOg.setLocale('en_US')
334
+
335
+ const element = document.querySelector('meta[property="og:locale"]')
336
+ expect(element).toBeTruthy()
337
+ expect(element?.getAttribute('content')).toBe('en_US')
338
+ })
339
+
340
+ it('should handle different locale formats', () => {
341
+ const locales = ['en_US', 'ru_RU', 'fr_FR', 'de_DE', 'ja_JP', 'zh_CN']
342
+
343
+ locales.forEach((locale) => {
344
+ metaOg.setLocale(locale)
345
+ expect(metaOg.getLocale()).toBe(locale)
346
+ })
347
+ })
348
+
349
+ it('should return this for chaining', () => {
350
+ const result = metaOg.setLocale('en_GB')
351
+ expect(result).toBe(metaOg)
352
+ })
353
+ })
354
+
355
+ describe('setSiteName', () => {
356
+ it('should set OG site name', () => {
357
+ metaOg.setSiteName('My Awesome Site')
358
+ expect(metaOg.getSiteName()).toBe('My Awesome Site')
359
+ })
360
+
361
+ it('should create meta tag in DOM', () => {
362
+ metaOg.setSiteName('Test Site')
363
+
364
+ const element = document.querySelector('meta[property="og:site_name"]')
365
+ expect(element).toBeTruthy()
366
+ expect(element?.getAttribute('content')).toBe('Test Site')
367
+ })
368
+
369
+ it('should return this for chaining', () => {
370
+ const result = metaOg.setSiteName('Chain Site')
371
+ expect(result).toBe(metaOg)
372
+ })
373
+
374
+ it('should handle site names with special characters', () => {
375
+ metaOg.setSiteName('Site & Company™')
376
+ expect(metaOg.getSiteName()).toBe('Site & Company™')
377
+ })
378
+ })
379
+
380
+ describe('html', () => {
381
+ it('should generate HTML with property attribute', () => {
382
+ metaOg.setTitle('HTML Test')
383
+
384
+ const html = metaOg.html()
385
+
386
+ expect(html).toContain('property="og:title"')
387
+ expect(html).not.toContain('name="og:title"')
388
+ })
389
+
390
+ it('should generate HTML for all set tags', () => {
391
+ metaOg
392
+ .setTitle('Test Title')
393
+ .setType(MetaOpenGraphType.article)
394
+ .setUrl('https://example.com')
395
+ .setImage('https://example.com/img.jpg')
396
+ .setDescription('Test Description')
397
+ .setLocale('en_US')
398
+ .setSiteName('Test Site')
399
+
400
+ const html = metaOg.html()
401
+
402
+ expect(html).toContain('property="og:title"')
403
+ expect(html).toContain('content="Test Title"')
404
+ expect(html).toContain('property="og:type"')
405
+ expect(html).toContain('content="article"')
406
+ expect(html).toContain('property="og:url"')
407
+ expect(html).toContain('property="og:image"')
408
+ expect(html).toContain('property="og:description"')
409
+ expect(html).toContain('property="og:locale"')
410
+ expect(html).toContain('property="og:site_name"')
411
+ })
412
+
413
+ it('should escape special characters in HTML', () => {
414
+ metaOg.setTitle('Title with "quotes" & <html>')
415
+
416
+ const html = metaOg.html()
417
+
418
+ expect(html).toContain('&quot;')
419
+ expect(html).toContain('&amp;')
420
+ expect(html).toContain('&lt;')
421
+ expect(html).toContain('&gt;')
422
+ })
423
+
424
+ it('should not include empty tags', () => {
425
+ metaOg.setTitle('Only Title')
426
+
427
+ const html = metaOg.html()
428
+
429
+ expect(html).toContain('property="og:title"')
430
+ expect(html).not.toContain('property="og:description"')
431
+ })
432
+ })
433
+
434
+ describe('method chaining', () => {
435
+ it('should support chaining all setter methods', () => {
436
+ const result = metaOg
437
+ .setTitle('Chained Title')
438
+ .setType(MetaOpenGraphType.article)
439
+ .setUrl('https://example.com/chain')
440
+ .setImage('https://example.com/chain.jpg')
441
+ .setDescription('Chained description')
442
+ .setLocale('en_US')
443
+ .setSiteName('Chained Site')
444
+
445
+ expect(result).toBe(metaOg)
446
+ expect(metaOg.getTitle()).toBe('Chained Title')
447
+ expect(metaOg.getType()).toBe(MetaOpenGraphType.article)
448
+ expect(metaOg.getUrl()).toBe('https://example.com/chain')
449
+ expect(metaOg.getImage()).toBe('https://example.com/chain.jpg')
450
+ expect(metaOg.getDescription()).toBe('Chained description')
451
+ expect(metaOg.getLocale()).toBe('en_US')
452
+ expect(metaOg.getSiteName()).toBe('Chained Site')
453
+ })
454
+ })
455
+
456
+ describe('edge cases', () => {
457
+ it('should handle unicode characters', () => {
458
+ metaOg.setTitle('Заголовок на русском 🎉')
459
+ metaOg.setDescription('描述中文 日本語')
460
+
461
+ expect(metaOg.getTitle()).toBe('Заголовок на русском 🎉')
462
+ expect(metaOg.getDescription()).toBe('描述中文 日本語')
463
+ })
464
+
465
+ it('should handle very long URLs', () => {
466
+ const longUrl = 'https://example.com/' + 'a'.repeat(1000)
467
+ metaOg.setUrl(longUrl)
468
+ expect(metaOg.getUrl()).toBe(longUrl)
469
+ })
470
+
471
+ it('should handle newlines in content', () => {
472
+ metaOg.setDescription('Line 1\nLine 2\nLine 3')
473
+ expect(metaOg.getDescription()).toBe('Line 1\nLine 2\nLine 3')
474
+ })
475
+
476
+ it('should handle multiple consecutive updates', () => {
477
+ for (let i = 0; i < 100; i++) {
478
+ metaOg.setTitle(`Title ${i}`)
479
+ }
480
+
481
+ expect(metaOg.getTitle()).toBe('Title 99')
482
+
483
+ const elements = document.querySelectorAll('meta[property="og:title"]')
484
+ expect(elements.length).toBe(1)
485
+ })
486
+ })
487
+
488
+ describe('DOM synchronization', () => {
489
+ it('should keep internal state and DOM in sync', () => {
490
+ metaOg.setTitle('Sync Test')
491
+
492
+ const domContent = document.querySelector('meta[property="og:title"]')?.getAttribute('content')
493
+ const internalContent = metaOg.getTitle()
494
+
495
+ expect(domContent).toBe(internalContent)
496
+ })
497
+
498
+ it('should update DOM when changing values', () => {
499
+ metaOg.setTitle('First')
500
+ metaOg.setTitle('Second')
501
+ metaOg.setTitle('Third')
502
+
503
+ const elements = document.querySelectorAll('meta[property="og:title"]')
504
+ expect(elements.length).toBe(1)
505
+ expect(elements?.[0]?.getAttribute('content')).toBe('Third')
506
+ })
507
+
508
+ it('should not create duplicate tags', () => {
509
+ const existingMeta = document.createElement('meta')
510
+ existingMeta.setAttribute('property', 'og:title')
511
+ existingMeta.setAttribute('content', 'Existing')
512
+ document.head.appendChild(existingMeta)
513
+
514
+ const newMetaOg = new MetaOg()
515
+ newMetaOg.setTitle('Updated')
516
+
517
+ const elements = document.querySelectorAll('meta[property="og:title"]')
518
+ expect(elements.length).toBe(1)
519
+ expect(elements?.[0]?.getAttribute('content')).toBe('Updated')
520
+ })
521
+ })
522
+
523
+ describe('SSR compatibility', () => {
524
+ it('should work without DOM runtime', () => {
525
+ const originalDocument = globalThis.document
526
+ delete (globalThis as any).document
527
+
528
+ try {
529
+ const ssrMetaOg = new MetaOg()
530
+
531
+ ssrMetaOg
532
+ .setTitle('SSR Title')
533
+ .setType(MetaOpenGraphType.article)
534
+ .setUrl('https://example.com/ssr')
535
+ .setImage('https://example.com/ssr.jpg')
536
+ .setDescription('SSR Description')
537
+ .setLocale('en_US')
538
+ .setSiteName('SSR Site')
539
+
540
+ expect(ssrMetaOg.getTitle()).toBe('SSR Title')
541
+ expect(ssrMetaOg.getType()).toBe(MetaOpenGraphType.article)
542
+ expect(ssrMetaOg.getUrl()).toBe('https://example.com/ssr')
543
+
544
+ const html = ssrMetaOg.html()
545
+ expect(html).toContain('property="og:title"')
546
+ expect(html).toContain('property="og:type"')
547
+ expect(html).toContain('property="og:url"')
548
+ } finally {
549
+ if (originalDocument) {
550
+ (globalThis as any).document = originalDocument
551
+ }
552
+ }
553
+ })
554
+
555
+ it('should generate valid HTML for SSR', () => {
556
+ const originalDocument = globalThis.document
557
+ delete (globalThis as any).document
558
+
559
+ try {
560
+ const ssrMetaOg = new MetaOg()
561
+
562
+ ssrMetaOg
563
+ .setTitle('SSR Page Title')
564
+ .setDescription('SSR Page Description')
565
+ .setImage('https://example.com/ssr-image.jpg')
566
+
567
+ const html = ssrMetaOg.html()
568
+
569
+ expect(html).toMatch(/<meta property="[^"]+" content="[^"]+">/)
570
+ expect(html).toContain('property="og:title" content="SSR Page Title"')
571
+ expect(html).toContain('property="og:description" content="SSR Page Description"')
572
+ } finally {
573
+ if (originalDocument) {
574
+ (globalThis as any).document = originalDocument
575
+ }
576
+ }
577
+ })
578
+ })
579
+
580
+ describe('real-world scenarios', () => {
581
+ it('should handle article page setup', () => {
582
+ metaOg
583
+ .setType(MetaOpenGraphType.article)
584
+ .setTitle('How to Build Web Applications')
585
+ .setDescription('A comprehensive guide to modern web development')
586
+ .setUrl('https://example.com/articles/web-apps')
587
+ .setImage('https://example.com/images/web-apps-cover.jpg')
588
+ .setLocale('en_US')
589
+ .setSiteName('Developer Blog')
590
+
591
+ expect(metaOg.getType()).toBe('article')
592
+
593
+ const html = metaOg.html()
594
+ expect(html).toContain('property="og:type" content="article"')
595
+ expect(html).toContain('How to Build Web Applications')
596
+ })
597
+
598
+ it('should handle product page setup', () => {
599
+ metaOg
600
+ .setType(MetaOpenGraphType.product)
601
+ .setTitle('Amazing Product Name')
602
+ .setDescription('Best product ever with great features')
603
+ .setUrl('https://store.com/products/amazing')
604
+ .setImage('https://store.com/images/product.jpg')
605
+ .setLocale('en_US')
606
+ .setSiteName('My Store')
607
+
608
+ expect(metaOg.getType()).toBe('product')
609
+
610
+ const html = metaOg.html()
611
+ expect(html).toContain('property="og:type" content="product"')
612
+ })
613
+
614
+ it('should handle video page setup', () => {
615
+ metaOg
616
+ .setType(MetaOpenGraphType.videoMovie)
617
+ .setTitle('Awesome Movie Title')
618
+ .setDescription('An epic movie about...')
619
+ .setUrl('https://videos.com/movies/awesome')
620
+ .setImage('https://videos.com/thumbnails/awesome.jpg')
621
+ .setLocale('en_US')
622
+ .setSiteName('Video Platform')
623
+
624
+ expect(metaOg.getType()).toBe('video.movie')
625
+ })
626
+
627
+ it('should handle website homepage setup', () => {
628
+ metaOg
629
+ .setType(MetaOpenGraphType.website)
630
+ .setTitle('Welcome to My Website')
631
+ .setDescription('The best website for everything')
632
+ .setUrl('https://example.com')
633
+ .setImage('https://example.com/og-home.jpg')
634
+ .setLocale('en_US')
635
+ .setSiteName('My Website')
636
+
637
+ const html = metaOg.html()
638
+
639
+ expect(html).toContain('property="og:type" content="website"')
640
+ expect(html).toContain('Welcome to My Website')
641
+ })
642
+ })
643
+
644
+ describe('integration with other meta managers', () => {
645
+ it('should work independently', () => {
646
+ metaOg.setTitle('OG Title')
647
+
648
+ expect(metaOg.getTitle()).toBe('OG Title')
649
+
650
+ const ogElement = document.querySelector('meta[property="og:title"]')
651
+ expect(ogElement).toBeTruthy()
652
+ })
653
+
654
+ it('should generate HTML that can be combined with other meta tags', () => {
655
+ metaOg
656
+ .setTitle('Combined Title')
657
+ .setDescription('Combined Description')
658
+
659
+ const html = metaOg.html()
660
+
661
+ expect(html).toBeTruthy()
662
+ expect(typeof html).toBe('string')
663
+
664
+ const combinedHtml = `
665
+ <!DOCTYPE html>
666
+ <html>
667
+ <head>
668
+ ${html}
669
+ </head>
670
+ </html>
671
+ `
672
+
673
+ expect(combinedHtml).toContain('<meta property="og:title"')
674
+ expect(combinedHtml).toContain('<meta property="og:description"')
675
+ })
676
+ })
677
+ })