@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.
- package/package.json +5 -3
- package/src/classes/Api.ts +407 -0
- package/src/classes/ApiDefault.ts +83 -0
- package/src/classes/ApiHeaders.ts +52 -0
- package/src/classes/ApiPreparation.ts +114 -0
- package/src/classes/ApiResponse.ts +293 -0
- package/src/classes/ApiStatus.ts +173 -0
- package/src/classes/BroadcastMessage.ts +73 -0
- package/src/classes/Cache.ts +60 -0
- package/src/classes/CacheItem.ts +95 -0
- package/src/classes/CacheStatic.ts +30 -0
- package/src/classes/Cookie.ts +135 -0
- package/src/classes/CookieBlock.ts +31 -0
- package/src/classes/DataStorage.ts +194 -0
- package/src/classes/Datetime.ts +891 -0
- package/src/classes/EventItem.ts +373 -0
- package/src/classes/Geo.ts +320 -0
- package/src/classes/GeoFlag.ts +386 -0
- package/src/classes/GeoIntl.ts +839 -0
- package/src/classes/GeoPhone.ts +272 -0
- package/src/classes/Global.ts +32 -0
- package/src/classes/Hash.ts +142 -0
- package/src/classes/Icons.ts +165 -0
- package/src/classes/Loading.ts +90 -0
- package/src/classes/Meta.ts +284 -0
- package/src/classes/MetaManager.ts +200 -0
- package/src/classes/MetaOg.ts +147 -0
- package/src/classes/MetaTwitter.ts +154 -0
- package/src/classes/ScrollbarWidth.ts +86 -0
- package/src/classes/Translate.ts +293 -0
- package/src/classes/__tests__/Api.test.ts +728 -0
- package/src/classes/__tests__/ApiDefault.test.ts +222 -0
- package/src/classes/__tests__/ApiHeaders.test.ts +447 -0
- package/src/classes/__tests__/ApiPreparation.test.ts +257 -0
- package/src/classes/__tests__/ApiResponse.test.ts +547 -0
- package/src/classes/__tests__/ApiStatus.test.ts +403 -0
- package/src/classes/__tests__/Meta.test.ts +629 -0
- package/src/classes/__tests__/MetaManager.test.ts +836 -0
- package/src/classes/__tests__/MetaOg.test.ts +677 -0
- package/src/classes/__tests__/MetaTwitter.test.ts +423 -0
- package/src/functions/anyToString.ts +36 -0
- package/src/functions/applyTemplate.ts +63 -0
- package/src/functions/arrFill.ts +10 -0
- package/src/functions/copyObject.ts +10 -0
- package/src/functions/createElement.ts +40 -0
- package/src/functions/domQuerySelector.ts +15 -0
- package/src/functions/domQuerySelectorAll.ts +15 -0
- package/src/functions/encodeAttribute.ts +15 -0
- package/src/functions/eventStopPropagation.ts +10 -0
- package/src/functions/executeFunction.ts +13 -0
- package/src/functions/executePromise.ts +19 -0
- package/src/functions/forEach.ts +39 -0
- package/src/functions/frame.ts +38 -0
- package/src/functions/getAttributes.ts +27 -0
- package/src/functions/getClipboardData.ts +13 -0
- package/src/functions/getColumn.ts +18 -0
- package/src/functions/getElement.ts +35 -0
- package/src/functions/getElementId.ts +39 -0
- package/src/functions/getElementItem.ts +27 -0
- package/src/functions/getElementOrWindow.ts +23 -0
- package/src/functions/getExp.ts +21 -0
- package/src/functions/getItemByPath.ts +24 -0
- package/src/functions/getKey.ts +9 -0
- package/src/functions/getLengthOfAllArray.ts +13 -0
- package/src/functions/getMaxLengthAllArray.ts +13 -0
- package/src/functions/getMinLengthAllArray.ts +13 -0
- package/src/functions/getMouseClient.ts +17 -0
- package/src/functions/getMouseClientX.ts +9 -0
- package/src/functions/getMouseClientY.ts +9 -0
- package/src/functions/getObjectByKeys.ts +24 -0
- package/src/functions/getObjectNoUndefined.ts +23 -0
- package/src/functions/getObjectOrNone.ts +11 -0
- package/src/functions/getRandomText.ts +29 -0
- package/src/functions/getRequestString.ts +21 -0
- package/src/functions/getStepPercent.ts +19 -0
- package/src/functions/getStepValue.ts +19 -0
- package/src/functions/goScroll.ts +40 -0
- package/src/functions/inArray.ts +10 -0
- package/src/functions/initScrollbarOffset.ts +14 -0
- package/src/functions/intersectKey.ts +34 -0
- package/src/functions/isArray.ts +9 -0
- package/src/functions/isDifferent.ts +27 -0
- package/src/functions/isDomRuntime.ts +12 -0
- package/src/functions/isFilled.ts +49 -0
- package/src/functions/isFloat.ts +16 -0
- package/src/functions/isFunction.ts +11 -0
- package/src/functions/isInDom.ts +15 -0
- package/src/functions/isIntegerBetween.ts +11 -0
- package/src/functions/isNull.ts +11 -0
- package/src/functions/isNumber.ts +16 -0
- package/src/functions/isObject.ts +9 -0
- package/src/functions/isObjectNotArray.ts +11 -0
- package/src/functions/isSelected.ts +32 -0
- package/src/functions/isSelectedByList.ts +19 -0
- package/src/functions/isString.ts +9 -0
- package/src/functions/isWindow.ts +11 -0
- package/src/functions/random.ts +10 -0
- package/src/functions/replaceRecursive.ts +60 -0
- package/src/functions/replaceTemplate.ts +22 -0
- package/src/functions/secondToTime.ts +20 -0
- package/src/functions/setElementItem.ts +56 -0
- package/src/functions/setValues.ts +59 -0
- package/src/functions/splice.ts +59 -0
- package/src/functions/strFill.ts +12 -0
- package/src/functions/toArray.ts +19 -0
- package/src/functions/toCamelCase.ts +16 -0
- package/src/functions/toCamelCaseFirst.ts +12 -0
- package/src/functions/toDate.ts +44 -0
- package/src/functions/toKebabCase.ts +25 -0
- package/src/functions/toNumber.ts +35 -0
- package/src/functions/toNumberByMax.ts +33 -0
- package/src/functions/toPercent.ts +10 -0
- package/src/functions/toPercentBy100.ts +12 -0
- package/src/functions/transformation.ts +59 -0
- package/src/functions/uniqueArray.ts +9 -0
- package/src/functions/writeClipboardData.ts +17 -0
- package/src/library.ts +116 -0
- package/src/types/apiTypes.ts +143 -0
- package/src/types/basicTypes.ts +155 -0
- package/src/types/geoTypes.ts +109 -0
- package/src/types/metaTypes.ts +764 -0
- 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('"')
|
|
419
|
+
expect(html).toContain('&')
|
|
420
|
+
expect(html).toContain('<')
|
|
421
|
+
expect(html).toContain('>')
|
|
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
|
+
})
|