@playpilot/tpi 8.10.0 → 8.10.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.
Files changed (37) hide show
  1. package/.env +1 -1
  2. package/dist/editorial.mount.js +9 -9
  3. package/dist/link-injections.js +1 -1
  4. package/dist/mount.js +6 -6
  5. package/package.json +1 -1
  6. package/src/lib/api/youtubeAvailability.ts +3 -4
  7. package/src/lib/enums/TrackingEvent.ts +4 -4
  8. package/src/lib/fakeData.ts +44 -32
  9. package/src/lib/injection.ts +10 -17
  10. package/src/lib/modal.ts +3 -1
  11. package/src/lib/popover.ts +13 -12
  12. package/src/lib/types/injection.d.ts +5 -0
  13. package/src/routes/components/Editorial/Editor.svelte +2 -3
  14. package/src/routes/components/Editorial/EditorItem.svelte +23 -9
  15. package/src/routes/components/Editorial/ManualInjection.svelte +1 -0
  16. package/src/routes/components/{TitlePopover.svelte → InjectionPopover.svelte} +26 -9
  17. package/src/routes/components/ListTitle.svelte +27 -6
  18. package/src/routes/components/Participant.svelte +18 -6
  19. package/src/routes/components/Playlinks/Playlinks.svelte +2 -1
  20. package/src/routes/components/Title.svelte +1 -1
  21. package/src/routes/components/TrackAnyClick.svelte +15 -2
  22. package/src/routes/components/YouTubeEmbed.svelte +1 -1
  23. package/src/routes/elements/+page.svelte +3 -3
  24. package/src/tests/helpers.js +1 -0
  25. package/src/tests/lib/api/youtubeAvailability.test.js +3 -3
  26. package/src/tests/lib/injection.test.js +44 -3
  27. package/src/tests/lib/popover.test.js +7 -7
  28. package/src/tests/routes/components/Editorial/EditorItem.test.js +10 -0
  29. package/src/tests/routes/components/Editorial/ManualInjection.test.js +4 -0
  30. package/src/tests/routes/components/Editorial/PlaylinkTypeSelect.test.js +2 -0
  31. package/src/tests/routes/components/InjectionPopover.test.js +117 -0
  32. package/src/tests/routes/components/ListTitle.test.js +7 -0
  33. package/src/tests/routes/components/Participant.test.js +7 -0
  34. package/src/tests/routes/components/Playlinks/AfterArticlePlaylinks.test.js +4 -0
  35. package/src/tests/routes/components/Playlinks/Playlinks.test.js +1 -1
  36. package/src/tests/routes/components/TrackAnyClick.test.js +137 -1
  37. package/src/tests/routes/components/TitlePopover.test.js +0 -78
@@ -55,7 +55,7 @@
55
55
  <TitlePoster {title} onload={() => posterLoaded = true} lazy={false} />
56
56
  </div>
57
57
 
58
- <div class="heading" use:heading={2} class:truncate={small} id="title">{title.title}</div>
58
+ <div class="heading" use:heading={2} class:truncate={small} id="heading">{title.title}</div>
59
59
 
60
60
  <div class="info">
61
61
  <div class="imdb">
@@ -18,8 +18,17 @@
18
18
  const targetSelector = `${target?.nodeName.toLowerCase()}${getClasslistAsString(target)}`
19
19
  const selector = `${parentSelector} > ${targetSelector}`
20
20
 
21
- const hasDirectTextContent = Array.from(target.childNodes).some(node => node.nodeName === '#text' && !!node.nodeValue?.trim())
22
- const text = hasDirectTextContent ? target.textContent.slice(0, 30) : ''
21
+ const closestButtonOrLink = target.closest('a, button')
22
+ const directTextNode =
23
+ elementHasDirectTextContent(target) ||
24
+ elementHasDirectTextContent(parent) ||
25
+ (closestButtonOrLink && elementHasDirectTextContent(closestButtonOrLink))
26
+
27
+ const textContent = directTextNode ? directTextNode.textContent?.slice(0, 30) : ''
28
+ const label = target.getAttribute('aria-label') || closestButtonOrLink?.getAttribute('aria-label') || parent?.getAttribute('aria-label')
29
+ const placeholder = (target as HTMLInputElement).placeholder
30
+ const alt = (target as HTMLImageElement).alt
31
+ const text = (textContent || label || placeholder || alt || '').trim()
23
32
 
24
33
  track(trackingEvent, null, { selector, text })
25
34
  }
@@ -33,6 +42,10 @@
33
42
 
34
43
  return '.' + classnames.join('.')
35
44
  }
45
+
46
+ function elementHasDirectTextContent(element: Element): Node | undefined {
47
+ return Array.from(element.childNodes).find(node => node.nodeName === '#text' && !!node.textContent?.trim())
48
+ }
36
49
  </script>
37
50
 
38
51
  <svelte:window {onclick} />
@@ -38,7 +38,7 @@
38
38
  </iframe>
39
39
 
40
40
  {#if showMuteControls}
41
- <button class="mute" onclick={() => toggleMute()}>
41
+ <button class="mute" onclick={() => toggleMute()} aria-label="Mute">
42
42
  <IconMute muted={isMuted} />
43
43
  </button>
44
44
  {/if}
@@ -16,7 +16,7 @@
16
16
  import RoundButton from '../components/RoundButton.svelte'
17
17
  import SkeletonText from '../components/SkeletonText.svelte'
18
18
  import Title from '../components/Title.svelte'
19
- import TitlePopover from '../components/TitlePopover.svelte'
19
+ import InjectionPopover from '../components/InjectionPopover.svelte'
20
20
  import Tooltip from '../components/Tooltip.svelte'
21
21
  import ExploreRouter from '../components/Explore/ExploreRouter.svelte'
22
22
  import TitlesRail from '../components/Rails/TitlesRail.svelte'
@@ -69,10 +69,10 @@
69
69
  </div>
70
70
 
71
71
  <div>
72
- <h3>TitlePopover.svelte</h3>
72
+ <h3>InjectionPopover.svelte</h3>
73
73
  <div class="item">
74
74
  <div style:height="500px"></div>
75
- <TitlePopover {title} event={{} as MouseEvent} />
75
+ <InjectionPopover data={title} event={{} as MouseEvent} />
76
76
  </div>
77
77
  </div>
78
78
  </div>
@@ -44,5 +44,6 @@ export function generateInjection(sentence, title) {
44
44
  playpilot_url: 'https://some-link.com/',
45
45
  key: 'some-key-' + Math.random().toString(),
46
46
  title_details,
47
+ type: 'title',
47
48
  }
48
49
  }
@@ -1,8 +1,6 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest'
2
2
  import { isYouTubeVideoAvailableInRegion } from '$lib/api/youtubeAvailability'
3
3
  import { fakeFetch } from '../../helpers'
4
- import { track } from '$lib/tracking'
5
- import { TrackingEvent } from '$lib/enums/TrackingEvent'
6
4
 
7
5
  vi.mock('$lib/tracking', () => ({
8
6
  track: vi.fn(),
@@ -64,7 +62,9 @@ describe('youtubeAvailability', () => {
64
62
  fakeFetch({ ok: false, status: 500 })
65
63
 
66
64
  expect(await isYouTubeVideoAvailableInRegion('video-id')).toBe(false)
67
- expect(track).toHaveBeenCalledWith(TrackingEvent.YouTubeAvailabilityRequestFailed, null, { message: expect.any(String) })
65
+
66
+ // For now it's silently failing on purpose
67
+ // expect(track).toHaveBeenCalledWith(TrackingEvent.YouTubeAvailabilityRequestFailed, null, { message: expect.any(String) })
68
68
  })
69
69
  })
70
70
  })
@@ -1,11 +1,12 @@
1
1
  import { fireEvent } from '@testing-library/svelte'
2
2
  import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
3
3
 
4
- import { injectLinksInDocument, clearLinkInjections, clearLinkInjection, isAvailableAsManualInjection, filterRemovedAndInactiveInjections, isEquivalentInjection, filterInvalidInTextInjections, filterInvalidAfterArticleInjections, isValidInjection, isValidPlaylinkType, removePlayPilotTitleLinks } from '$lib/injection'
4
+ import { injectLinksInDocument, clearLinkInjections, clearLinkInjection, isAvailableAsManualInjection, filterRemovedAndInactiveInjections, isEquivalentInjection, filterInvalidInTextInjections, filterInvalidAfterArticleInjections, isValidInjection, isValidPlaylinkType, removePlayPilotTitleLinks, hasValidTypeData } from '$lib/injection'
5
5
  import { mount, unmount } from 'svelte'
6
6
  import { fakeFetch, generateInjection } from '../helpers'
7
7
  import { openModalForInjectedLink } from '$lib/modal'
8
8
  import { getLinkInjectionElements } from '$lib/injectionElements'
9
+ import { participants } from '$lib/fakeData'
9
10
 
10
11
  vi.mock('svelte', () => ({
11
12
  mount: vi.fn(),
@@ -568,7 +569,7 @@ describe('injection.ts', () => {
568
569
  })
569
570
 
570
571
  it('Should remove all popovers that may not have been removed properly in previous destroy attempts', async () => {
571
- document.body.innerHTML = '<div data-playpilot-title-popover></div> <p>This is a sentence with an injection.</p>'
572
+ document.body.innerHTML = '<div data-playpilot-injection-popover></div> <p>This is a sentence with an injection.</p>'
572
573
 
573
574
  const elements = Array.from(document.body.querySelectorAll('p'))
574
575
  const injection = generateInjection('This is a sentence with an injection.', 'an injection')
@@ -583,7 +584,7 @@ describe('injection.ts', () => {
583
584
  await fireEvent.mouseMove(document.body)
584
585
  vi.advanceTimersByTime(200)
585
586
 
586
- expect(document.querySelectorAll('[data-playpilot-title-popover]')).toHaveLength(0)
587
+ expect(document.querySelectorAll('[data-playpilot-injection-popover]')).toHaveLength(0)
587
588
  })
588
589
 
589
590
  it('Should inject links of the same phrase when multiple are present', () => {
@@ -946,6 +947,20 @@ describe('injection.ts', () => {
946
947
 
947
948
  expect(document.querySelector('a')?.closest('[data-playpilot-injection-key]')).toBeTruthy()
948
949
  })
950
+
951
+ describe('Participants', () => {
952
+ it('Should inject participants just as a title', () => {
953
+ document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
954
+
955
+ const elements = Array.from(document.body.querySelectorAll('p'))
956
+ const injection = { ...generateInjection('This is a sentence with an injection.', 'an injection'), type: 'participant', participant_details: participants[0] }
957
+
958
+ // @ts-ignore
959
+ injectLinksInDocument(elements, { aiInjections: [injection], manualInjections: [] })
960
+
961
+ expect(document.querySelector('[data-playpilot-injection-key]')).toBeTruthy()
962
+ })
963
+ })
949
964
  })
950
965
 
951
966
  describe('clearLinkInjections', () => {
@@ -1127,6 +1142,32 @@ describe('injection.ts', () => {
1127
1142
  })
1128
1143
  })
1129
1144
 
1145
+ describe('hasValidTypeData', () => {
1146
+ it('Should return true if injection has type title and has title_details', () => {
1147
+ // @ts-ignore
1148
+ expect(hasValidTypeData({ type: 'title', title_details: {} })).toBe(true)
1149
+ })
1150
+
1151
+ it('Should return true if injection has type participant and has participant_details', () => {
1152
+ // @ts-ignore
1153
+ expect(hasValidTypeData({ type: 'participant', participant_details: {} })).toBe(true)
1154
+ })
1155
+
1156
+ it('Should return false if injection has type title but has no title_details', () => {
1157
+ // @ts-ignore
1158
+ expect(hasValidTypeData({ type: 'title', title_details: null })).toBe(false)
1159
+ // @ts-ignore
1160
+ expect(hasValidTypeData({ type: 'title' })).toBe(false)
1161
+ })
1162
+
1163
+ it('Should return false if injection has type participant but has no participant_details', () => {
1164
+ // @ts-ignore
1165
+ expect(hasValidTypeData({ type: 'participant', participant_details: null })).toBe(false)
1166
+ // @ts-ignore
1167
+ expect(hasValidTypeData({ type: 'participant' })).toBe(false)
1168
+ })
1169
+ })
1170
+
1130
1171
  describe('removePlayPilotTitleLinks', () => {
1131
1172
  it('Should remove existing title links', () => {
1132
1173
  document.body.innerHTML = `
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
2
  import { linkInjections } from '$lib/fakeData'
3
3
  import { mount, unmount } from 'svelte'
4
4
  import { clearCurrentlyHoveredInjection, currentlyHoveredInjection, destroyLinkPopover, isPopoverActive, openPopoverForInjectedLink } from '$lib/popover'
5
- import TitlePopover from '../../routes/components/TitlePopover.svelte'
5
+ import InjectionPopover from '../../routes/components/InjectionPopover.svelte'
6
6
  import { waitFor } from '@testing-library/svelte'
7
7
 
8
8
  vi.mock('svelte', () => ({
@@ -22,17 +22,17 @@ describe('popover.js', () => {
22
22
  describe('openPopoverForInjectedLink', () => {
23
23
  it('Should mount popover', async () => {
24
24
  // @ts-ignore
25
- openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections)
25
+ openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections[0])
26
26
 
27
27
  await waitFor(() => {
28
- expect(mount).toHaveBeenCalledWith(TitlePopover, expect.any(Object))
28
+ expect(mount).toHaveBeenCalledWith(InjectionPopover, expect.any(Object))
29
29
  expect(currentlyHoveredInjection).toBeTruthy()
30
30
  })
31
31
  })
32
32
 
33
33
  it('Should not mount popover if user is no longer hovering after delay', async () => {
34
34
  // @ts-ignore
35
- openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections)
35
+ openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections[0])
36
36
 
37
37
  await new Promise(res => setTimeout(res, 50))
38
38
 
@@ -46,19 +46,19 @@ describe('popover.js', () => {
46
46
 
47
47
  describe('destroyLinkPopover', () => {
48
48
  it('Should not call unmount but still remove potential popover elements if no active popover is set', async () => {
49
- document.body.innerHTML = '<div data-playpilot-title-popover></div> <div data-playpilot-title-popover></div>'
49
+ document.body.innerHTML = '<div data-playpilot-injection-popover></div> <div data-playpilot-injection-popover></div>'
50
50
 
51
51
  destroyLinkPopover()
52
52
 
53
53
  expect(unmount).not.toHaveBeenCalled()
54
- expect(document.querySelectorAll('[data-playpilot-title-popover]')).toHaveLength(0)
54
+ expect(document.querySelectorAll('[data-playpilot-injection-popover]')).toHaveLength(0)
55
55
  })
56
56
  })
57
57
 
58
58
  describe('clearCurrentlyHoveredInjection', () => {
59
59
  it('Should clear currentlyHoveredInjection', async () => {
60
60
  // @ts-ignore
61
- openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections)
61
+ openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections[0])
62
62
 
63
63
  await waitFor(() => expect(currentlyHoveredInjection).toBeTruthy())
64
64
 
@@ -6,6 +6,7 @@ import { injectLinksInDocument } from '$lib/injection'
6
6
  import { track } from '$lib/tracking'
7
7
  import { generateInjection } from '../../../helpers'
8
8
  import { removeImageUrlPrefix } from '$lib/image'
9
+ import { linkInjections as fakeLinkInjections } from '$lib/fakeData'
9
10
 
10
11
  vi.mock('$lib/tracking', () => ({
11
12
  track: vi.fn(),
@@ -200,4 +201,13 @@ describe('EditorItem.svelte', () => {
200
201
 
201
202
  expect(getByText('What would you like to report?')).toBeTruthy()
202
203
  })
204
+
205
+ it('Should render as participant when type is participant', () => {
206
+ const { container, queryByLabelText, getByText } = render(EditorItem, { linkInjection: fakeLinkInjections[4] })
207
+
208
+ expect(queryByLabelText('Expand')).not.toBeTruthy()
209
+ expect(getByText(fakeLinkInjections[4].participant_details?.name || '-')).toBeTruthy()
210
+ expect(container.querySelector('.poster')).not.toBeTruthy()
211
+ expect(container.querySelector('.placeholder-image')).toBeTruthy()
212
+ })
203
213
  })
@@ -99,6 +99,7 @@ describe('ManualInjection.svelte', () => {
99
99
  manual: true,
100
100
  phrase_before: 'Some',
101
101
  phrase_after: 'in a',
102
+ type: 'title',
102
103
  })
103
104
  })
104
105
 
@@ -141,6 +142,7 @@ describe('ManualInjection.svelte', () => {
141
142
  manual: true,
142
143
  phrase_before: '',
143
144
  phrase_after: '',
145
+ type: 'title',
144
146
  })
145
147
  })
146
148
 
@@ -183,6 +185,7 @@ describe('ManualInjection.svelte', () => {
183
185
  manual: true,
184
186
  phrase_before: expect.any(String),
185
187
  phrase_after: expect.any(String),
188
+ type: 'title',
186
189
  })
187
190
  })
188
191
 
@@ -225,6 +228,7 @@ describe('ManualInjection.svelte', () => {
225
228
  manual: true,
226
229
  phrase_before: expect.any(String),
227
230
  phrase_after: expect.any(String),
231
+ type: 'title',
228
232
  })
229
233
  })
230
234
 
@@ -11,6 +11,8 @@ describe('PlaylinkTypeSelect.svelte', () => {
11
11
  sentence: 'Some sentence',
12
12
  playpilot_url: 'https://playpilot.com/movie/example-3/',
13
13
  key: 'some-key',
14
+ /** @type {import('$lib/types/injection').LinkInjectionDataType} */
15
+ type: 'title',
14
16
  }
15
17
 
16
18
  beforeEach(() => {
@@ -0,0 +1,117 @@
1
+ import { render } from '@testing-library/svelte'
2
+ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
3
+
4
+ import InjectionPopover from '../../../routes/components/InjectionPopover.svelte'
5
+ import { participants, title } from '$lib/fakeData'
6
+ import { track } from '$lib/tracking'
7
+ import { TrackingEvent } from '$lib/enums/TrackingEvent'
8
+ import { hasConsentedTo } from '$lib/consent'
9
+
10
+ vi.mock('$lib/consent', () => ({
11
+ hasConsentedTo: vi.fn(() => true),
12
+ }))
13
+
14
+ vi.mock('$lib/tracking', () => ({
15
+ track: vi.fn(),
16
+ }))
17
+
18
+ vi.mock('$lib/pixel', () => ({
19
+ isPixelAllowed: vi.fn(),
20
+ }))
21
+
22
+ vi.mock('$lib/api/participants', () => ({
23
+ fetchParticipantsForTitle: vi.fn(),
24
+ }))
25
+
26
+ vi.mock('$lib/api/titles', () => ({
27
+ fetchSimilarTitles: vi.fn(),
28
+ }))
29
+
30
+ describe('InjectionPopover.svelte', () => {
31
+ beforeEach(() => {
32
+ vi.resetAllMocks()
33
+ vi.mocked(hasConsentedTo).mockImplementation(() => true)
34
+ })
35
+
36
+ afterEach(() => {
37
+ vi.useRealTimers()
38
+ })
39
+
40
+ it('Should render top scroll ad when given', () => {
41
+ // @ts-ignore
42
+ window.PlayPilotLinkInjections = { ads: [{ campaign_format: 'top_scroll', content: {}, cta: {} }] }
43
+
44
+ const event = new MouseEvent('mouseenter')
45
+ const { container } = render(InjectionPopover, { event, type: 'title', data: title })
46
+
47
+ expect(container.querySelector('.top-scroll')).toBeTruthy()
48
+ })
49
+
50
+ it('Should not render top scroll ad when not given', () => {
51
+ // @ts-ignore
52
+ window.PlayPilotLinkInjections = { ads: null }
53
+
54
+ const event = new MouseEvent('mouseenter')
55
+ const { container } = render(InjectionPopover, { event, type: 'title', data: title })
56
+
57
+ expect(container.querySelector('.top-scroll')).not.toBeTruthy()
58
+ })
59
+
60
+ describe('Title type', () => {
61
+ it('Should render the given title', () => {
62
+ const event = new MouseEvent('mouseenter')
63
+ const { getByText } = render(InjectionPopover, { event, type: 'title', data: title })
64
+
65
+ expect(getByText(title.title)).toBeTruthy()
66
+ })
67
+
68
+ it('Should call track function when rendered', () => {
69
+ const event = new MouseEvent('mouseenter')
70
+ render(InjectionPopover, { event, type: 'title', data: title })
71
+
72
+ expect(track).toHaveBeenCalledWith(TrackingEvent.InjectionPopoverView, title, { type: 'title' })
73
+ })
74
+
75
+ it('Should call track function with time_spent when destroyed', async () => {
76
+ vi.useFakeTimers()
77
+
78
+ const event = new MouseEvent('mouseenter')
79
+ const { unmount } = render(InjectionPopover, { event, type: 'title', data: title })
80
+
81
+ vi.advanceTimersByTime(200)
82
+ unmount()
83
+
84
+ expect(track).toHaveBeenCalledWith(TrackingEvent.InjectionPopoverClose, title, expect.objectContaining({ time_spent: 200, type: 'title' }))
85
+ })
86
+ })
87
+
88
+ describe('Participant type', () => {
89
+ const participant = participants[0]
90
+
91
+ it('Should render the given participant', () => {
92
+ const event = new MouseEvent('mouseenter')
93
+ const { getByText } = render(InjectionPopover, { event, type: 'participant', data: participant })
94
+
95
+ expect(getByText(participant.name)).toBeTruthy()
96
+ })
97
+
98
+ it('Should call track function when rendered', () => {
99
+ const event = new MouseEvent('mouseenter')
100
+ render(InjectionPopover, { event, type: 'participant', data: participant })
101
+
102
+ expect(track).toHaveBeenCalledWith(TrackingEvent.InjectionPopoverView, null, { type: 'participant', participant: participant.name })
103
+ })
104
+
105
+ it('Should call track function with time_spent when destroyed', async () => {
106
+ vi.useFakeTimers()
107
+
108
+ const event = new MouseEvent('mouseenter')
109
+ const { unmount } = render(InjectionPopover, { event, type: 'participant', data: participant })
110
+
111
+ vi.advanceTimersByTime(200)
112
+ unmount()
113
+
114
+ expect(track).toHaveBeenCalledWith(TrackingEvent.InjectionPopoverClose, null, expect.objectContaining({ type: 'participant', time_spent: 200, participant: participant.name }))
115
+ })
116
+ })
117
+ })
@@ -50,6 +50,13 @@ describe('ListTitle.svelte', () => {
50
50
  expect(onclick).toHaveBeenCalled()
51
51
  })
52
52
 
53
+ it('Should render as small variant when given', () => {
54
+ const { container } = render(ListTitle, { title, compact: true })
55
+
56
+ expect(container.querySelector('.compact')).toBeTruthy()
57
+ expect(container.querySelector('.description')).not.toBeTruthy()
58
+ })
59
+
53
60
  describe('Playlinks', () => {
54
61
  const playlinks = [
55
62
  { name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
@@ -73,4 +73,11 @@ describe('Participant.svelte', () => {
73
73
 
74
74
  expect(fetchTitlesForParticipant).toHaveBeenCalledWith(participants[0], { page: 2 })
75
75
  })
76
+
77
+ it('Should render as small variant when given', () => {
78
+ const { container, queryByText } = render(Participant, { participant: participants[0], small: true })
79
+
80
+ expect(queryByText('Credits')).not.toBeTruthy()
81
+ expect(container.querySelector('.small')).toBeTruthy()
82
+ })
76
83
  })
@@ -23,6 +23,8 @@ const linkInjections = [{
23
23
  poster: 'some-poster',
24
24
  key: 'some-key-1',
25
25
  title_details: title,
26
+ /** @type {import('$lib/types/injection').LinkInjectionDataType} */
27
+ type: 'title',
26
28
  }, {
27
29
  sid: '2',
28
30
  title: 'a sentence',
@@ -31,6 +33,8 @@ const linkInjections = [{
31
33
  poster: 'some-poster',
32
34
  key: 'some-key-1',
33
35
  title_details: title,
36
+ /** @type {import('$lib/types/injection').LinkInjectionDataType} */
37
+ type: 'title',
34
38
  }]
35
39
 
36
40
  describe('AfterArticlePlaylinks.svelte', () => {
@@ -85,7 +85,7 @@ describe('Playlinks.svelte', () => {
85
85
 
86
86
  await fireEvent.click(getByText(playlinks[0].name))
87
87
 
88
- expect(track).toHaveBeenCalledWith(TrackingEvent.TitlePopoverPlaylinkClick, title, { playlink: playlinks[0].name })
88
+ expect(track).toHaveBeenCalledWith(TrackingEvent.InjectionPopoverPlaylinkClick, title, { playlink: playlinks[0].name })
89
89
  })
90
90
 
91
91
  it('Should call track function for modal when clicked inside of modal scope', async () => {
@@ -42,7 +42,7 @@ describe('TrackAnyClick.svelte', () => {
42
42
  TrackingEvent.ExploreAnyClick,
43
43
  null,
44
44
  expect.objectContaining({
45
- text: 'This is a very long text that ',
45
+ text: 'This is a very long text that',
46
46
  }),
47
47
  )
48
48
  })
@@ -104,4 +104,140 @@ describe('TrackAnyClick.svelte', () => {
104
104
 
105
105
  expect(track).toHaveBeenCalledWith(TrackingEvent.ExploreAnyClick, null, expect.objectContaining({ selector: 'section > p' }))
106
106
  })
107
+
108
+ it('Should select text of closest button', async () => {
109
+ document.body.innerHTML = '<button>Some text <span><em></em></span></button>'
110
+
111
+ render(ClickTracking)
112
+
113
+ // @ts-ignore
114
+ await fireEvent.click(document.querySelector('em'))
115
+
116
+ expect(track).toHaveBeenCalledWith(
117
+ TrackingEvent.ExploreAnyClick,
118
+ null,
119
+ expect.objectContaining({
120
+ text: 'Some text',
121
+ }),
122
+ )
123
+ })
124
+
125
+ it('Should select text of closest link', async () => {
126
+ document.body.innerHTML = '<a href="/">Some text <span><em></em></span></a>'
127
+
128
+ render(ClickTracking)
129
+
130
+ // @ts-ignore
131
+ await fireEvent.click(document.querySelector('em'))
132
+
133
+ expect(track).toHaveBeenCalledWith(
134
+ TrackingEvent.ExploreAnyClick,
135
+ null,
136
+ expect.objectContaining({
137
+ text: 'Some text',
138
+ }),
139
+ )
140
+ })
141
+
142
+ it('Should use placeholder text', async () => {
143
+ document.body.innerHTML = '<input placeholder="Some placeholder" />'
144
+
145
+ render(ClickTracking)
146
+
147
+ // @ts-ignore
148
+ await fireEvent.click(document.querySelector('input'))
149
+
150
+ expect(track).toHaveBeenCalledWith(
151
+ TrackingEvent.ExploreAnyClick,
152
+ null,
153
+ expect.objectContaining({
154
+ text: 'Some placeholder',
155
+ }),
156
+ )
157
+ })
158
+
159
+ it('Should use alt text', async () => {
160
+ document.body.innerHTML = '<img alt="Some alt text" />'
161
+
162
+ render(ClickTracking)
163
+
164
+ // @ts-ignore
165
+ await fireEvent.click(document.querySelector('img'))
166
+
167
+ expect(track).toHaveBeenCalledWith(
168
+ TrackingEvent.ExploreAnyClick,
169
+ null,
170
+ expect.objectContaining({
171
+ text: 'Some alt text',
172
+ }),
173
+ )
174
+ })
175
+
176
+ it('Should use aria label', async () => {
177
+ document.body.innerHTML = '<button aria-label="Some aria label"></button>'
178
+
179
+ render(ClickTracking)
180
+
181
+ // @ts-ignore
182
+ await fireEvent.click(document.querySelector('button'))
183
+
184
+ expect(track).toHaveBeenCalledWith(
185
+ TrackingEvent.ExploreAnyClick,
186
+ null,
187
+ expect.objectContaining({
188
+ text: 'Some aria label',
189
+ }),
190
+ )
191
+ })
192
+
193
+ it('Should use parent aria label', async () => {
194
+ document.body.innerHTML = '<button aria-label="Some aria label"><img /></button>'
195
+
196
+ render(ClickTracking)
197
+
198
+ // @ts-ignore
199
+ await fireEvent.click(document.querySelector('img'))
200
+
201
+ expect(track).toHaveBeenCalledWith(
202
+ TrackingEvent.ExploreAnyClick,
203
+ null,
204
+ expect.objectContaining({
205
+ text: 'Some aria label',
206
+ }),
207
+ )
208
+ })
209
+
210
+ it('Should prefer aria label over placeholder and alt text', async () => {
211
+ document.body.innerHTML = '<button aria-label="Some aria label"><img placeholder="Some placeholder" alt="Some alt text" /></button>'
212
+
213
+ render(ClickTracking)
214
+
215
+ // @ts-ignore
216
+ await fireEvent.click(document.querySelector('img'))
217
+
218
+ expect(track).toHaveBeenCalledWith(
219
+ TrackingEvent.ExploreAnyClick,
220
+ null,
221
+ expect.objectContaining({
222
+ text: 'Some aria label',
223
+ }),
224
+ )
225
+ })
226
+
227
+ it('Should prefer text content over aria label, placeholder and alt text', async () => {
228
+ document.body.innerHTML = '<button aria-label="Some aria label">Some text <img placeholder="Some placeholder" alt="Some alt text" /></button>'
229
+
230
+ render(ClickTracking)
231
+
232
+ // @ts-ignore
233
+ await fireEvent.click(document.querySelector('img'))
234
+
235
+ expect(track).toHaveBeenCalledWith(
236
+ TrackingEvent.ExploreAnyClick,
237
+ null,
238
+ expect.objectContaining({
239
+ text: 'Some text',
240
+ }),
241
+ )
242
+ })
107
243
  })