@oslokommune/punkt-elements 13.5.0 → 13.5.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,436 @@
1
+ import '@testing-library/jest-dom'
2
+ import { axe, toHaveNoViolations } from 'jest-axe'
3
+ import { fireEvent } from '@testing-library/dom'
4
+
5
+ expect.extend(toHaveNoViolations)
6
+
7
+ import './consent'
8
+ import { PktConsent } from './consent'
9
+
10
+ const waitForCustomElements = async () => {
11
+ await customElements.whenDefined('pkt-consent')
12
+ await customElements.whenDefined('pkt-button')
13
+ }
14
+
15
+ // Helper function to create consent markup
16
+ const createConsent = async (consentProps = '') => {
17
+ const container = document.createElement('div')
18
+ container.innerHTML = `
19
+ <pkt-consent ${consentProps}></pkt-consent>
20
+ `
21
+ document.body.appendChild(container)
22
+ await waitForCustomElements()
23
+ return container
24
+ }
25
+
26
+ // Cleanup after each test
27
+ afterEach(() => {
28
+ document.body.innerHTML = ''
29
+ // Clean up any injected scripts
30
+ const existingScript = document.querySelector('#oslo-consent-script')
31
+ if (existingScript) {
32
+ existingScript.remove()
33
+ }
34
+ const existingStyles = document.querySelector('#oslo-consent-styles')
35
+ if (existingStyles) {
36
+ existingStyles.remove()
37
+ }
38
+ // Clean up global variables
39
+ delete window.cookieBanner_googleAnalyticsId
40
+ delete window.cookieBanner_hotjarId
41
+ delete window.cookieBanner_devMode
42
+ delete window.cookieBanner_cookieDomain
43
+ delete window.cookieBanner_cookieSecure
44
+ delete window.cookieBanner_cookieExpiryDays
45
+ delete window.cookieBanner
46
+ delete window.__cookieEvents
47
+ })
48
+
49
+ describe('PktConsent', () => {
50
+ describe('Rendering and basic functionality', () => {
51
+ test('renders without errors', async () => {
52
+ const container = await createConsent()
53
+
54
+ const consent = container.querySelector('pkt-consent') as PktConsent
55
+ expect(consent).toBeInTheDocument()
56
+
57
+ await consent.updateComplete
58
+ expect(consent).toBeTruthy()
59
+ })
60
+
61
+ test('renders with default trigger type as button', async () => {
62
+ const container = await createConsent()
63
+
64
+ const consent = container.querySelector('pkt-consent') as PktConsent
65
+ await consent.updateComplete
66
+
67
+ expect(consent.triggerType).toBe('button')
68
+
69
+ const button = consent.querySelector('pkt-button')
70
+ expect(button).toBeInTheDocument()
71
+ })
72
+
73
+ test('renders with custom trigger text', async () => {
74
+ const customText = 'Custom consent settings'
75
+ const container = await createConsent(`triggerText="${customText}"`)
76
+
77
+ const consent = container.querySelector('pkt-consent') as PktConsent
78
+ await consent.updateComplete
79
+
80
+ expect(consent.triggerText).toBe(customText)
81
+ const button = consent.querySelector('pkt-button')
82
+ expect(button?.textContent?.trim()).toBe(customText)
83
+ })
84
+ })
85
+
86
+ describe('Properties and attributes', () => {
87
+ test('applies default properties correctly', async () => {
88
+ const container = await createConsent()
89
+
90
+ const consent = container.querySelector('pkt-consent') as PktConsent
91
+ await consent.updateComplete
92
+
93
+ expect(consent.devMode).toBe(false)
94
+ expect(consent.triggerType).toBe('button')
95
+ expect(consent.i18nLanguage).toBe('nb')
96
+ expect(consent.hotjarId).toBe(null)
97
+ expect(consent.googleAnalyticsId).toBe(null)
98
+ expect(consent.cookieDomain).toBe(null)
99
+ expect(consent.cookieSecure).toBe(null)
100
+ expect(consent.cookieExpiryDays).toBe(null)
101
+ })
102
+
103
+ test('sets dev mode correctly', async () => {
104
+ const container = await createConsent('devMode')
105
+
106
+ const consent = container.querySelector('pkt-consent') as PktConsent
107
+ await consent.updateComplete
108
+
109
+ expect(consent.devMode).toBe(true)
110
+ })
111
+
112
+ test('sets analytics IDs correctly', async () => {
113
+ const googleId = 'GA-123456789'
114
+ const hotjarId = 'HJ-987654321'
115
+ const container = await createConsent(
116
+ `googleAnalyticsId="${googleId}" hotjarId="${hotjarId}"`,
117
+ )
118
+
119
+ const consent = container.querySelector('pkt-consent') as PktConsent
120
+ await consent.updateComplete
121
+
122
+ expect(consent.googleAnalyticsId).toBe(googleId)
123
+ expect(consent.hotjarId).toBe(hotjarId)
124
+ })
125
+
126
+ test('sets cookie configuration correctly', async () => {
127
+ const domain = '.example.com'
128
+ const secure = 'true'
129
+ const expiryDays = '365'
130
+ const container = await createConsent(
131
+ `cookieDomain="${domain}" cookieSecure="${secure}" cookieExpiryDays="${expiryDays}"`,
132
+ )
133
+
134
+ const consent = container.querySelector('pkt-consent') as PktConsent
135
+ await consent.updateComplete
136
+
137
+ expect(consent.cookieDomain).toBe(domain)
138
+ expect(consent.cookieSecure).toBe(secure)
139
+ expect(consent.cookieExpiryDays).toBe(expiryDays)
140
+ })
141
+
142
+ test('sets language correctly', async () => {
143
+ const container = await createConsent('i18nLanguage="en"')
144
+
145
+ const consent = container.querySelector('pkt-consent') as PktConsent
146
+ await consent.updateComplete
147
+
148
+ expect(consent.i18nLanguage).toBe('en')
149
+ })
150
+ })
151
+
152
+ describe('Trigger types', () => {
153
+ test('renders as button trigger type', async () => {
154
+ const container = await createConsent('triggerType="button"')
155
+
156
+ const consent = container.querySelector('pkt-consent') as PktConsent
157
+ await consent.updateComplete
158
+
159
+ expect(consent.triggerType).toBe('button')
160
+ const button = consent.querySelector('pkt-button')
161
+ expect(button).toBeInTheDocument()
162
+ expect(button?.getAttribute('skin')).toBe(null) // Default button
163
+ })
164
+
165
+ test('renders as link trigger type', async () => {
166
+ const container = await createConsent('triggerType="link"')
167
+
168
+ const consent = container.querySelector('pkt-consent') as PktConsent
169
+ await consent.updateComplete
170
+
171
+ expect(consent.triggerType).toBe('link')
172
+ const link = consent.querySelector('a.pkt-link')
173
+ expect(link).toBeInTheDocument()
174
+ expect(link?.getAttribute('href')).toBe('#')
175
+ })
176
+
177
+ test('renders as footer link trigger type', async () => {
178
+ const container = await createConsent('triggerType="footerlink"')
179
+
180
+ const consent = container.querySelector('pkt-consent') as PktConsent
181
+ await consent.updateComplete
182
+
183
+ expect(consent.triggerType).toBe('footerlink')
184
+ const footerLink = consent.querySelector('a.pkt-footer__link')
185
+ expect(footerLink).toBeInTheDocument()
186
+
187
+ const icon = footerLink?.querySelector('pkt-icon')
188
+ expect(icon).toBeInTheDocument()
189
+ expect(icon?.getAttribute('name')).toBe('chevron-right')
190
+ })
191
+
192
+ test('renders as icon trigger type', async () => {
193
+ const container = await createConsent('triggerType="icon"')
194
+
195
+ const consent = container.querySelector('pkt-consent') as PktConsent
196
+ await consent.updateComplete
197
+
198
+ expect(consent.triggerType).toBe('icon')
199
+ const button = consent.querySelector('pkt-button')
200
+ expect(button).toBeInTheDocument()
201
+ expect(button?.getAttribute('skin')).toBe('tertiary')
202
+ expect(button?.getAttribute('variant')).toBe('icon-only')
203
+ expect(button?.getAttribute('iconName')).toBe('cookie')
204
+ })
205
+ })
206
+
207
+ describe('Event handling', () => {
208
+ beforeEach(() => {
209
+ // Mock the entire window.cookieBanner object
210
+ Object.defineProperty(window, 'cookieBanner', {
211
+ value: {
212
+ cookieConsent: {
213
+ validateConsentCookie: jest.fn().mockResolvedValue(true),
214
+ getConsentCookie: jest.fn().mockReturnValue('mock-cookie'),
215
+ },
216
+ openCookieModal: jest.fn(),
217
+ },
218
+ writable: true,
219
+ })
220
+
221
+ // Mock window.__cookieEvents
222
+ Object.defineProperty(window, '__cookieEvents', {
223
+ value: {
224
+ on: jest.fn(),
225
+ off: jest.fn(),
226
+ },
227
+ writable: true,
228
+ })
229
+
230
+ // Use fake timers to control setTimeout
231
+ jest.useFakeTimers()
232
+ })
233
+
234
+ afterEach(() => {
235
+ // Clean up mocks and timers
236
+ jest.useRealTimers()
237
+ delete (window as any).cookieBanner
238
+ delete (window as any).__cookieEvents
239
+ })
240
+
241
+ test('handles click event on button trigger', async () => {
242
+ const container = await createConsent()
243
+ const consent = container.querySelector('pkt-consent') as PktConsent
244
+ await consent.updateComplete
245
+
246
+ const button = consent.querySelector('pkt-button')
247
+ expect(button).toBeInTheDocument()
248
+
249
+ fireEvent.click(button!)
250
+
251
+ // Fast-forward time to trigger setTimeout
252
+ jest.runAllTimers()
253
+
254
+ expect(window.cookieBanner.openCookieModal).toHaveBeenCalled()
255
+ })
256
+
257
+ test('handles click event on link trigger', async () => {
258
+ const container = await createConsent('triggerType="link"')
259
+ const consent = container.querySelector('pkt-consent') as PktConsent
260
+ await consent.updateComplete
261
+
262
+ const link = consent.querySelector('a.pkt-link')
263
+ expect(link).toBeInTheDocument()
264
+
265
+ fireEvent.click(link!)
266
+
267
+ // Fast-forward time to trigger setTimeout
268
+ jest.runAllTimers()
269
+
270
+ expect(window.cookieBanner.openCookieModal).toHaveBeenCalled()
271
+ })
272
+
273
+ test('dispatches toggle-consent event', async () => {
274
+ const container = await createConsent()
275
+ const consent = container.querySelector('pkt-consent') as PktConsent
276
+ await consent.updateComplete
277
+
278
+ let eventFired = false
279
+ let eventDetail: any = null
280
+
281
+ consent.addEventListener('toggle-consent', (e: Event) => {
282
+ eventFired = true
283
+ eventDetail = (e as CustomEvent).detail
284
+ })
285
+
286
+ // Mock consent data
287
+ const mockConsent = {
288
+ value: JSON.stringify({
289
+ items: [
290
+ { name: 'analytics', consent: true },
291
+ { name: 'marketing', consent: false },
292
+ ],
293
+ }),
294
+ }
295
+
296
+ consent.emitCookieConsents(mockConsent)
297
+
298
+ expect(eventFired).toBe(true)
299
+ expect(eventDetail).toEqual({
300
+ analytics: true,
301
+ marketing: false,
302
+ })
303
+ })
304
+ })
305
+
306
+ describe('Utility methods', () => {
307
+ test('returnJsonOrObject handles JSON strings', async () => {
308
+ const container = await createConsent()
309
+ const consent = container.querySelector('pkt-consent') as PktConsent
310
+
311
+ const jsonString = '{"test": "value"}'
312
+ const result = consent.returnJsonOrObject(jsonString)
313
+
314
+ expect(result).toEqual({ test: 'value' })
315
+ })
316
+
317
+ test('returnJsonOrObject handles non-JSON objects', async () => {
318
+ const container = await createConsent()
319
+ const consent = container.querySelector('pkt-consent') as PktConsent
320
+
321
+ const nonJsonObject = { test: 'value' }
322
+ const result = consent.returnJsonOrObject(nonJsonObject)
323
+
324
+ expect(result).toEqual(nonJsonObject)
325
+ })
326
+
327
+ test('returnJsonOrObject handles invalid JSON gracefully', async () => {
328
+ const container = await createConsent()
329
+ const consent = container.querySelector('pkt-consent') as PktConsent
330
+
331
+ const invalidJson = 'invalid json string'
332
+ const result = consent.returnJsonOrObject(invalidJson)
333
+
334
+ expect(result).toBe(invalidJson)
335
+ })
336
+ })
337
+
338
+ describe('Accessibility', () => {
339
+ test('button trigger is accessible', async () => {
340
+ const container = await createConsent('triggerText="Cookie settings"')
341
+
342
+ const consent = container.querySelector('pkt-consent') as PktConsent
343
+ await consent.updateComplete
344
+
345
+ const results = await axe(container)
346
+ expect(results).toHaveNoViolations()
347
+
348
+ const button = consent.querySelector('pkt-button')
349
+ expect(button).toBeInTheDocument()
350
+ expect(button?.textContent?.trim()).toBe('Cookie settings')
351
+ })
352
+
353
+ test('link trigger is accessible', async () => {
354
+ const container = await createConsent('triggerType="link" triggerText="Cookie settings"')
355
+
356
+ const consent = container.querySelector('pkt-consent') as PktConsent
357
+ await consent.updateComplete
358
+
359
+ const results = await axe(container)
360
+ expect(results).toHaveNoViolations()
361
+
362
+ const link = consent.querySelector('a')
363
+ expect(link).toBeInTheDocument()
364
+ expect(link?.textContent?.trim()).toBe('Cookie settings')
365
+ })
366
+
367
+ test('footer link trigger is accessible', async () => {
368
+ const container = await createConsent(
369
+ 'triggerType="footerlink" triggerText="Cookie settings"',
370
+ )
371
+
372
+ const consent = container.querySelector('pkt-consent') as PktConsent
373
+ await consent.updateComplete
374
+
375
+ const results = await axe(container)
376
+ expect(results).toHaveNoViolations()
377
+
378
+ const link = consent.querySelector('a.pkt-footer__link')
379
+ expect(link).toBeInTheDocument()
380
+ expect(link?.textContent?.trim()).toContain('Cookie settings')
381
+ })
382
+
383
+ test('icon trigger is accessible', async () => {
384
+ const container = await createConsent('triggerType="icon" triggerText="Cookie settings"')
385
+
386
+ const consent = container.querySelector('pkt-consent') as PktConsent
387
+ await consent.updateComplete
388
+
389
+ const results = await axe(container)
390
+ expect(results).toHaveNoViolations()
391
+
392
+ const button = consent.querySelector('pkt-button')
393
+ expect(button).toBeInTheDocument()
394
+ expect(button?.textContent?.trim()).toContain('Cookie settings')
395
+ })
396
+ })
397
+
398
+ describe('Integration and lifecycle', () => {
399
+ test('sets global variables on firstUpdated', async () => {
400
+ const googleId = 'GA-123456789'
401
+ const hotjarId = 'HJ-987654321'
402
+ const container = await createConsent(
403
+ `devMode googleAnalyticsId="${googleId}" hotjarId="${hotjarId}" cookieDomain=".test.com"`,
404
+ )
405
+
406
+ const consent = container.querySelector('pkt-consent') as PktConsent
407
+ await consent.updateComplete
408
+
409
+ expect(window.cookieBanner_googleAnalyticsId).toBe(googleId)
410
+ expect(window.cookieBanner_hotjarId).toBe(hotjarId)
411
+ expect(window.cookieBanner_devMode).toBe(true)
412
+ expect(window.cookieBanner_cookieDomain).toBe('.test.com')
413
+ })
414
+
415
+ test('cleans up event listeners on disconnect', async () => {
416
+ const container = await createConsent()
417
+ const consent = container.querySelector('pkt-consent') as PktConsent
418
+ await consent.updateComplete
419
+
420
+ // Mock the event system
421
+ window.__cookieEvents = {
422
+ on: jest.fn(),
423
+ off: jest.fn(),
424
+ }
425
+
426
+ // Set up a handler
427
+ const mockHandler = jest.fn()
428
+ consent['_cookieEventHandler'] = mockHandler
429
+
430
+ // Disconnect the component
431
+ consent.disconnectedCallback()
432
+
433
+ expect(window.__cookieEvents.off).toHaveBeenCalledWith('CookieManager.setCookie', mockHandler)
434
+ })
435
+ })
436
+ })