@playpilot/tpi 5.12.0-beta.similar.1 → 5.12.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 (44) hide show
  1. package/dist/link-injections.js +10 -11
  2. package/events.md +1 -0
  3. package/package.json +1 -2
  4. package/src/lib/enums/SplitTest.ts +1 -2
  5. package/src/lib/enums/TrackingEvent.ts +2 -1
  6. package/src/lib/fakeData.ts +0 -70
  7. package/src/lib/linkInjection.ts +55 -12
  8. package/src/lib/playlink.ts +1 -4
  9. package/src/lib/splitTest.ts +9 -2
  10. package/src/lib/types/title.d.ts +0 -2
  11. package/src/routes/+page.svelte +0 -1
  12. package/src/routes/components/Icons/IconClose.svelte +1 -1
  13. package/src/routes/components/Icons/IconScrollIndicator.svelte +3 -0
  14. package/src/routes/components/Modal.svelte +7 -41
  15. package/src/routes/components/Playlinks.svelte +3 -6
  16. package/src/routes/components/Popover.svelte +111 -12
  17. package/src/routes/components/Title.svelte +18 -33
  18. package/src/routes/components/TitleModal.svelte +3 -2
  19. package/src/routes/components/TitlePopover.svelte +2 -5
  20. package/src/tests/lib/linkInjection.test.js +35 -9
  21. package/src/tests/lib/playlink.test.js +10 -25
  22. package/src/tests/lib/splitTest.test.js +31 -3
  23. package/src/tests/routes/components/Modal.test.js +19 -51
  24. package/src/tests/routes/components/Title.test.js +0 -8
  25. package/src/tests/setup.js +10 -0
  26. package/src/lib/modal.ts +0 -81
  27. package/src/lib/types/participant.d.ts +0 -14
  28. package/src/routes/components/Icons/IconArrow.svelte +0 -17
  29. package/src/routes/components/ListTitle.svelte +0 -172
  30. package/src/routes/components/Participant.svelte +0 -88
  31. package/src/routes/components/ParticipantModal.svelte +0 -30
  32. package/src/routes/components/PlaylinkIcon.svelte +0 -41
  33. package/src/routes/components/Rails/ParticipantsRail.svelte +0 -56
  34. package/src/routes/components/Rails/Rail.svelte +0 -91
  35. package/src/routes/components/Rails/SimilarRail.svelte +0 -16
  36. package/src/routes/components/Rails/TitlesRail.svelte +0 -95
  37. package/src/routes/components/Tabs.svelte +0 -47
  38. package/src/routes/components/TitlePoster.svelte +0 -30
  39. package/src/tests/lib/modal.test.js +0 -148
  40. package/src/tests/routes/components/ListTitle.test.js +0 -82
  41. package/src/tests/routes/components/PlaylinkIcon.test.js +0 -27
  42. package/src/tests/routes/components/Rails/ParticipantsRail.test.js +0 -41
  43. package/src/tests/routes/components/Rails/TitleRail.test.js +0 -38
  44. package/src/tests/routes/components/TitlePoster.test.js +0 -20
@@ -1,18 +1,18 @@
1
1
  <script lang="ts">
2
+ import IconScrollIndicator from './Icons/IconScrollIndicator.svelte'
2
3
  import { SplitTest } from '$lib/enums/SplitTest'
3
4
  import { isInSplitTestVariant } from '$lib/splitTest'
4
5
  import { onMount, setContext, tick, type Snippet } from 'svelte'
5
6
  import { prefersReducedMotion } from 'svelte/motion'
6
- import { fly } from 'svelte/transition'
7
+ import { fade, fly } from 'svelte/transition'
7
8
 
8
9
  interface Props {
9
10
  children: Snippet
10
11
  bubble?: Snippet | null
11
12
  append?: Snippet | null
12
- maxHeight?: number
13
13
  }
14
14
 
15
- let { children, bubble, append, maxHeight = $bindable() }: Props = $props()
15
+ const { children, bubble, append }: Props = $props()
16
16
 
17
17
  setContext('scope', 'popover')
18
18
 
@@ -22,14 +22,30 @@
22
22
 
23
23
  let element: HTMLElement | null = $state(null)
24
24
  let flip = $state(false)
25
- let appendHeight = $state(0)
25
+ let canAppendFit = $state(true)
26
+ let scrollIndicatorVisible = $state(false)
27
+ let fullHeight = $state(0) // Full height of all snippets together, via bind:clientHeight
28
+ let appendHeight = $state(0) // Only the height of the append (in most cases, this will be the display ad), via bind:clientHeight
29
+ let maxHeight = $state(0) // Max height that is computed based on the available screen size, via setMaxHeight()
26
30
 
27
31
  onMount(async () => {
28
32
  await tick()
29
33
  positionElement()
30
34
 
35
+ await setMaxHeight()
36
+
37
+ if (append) {
38
+ // When an append is visible it pushes the dialog's height down. We need to recalculate the popover's available height
39
+ // When there is little room for the popover to show, the append will hide itself. We calculate the new height after hiding.
40
+
41
+ requestAnimationFrame(() => {
42
+ setAppendVisibility()
43
+ setMaxHeight()
44
+ })
45
+ }
46
+
31
47
  await tick()
32
- setMaxHeight()
48
+ setScrollIndicatorVisible()
33
49
  })
34
50
 
35
51
  /**
@@ -50,17 +66,25 @@
50
66
  if (right + offset > window.innerWidth) element.style.left = `${fromRight}px`
51
67
  }
52
68
 
69
+ function setAppendVisibility(): void {
70
+ canAppendFit = fullHeight > 500
71
+ }
72
+
53
73
  /**
54
74
  * Set the max height of the dialog so that it always fits on screen, even after flipping.
55
75
  */
56
- function setMaxHeight(): void {
76
+ async function setMaxHeight(): Promise<void> {
77
+ maxHeight = 0
78
+
79
+ await new Promise(res => requestAnimationFrame(res))
80
+
57
81
  if (!element) return
58
82
 
59
83
  const { top, height } = element.getBoundingClientRect()
60
84
  const offset = getOffset()
61
85
  const gap = append ? parseInt(window.getComputedStyle(element).gap) : 0
62
86
 
63
- const appendOffset = appendHeight + gap
87
+ const appendOffset = canAppendFit ? appendHeight + gap : 0
64
88
  const availableSpace = flip ? (window.innerHeight - top - offset - appendOffset) : (top + height - appendOffset - offset)
65
89
 
66
90
  maxHeight = Math.min(availableSpace, window.innerHeight * 0.8)
@@ -69,11 +93,41 @@
69
93
  function getOffset(): number {
70
94
  return parseFloat(getComputedStyle(document.documentElement).fontSize) // 1 rem
71
95
  }
96
+
97
+ function setScrollIndicatorVisible(): void {
98
+ if (!element?.firstChild) return
99
+
100
+ const dialog = element.firstChild as HTMLElement
101
+ const { offsetHeight, scrollHeight, scrollTop } = dialog
102
+
103
+ if (scrollHeight <= offsetHeight) {
104
+ scrollIndicatorVisible = false
105
+ } else {
106
+ scrollIndicatorVisible = scrollTop + offsetHeight < scrollHeight
107
+ }
108
+ }
72
109
  </script>
73
110
 
74
- <div class="popover" class:flip class:inline={inlineBubble} class:has-bubble={!!bubble && inlineBubble} bind:this={element} style:--max-height={maxHeight ? maxHeight + 'px' : null} tabindex="-1" aria-hidden="true">
75
- <div class="dialog" transition:fly|global={transition} data-view-transition-old>
111
+ <div
112
+ class="popover"
113
+ class:flip
114
+ class:inline={inlineBubble}
115
+ class:has-bubble={!!bubble && inlineBubble}
116
+ style:--max-height={maxHeight ? maxHeight + 'px' : null}
117
+ tabindex="-1"
118
+ aria-hidden="true"
119
+ bind:this={element}
120
+ bind:clientHeight={fullHeight}>
121
+ <div class="dialog" transition:fly|global={transition} onscroll={setScrollIndicatorVisible} data-view-transition-old>
76
122
  {@render children()}
123
+
124
+ {#if scrollIndicatorVisible}
125
+ <div class="scroll-indicator" transition:fade={{ duration: 100 }}>
126
+ <div class="scroll-indicator-icon">
127
+ <IconScrollIndicator />
128
+ </div>
129
+ </div>
130
+ {/if}
77
131
  </div>
78
132
 
79
133
  {#if bubble}
@@ -82,7 +136,7 @@
82
136
  </div>
83
137
  {/if}
84
138
 
85
- {#if append}
139
+ {#if append && canAppendFit}
86
140
  <div class="append" transition:fly|global={transition} bind:clientHeight={appendHeight}>
87
141
  {@render append()}
88
142
  </div>
@@ -90,24 +144,36 @@
90
144
  </div>
91
145
 
92
146
  <style lang="scss">
147
+ @keyframes delay-render {
148
+ from {
149
+ opacity: 0;
150
+ }
151
+
152
+ to {
153
+ opacity: 1;
154
+ }
155
+ }
156
+
93
157
  .popover {
94
158
  --offset: #{margin(0.5)};
95
159
  display: flex;
96
160
  flex-direction: column-reverse;
97
161
  gap: margin(0.5);
98
162
  position: absolute;
99
- top: calc((var(--offset) - 1px) * -1); /* Add 1 pixel to account for rounding errors */
163
+ top: calc((var(--offset) - 1px) * -1); // Add 1 pixel to account for rounding errors
100
164
  left: 0;
101
165
  width: calc(100vw - margin(2));
102
166
  max-width: margin(20);
103
167
  padding: var(--offset) 0;
104
168
  transform: translateY(calc(-100% + var(--offset)));
169
+ opacity: 0;
170
+ animation: delay-render 0ms 1ms forwards; // Allow height calculations to perform before showing the popover
105
171
  z-index: 2147483647; // As high as she goes
106
172
 
107
173
  &.flip {
108
174
  flex-direction: column;
109
175
  top: auto;
110
- bottom: calc(var(--offset) + 1px); /* Add 1 pixel to account for rounding errors */
176
+ bottom: calc(var(--offset) + 1px); // Add 1 pixel to account for rounding errors
111
177
  transform: translateY(calc(100% + var(--offset)));
112
178
 
113
179
  &.inline {
@@ -138,4 +204,37 @@
138
204
  border-top-right-radius: 0;
139
205
  }
140
206
  }
207
+
208
+ .scroll-indicator {
209
+ z-index: 1;
210
+ position: sticky;
211
+ bottom: 0;
212
+ pointer-events: none;
213
+
214
+ &::after {
215
+ content: "";
216
+ display: block;
217
+ position: absolute;
218
+ bottom: 0;
219
+ left: 0;
220
+ height: margin(2);
221
+ width: 100%;
222
+ background: linear-gradient(to bottom, transparent, black);
223
+ opacity: var(--playpilot-popover-scroll-indicator-opacity, 0.5);
224
+ }
225
+ }
226
+
227
+ .scroll-indicator-icon {
228
+ z-index: 1;
229
+ position: absolute;
230
+ display: flex;
231
+ bottom: margin(0.25);
232
+ left: 50%;
233
+ transform: translateX(-50%);
234
+ opacity: 0.75;
235
+
236
+ :global(svg) {
237
+ fill: var(--playpilot-popover-scroll-indicator-fill, var(--playpilot-detail-text-color, var(--playpilot-text-color))) !important;
238
+ }
239
+ }
141
240
  </style>
@@ -3,35 +3,33 @@
3
3
  import Playlinks from './Playlinks.svelte'
4
4
  import Description from './Description.svelte'
5
5
  import IconIMDb from './Icons/IconIMDb.svelte'
6
- import ParticipantsRail from './Rails/ParticipantsRail.svelte'
7
- import SimilarRail from './Rails/SimilarRail.svelte'
8
- import TitlePoster from './TitlePoster.svelte'
9
6
  import { t } from '$lib/localization'
10
7
  import type { TitleData } from '$lib/types/title'
11
8
  import { heading } from '$lib/actions/heading'
12
9
  import { removeImageUrlPrefix } from '$lib/image'
13
- import { participants } from '$lib/fakeData'
14
10
 
15
11
  interface Props {
16
12
  title: TitleData
17
13
  small?: boolean
18
- compact?: boolean
19
14
  }
20
15
 
21
- const { title, small = false, compact = false }: Props = $props()
16
+ const { title, small = false }: Props = $props()
22
17
 
23
18
  let posterLoaded = $state(false)
24
19
  let backgroundLoaded = $state(false)
25
20
  let useBackgroundFallback = $state(false)
26
21
  </script>
27
22
 
28
- <div class="content" class:small class:compact data-playpilot-link-injections-title>
23
+ <div class="content" class:small data-playpilot-link-injections-title>
29
24
  <div class="header">
30
- {#if !compact}
31
- <div class="poster" class:loaded={posterLoaded}>
32
- <TitlePoster {title} onload={() => posterLoaded = true} />
33
- </div>
34
- {/if}
25
+ <div class="top">
26
+ <img
27
+ class="poster"
28
+ class:loaded={posterLoaded}
29
+ src={removeImageUrlPrefix(title.standing_poster)}
30
+ alt="Movie poster for '{title.title}'"
31
+ onload={() => posterLoaded = true} />
32
+ </div>
35
33
 
36
34
  <div class="heading" use:heading={2} class:truncate={small} id="title">{title.title}</div>
37
35
 
@@ -57,17 +55,11 @@
57
55
 
58
56
  {#if !small}
59
57
  <Description text={title.description} blurb={title.blurb} />
60
-
61
- {#if true || title.participants?.length}
62
- <ParticipantsRail participants={participants} />
63
- {/if}
64
-
65
- <SimilarRail />
66
58
  {/if}
67
59
  </div>
68
60
  </div>
69
61
 
70
- <div class="background" class:faded={compact}>
62
+ <div class="background">
71
63
  {#key useBackgroundFallback}
72
64
  <img
73
65
  src={removeImageUrlPrefix(useBackgroundFallback ? title.standing_poster : title.medium_poster)}
@@ -92,10 +84,6 @@
92
84
  .small & {
93
85
  font-size: var(--playpilot-detail-title-font-size-small, margin(1.125));
94
86
  }
95
-
96
- .compact & {
97
- margin-top: 0;
98
- }
99
87
  }
100
88
 
101
89
  .content {
@@ -115,23 +103,25 @@
115
103
  }
116
104
 
117
105
  &.small {
118
- font-size: var(--playpilot-detail-font-size-small, 12px);
106
+ font-size: var(--playpilot-detail-font-size, 12px);
119
107
  line-height: 1.45;
120
108
  padding-bottom: margin(0.5);
121
109
  }
122
110
  }
123
111
 
124
112
  .header {
125
- padding: margin(3) 0 margin(1);
113
+ padding: margin(2) 0 margin(1);
126
114
  background: transparent;
127
115
 
128
116
  .small & {
129
117
  padding-top: margin(1);
130
118
  }
119
+ }
131
120
 
132
- .compact & {
133
- padding: 0 0 margin(1);
134
- }
121
+ .top {
122
+ display: flex;
123
+ justify-content: space-between;
124
+ align-items: flex-end;
135
125
  }
136
126
 
137
127
  .poster {
@@ -142,7 +132,6 @@
142
132
  border-radius: var(--playpilot-detail-image-border-radius, margin(0.5));
143
133
  background: var(--playpilot-detail-image-background, var(--playpilot-content));
144
134
  box-shadow: var(--playpilot-detail-image-shadow, var(--playpilot-shadow));
145
- overflow: hidden;
146
135
  opacity: 0;
147
136
 
148
137
  &.loaded {
@@ -221,10 +210,6 @@
221
210
  }
222
211
  }
223
212
 
224
- .faded {
225
- opacity: 0.15;
226
- }
227
-
228
213
  .capitalize {
229
214
  text-transform: capitalize;
230
215
  }
@@ -10,10 +10,11 @@
10
10
  import { getFirstAdOfType } from '$lib/ads'
11
11
 
12
12
  interface Props {
13
+ onclose: () => void,
13
14
  title: TitleData
14
15
  }
15
16
 
16
- const { title }: Props = $props()
17
+ const { onclose, title }: Props = $props()
17
18
 
18
19
  const topScrollAd = getFirstAdOfType('top_scroll')
19
20
  const displayAd = getFirstAdOfType('card')
@@ -48,6 +49,6 @@
48
49
  {/if}
49
50
  {/snippet}
50
51
 
51
- <Modal {onscroll} prepend={displayAd ? prepend : null} bubble={topScrollAd ? bubble : null} tall>
52
+ <Modal {onclose} {onscroll} prepend={displayAd ? prepend : null} bubble={topScrollAd ? bubble : null} tall>
52
53
  <Title {title} />
53
54
  </Modal>
@@ -19,11 +19,8 @@
19
19
  const topScroll = getFirstAdOfType('top_scroll')
20
20
  const displayAd = getFirstAdOfType('card')
21
21
 
22
- let maxHeight = $state(0)
23
22
  let element: HTMLElement | null = $state(null)
24
23
 
25
- const compact = $derived(!!maxHeight && maxHeight < 250)
26
-
27
24
  track(TrackingEvent.TitlePopoverView, title)
28
25
 
29
26
  onMount(() => {
@@ -68,8 +65,8 @@
68
65
  {/snippet}
69
66
 
70
67
  <div class="title-popover" bind:this={element} data-playpilot-title-popover role="region" aria-labelledby="title">
71
- <Popover bind:maxHeight append={displayAd && !compact ? append : null} bubble={topScroll ? bubble : null}>
72
- <Title {title} {compact} small />
68
+ <Popover append={displayAd ? append : null} bubble={topScroll ? bubble : null}>
69
+ <Title {title} small />
73
70
  </Popover>
74
71
  </div>
75
72
 
@@ -1,21 +1,18 @@
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, getLinkInjectionElements, insertAfterArticlePlaylinks, getLinkInjectionsParentElement, isAvailableAsManualInjection, filterRemovedAndInactiveInjections, isEquivalentInjection, filterInvalidInTextInjections, filterInvalidAfterArticleInjections, isValidInjection, isValidPlaylinkType, removePlayPilotTitleLinks } from '$lib/linkInjection'
4
+ import { injectLinksInDocument, clearLinkInjections, clearLinkInjection, getLinkInjectionElements, insertAfterArticlePlaylinks, getLinkInjectionsParentElement, isAvailableAsManualInjection, filterRemovedAndInactiveInjections, isEquivalentInjection, filterInvalidInTextInjections, filterInvalidAfterArticleInjections, isValidInjection, isValidPlaylinkType, removePlayPilotTitleLinks, trackLinkIntersection } from '$lib/linkInjection'
5
5
  import { mount, unmount } from 'svelte'
6
6
  import { fakeFetch, generateInjection } from '../helpers'
7
- import { openModal } from '$lib/modal'
7
+ import { track } from '$lib/tracking'
8
8
 
9
9
  vi.mock('svelte', () => ({
10
10
  mount: vi.fn(),
11
11
  unmount: vi.fn(),
12
12
  }))
13
13
 
14
- vi.mock('$lib/modal', () => ({
15
- openModal: vi.fn(),
16
- destroyAllModals: vi.fn(),
17
- getPreviousModal: vi.fn(),
18
- goBackToPreviousModal: vi.fn(),
14
+ vi.mock('$lib/tracking', () => ({
15
+ track: vi.fn(),
19
16
  }))
20
17
 
21
18
  function mockMatchMedia(matches = false) {
@@ -314,7 +311,7 @@ describe('linkInjection.js', () => {
314
311
  expect(() => injectLinksInDocument(elements, { aiInjections: linkInjections, manualInjections: [] })).not.toThrow()
315
312
  })
316
313
 
317
- it('Should open modal when link is clicked', async () => {
314
+ it('Should mount modal when clicked', async () => {
318
315
  document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
319
316
 
320
317
  const elements = Array.from(document.body.querySelectorAll('p'))
@@ -325,7 +322,27 @@ describe('linkInjection.js', () => {
325
322
  const link = /** @type {HTMLAnchorElement} */ (document.querySelector('a'))
326
323
  await fireEvent.click(link)
327
324
 
328
- expect(openModal).toHaveBeenCalled()
325
+ expect(mount).toHaveBeenCalled()
326
+ })
327
+
328
+ it('Should not mount modal multiple times if modal is already open', async () => {
329
+ const sentence = 'This is a sentence with an injection.'
330
+ document.body.innerHTML = `<p>${sentence}</p>`
331
+
332
+ const elements = Array.from(document.body.querySelectorAll('p'))
333
+
334
+ const linkInjections = [
335
+ generateInjection(sentence, 'a sentence'),
336
+ generateInjection(sentence, 'an injection'),
337
+ ]
338
+
339
+ injectLinksInDocument(elements, { aiInjections: linkInjections, manualInjections: [] })
340
+
341
+ const links = document.querySelectorAll('a')
342
+ await fireEvent.click(links[0])
343
+ await fireEvent.click(links[1])
344
+
345
+ expect(mount).toHaveBeenCalledTimes(1)
329
346
  })
330
347
 
331
348
  it('Should not fire given onclick function when clicked with modifier keys or not left click', async () => {
@@ -1225,4 +1242,13 @@ describe('linkInjection.js', () => {
1225
1242
  expect(document.querySelector('a')).toBeTruthy()
1226
1243
  })
1227
1244
  })
1245
+
1246
+ describe('trackLinkIntersection', () => {
1247
+ it('Should fire tracking for each intersecting link', () => {
1248
+ // @ts-ignore
1249
+ trackLinkIntersection([{ isIntersecting: true }, { isIntersecting: true }, { isIntersecting: false }])
1250
+
1251
+ expect(track).toHaveBeenCalledTimes(2)
1252
+ })
1253
+ })
1228
1254
  })
@@ -4,12 +4,12 @@ import { mergePlaylinks } from '$lib/playlink'
4
4
  describe('$lib/playlink', () => {
5
5
  describe('mergePlaylinks', () => {
6
6
  it('Should return an array with RENT and BUY categories of the same provider as one entry', () => {
7
- /** @type {import('$lib/types/playlink').PlaylinkData[]} */
7
+ /** @type {PlaylinkData[]} */
8
8
  const playlinks = [{
9
9
  sid: '1',
10
10
  name: 'Netflix',
11
11
  url: '',
12
- logo_url: 'a',
12
+ logo_url: '',
13
13
  extra_info: {
14
14
  category: 'RENT',
15
15
  },
@@ -17,7 +17,7 @@ describe('$lib/playlink', () => {
17
17
  sid: '2',
18
18
  name: 'Netflix',
19
19
  url: '',
20
- logo_url: 'a',
20
+ logo_url: '',
21
21
  extra_info: {
22
22
  category: 'BUY',
23
23
  },
@@ -28,12 +28,12 @@ describe('$lib/playlink', () => {
28
28
  })
29
29
 
30
30
  it('Should not merge playlinks if they do not share the same name', () => {
31
- /** @type {import('$lib/types/playlink').PlaylinkData[]} */
31
+ /** @type {PlaylinkData[]} */
32
32
  const playlinks = [{
33
33
  sid: '1',
34
34
  name: 'Netflix',
35
35
  url: '',
36
- logo_url: 'a',
36
+ logo_url: '',
37
37
  extra_info: {
38
38
  category: 'RENT',
39
39
  },
@@ -41,7 +41,7 @@ describe('$lib/playlink', () => {
41
41
  sid: '2',
42
42
  name: 'Apple',
43
43
  url: '',
44
- logo_url: 'a',
44
+ logo_url: '',
45
45
  extra_info: {
46
46
  category: 'BUY',
47
47
  },
@@ -51,12 +51,12 @@ describe('$lib/playlink', () => {
51
51
  })
52
52
 
53
53
  it('Should not merge playlinks if one of the categories is not RENT or BUY', () => {
54
- /** @type {import('$lib/types/playlink').PlaylinkData[]} */
54
+ /** @type {PlaylinkData[]} */
55
55
  const playlinks = [{
56
56
  sid: '1',
57
57
  name: 'Netflix',
58
58
  url: '',
59
- logo_url: 'a',
59
+ logo_url: '',
60
60
  extra_info: {
61
61
  category: 'RENT',
62
62
  },
@@ -64,7 +64,7 @@ describe('$lib/playlink', () => {
64
64
  sid: '2',
65
65
  name: 'Netflix',
66
66
  url: '',
67
- logo_url: 'a',
67
+ logo_url: '',
68
68
  extra_info: {
69
69
  category: 'SVOD',
70
70
  },
@@ -73,23 +73,8 @@ describe('$lib/playlink', () => {
73
73
  expect(mergePlaylinks(playlinks)).toHaveLength(2)
74
74
  })
75
75
 
76
- it('Should filter out playlinks without logos', () => {
77
- /** @type {import('$lib/types/playlink').PlaylinkData[]} */
78
- const playlinks = [{
79
- sid: '1',
80
- name: 'Netflix',
81
- url: '',
82
- logo_url: '',
83
- extra_info: {
84
- category: 'RENT',
85
- },
86
- }]
87
-
88
- expect(mergePlaylinks(playlinks)).toHaveLength(0)
89
- })
90
-
91
76
  it('Should return an empty array if given', () => {
92
- /** @type {import('$lib/types/playlink').PlaylinkData[]} */
77
+ /** @type {PlaylinkData[]} */
93
78
  const playlinks = []
94
79
  expect(mergePlaylinks(playlinks)).toEqual([])
95
80
  })
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi, afterEach } from 'vitest'
2
- import { getSplitTestIdentifier, getSplitTestVariantIndex, isInSplitTestVariant, trackSplitTestAction, trackSplitTestView } from '$lib/splitTest'
2
+ import { getSplitTestIdentifier, getSplitTestVariantIndex, getSplitTestVariantName, isInSplitTestVariant, trackSplitTestAction, trackSplitTestView } from '$lib/splitTest'
3
3
  import { track } from '$lib/tracking'
4
4
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
5
5
 
@@ -99,19 +99,47 @@ describe('$lib/splitTest', () => {
99
99
  it('Should track view event with the given key', () => {
100
100
  trackSplitTestView(splitTest)
101
101
 
102
- const variant = isInSplitTestVariant(splitTest) ? 1 : 0
102
+ const variant = getSplitTestVariantName(splitTest)
103
103
 
104
104
  expect(track).toHaveBeenCalledWith(TrackingEvent.SplitTestView, null, { key: 'Some key', variant })
105
105
  })
106
+
107
+ it('Should track view event with the given key and variant name if given', () => {
108
+ // @ts-ignore
109
+ window.PlayPilotLinkInjections.split_test_identifiers = { [splitTest.key]: 0.25 }
110
+
111
+ trackSplitTestView({ ...splitTest, variantNames: ['A', 'B'] })
112
+
113
+ expect(track).toHaveBeenCalledWith(TrackingEvent.SplitTestView, null, { key: 'Some key', variant: 'A' })
114
+ })
106
115
  })
107
116
 
108
117
  describe('trackSplitTestAction', () => {
109
118
  it('Should track action event with the given key and action', () => {
110
119
  trackSplitTestAction(splitTest, 'Some action')
111
- const variant = isInSplitTestVariant(splitTest) ? 1 : 0
120
+ const variant = getSplitTestVariantName(splitTest)
112
121
 
113
122
  expect(track).toHaveBeenCalledWith(TrackingEvent.SplitTestAction, null, { key: splitTest.key, variant, action: 'Some action' })
114
123
  })
124
+
125
+ it('Should track action event with the given key and action when consisting of multiple variants', () => {
126
+ const multiple = { key: 'multiple', numberOfVariants: 3 }
127
+
128
+ window.PlayPilotLinkInjections.split_test_identifiers = { [multiple.key]: 0.75 }
129
+
130
+ trackSplitTestAction(multiple, 'Some action')
131
+
132
+ expect(track).toHaveBeenCalledWith(TrackingEvent.SplitTestAction, null, { key: multiple.key, variant: '2', action: 'Some action' })
133
+ })
134
+
135
+ it('Should track action event with the given key and variant name if given', () => {
136
+ // @ts-ignore
137
+ window.PlayPilotLinkInjections.split_test_identifiers = { [splitTest.key]: 0.5 }
138
+
139
+ trackSplitTestView({ ...splitTest, variantNames: ['A', 'B'] })
140
+
141
+ expect(track).toHaveBeenCalledWith(TrackingEvent.SplitTestView, null, { key: 'Some key', variant: 'B' })
142
+ })
115
143
  })
116
144
 
117
145
  describe('getSplitTestVariantIndex', () => {