@playpilot/tpi 7.0.0-beta.7 → 7.0.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "7.0.0-beta.7",
3
+ "version": "7.0.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -30,6 +30,7 @@ export async function fetchAds(): Promise<Campaign[]> {
30
30
  */
31
31
  export function getFirstAdOfType(format: CampaignFormat): Campaign | null {
32
32
  if (!hasConsentedTo('ads')) return null
33
+ if (window.PlayPilotLinkInjections.no_affiliate) return null
33
34
 
34
35
  return (window.PlayPilotLinkInjections?.ads || []).find(i => i.campaign_format === format) || null
35
36
  }
@@ -5,7 +5,7 @@ const cookieName = 'EncryptedToken'
5
5
  const urlParam = 'articleReplacementEditToken'
6
6
  const editorialParam = 'playpilot-editorial-mode'
7
7
 
8
- export async function authorize({ href, throwError }: { href?: string, throwError?: boolean } = { href: window.location.href, throwError: false }): Promise<boolean> {
8
+ export async function authorize({ href = window.location.href, throwError = false }: { href?: string, throwError?: boolean } = {}): Promise<boolean> {
9
9
  try {
10
10
  const apiToken = getApiToken()
11
11
  if (!apiToken) throw new Error('No token was provided')
@@ -37,6 +37,7 @@ export async function track(event: string, title: TitleData | null = null, paylo
37
37
  }
38
38
 
39
39
  payload.version = __SCRIPT_VERSION__
40
+ payload.time_since_initialize = window.PlayPilotLinkInjections?.time_at_initialize ? Date.now() - window.PlayPilotLinkInjections.time_at_initialize : 0
40
41
  payload.url = getFullUrlPath()
41
42
  payload.organization_sid = window.PlayPilotLinkInjections?.organization_sid || 'undefined'
42
43
  payload.domain_sid = window.PlayPilotLinkInjections?.domain_sid || 'undefined'
@@ -44,6 +44,12 @@ export type ConfigResponse = {
44
44
  */
45
45
  playlinks_disclaimer_text?: string
46
46
 
47
+ /**
48
+ * Some partners may want to display titles without affiliate links, which currently comes down to no playlinks.
49
+ * This can also be set via the config on the script itself.
50
+ */
51
+ no_affiliate?: boolean
52
+
47
53
  /**
48
54
  * Whether or not we can insert retargeting pixels. Not all third parties allow this and it is disabled by default
49
55
  */
@@ -34,6 +34,8 @@ export type ScriptConfig = {
34
34
  initial_link_injections_promise?: Promise<LinkInjectionResponse> | null
35
35
  // By default the script requires consent from the user through tcfapi. Can be disabled using this setting.
36
36
  require_consent?: boolean
37
+ // Some partners may want to display titles without affiliate links, which currently comes down to no playlinks.
38
+ no_affiliate?: boolean
37
39
  // Used to check if a user has consented via tcfapi to various consent categories.
38
40
  consents?: ConsentOptions
39
41
  // The config response from the api, saved to the window object to be used without having to pass the response around.
@@ -42,4 +44,6 @@ export type ScriptConfig = {
42
44
  ads?: Campaign[]
43
45
  // The region the user is in, either fetched from an external service or based on the default region in the config object
44
46
  region?: string | null
47
+ // The time at which the script was initialized
48
+ time_at_initialize?: number
45
49
  }
package/src/main.ts CHANGED
@@ -22,8 +22,10 @@ window.PlayPilotLinkInjections = {
22
22
  split_test_identifiers: {},
23
23
  evaluated_link_injections: [],
24
24
  initial_link_injections_promise: null,
25
+ time_at_initialize: 0,
25
26
  ads: [],
26
27
  require_consent: true,
28
+ no_affiliate: false,
27
29
  consents: {
28
30
  ads: false,
29
31
  pixels: false,
@@ -34,7 +36,7 @@ window.PlayPilotLinkInjections = {
34
36
  config: {},
35
37
  app: null,
36
38
 
37
- async initialize(options = { token: '', selector: '', after_article_selector: '', after_article_insert_position: '', language: null, region: null, organization_sid: null, domain_sid: null, editorial_token: '', require_consent: true }): Promise<void> {
39
+ async initialize(options = { token: '', selector: '', after_article_selector: '', after_article_insert_position: '', language: null, region: null, organization_sid: null, domain_sid: null, editorial_token: '', require_consent: true, no_affiliate: false }): Promise<void> {
38
40
  if (!options.token) {
39
41
  console.error('An API token is required.')
40
42
  return
@@ -51,6 +53,8 @@ window.PlayPilotLinkInjections = {
51
53
  this.organization_sid = options.organization_sid
52
54
  this.domain_sid = options.domain_sid
53
55
  this.require_consent = options.require_consent
56
+ this.no_affiliate = options.no_affiliate
57
+ this.time_at_initialize = Date.now()
54
58
 
55
59
  if (this.require_consent === false) {
56
60
  setConsent({
@@ -82,12 +86,13 @@ window.PlayPilotLinkInjections = {
82
86
 
83
87
  const script = document.createElement('script')
84
88
 
89
+ script.id = 'playpilot-mount'
85
90
  script.src = `https://cdn.jsdelivr.net/npm/@playpilot/tpi@${__SCRIPT_VERSION__}/dist/${shouldLoadEditorial ? 'editorial.' : ''}mount.js`
86
91
  // script.src = './dist/mount.js' // Use me during development of this script
87
92
 
88
93
  script.onload = () => window.PlayPilotMount?.mount()
89
94
 
90
- document.body.insertAdjacentElement('beforeend', script)
95
+ document.body.appendChild(script)
91
96
  },
92
97
  }
93
98
 
@@ -58,6 +58,7 @@
58
58
  track(TrackingEvent.ArticlePageView)
59
59
 
60
60
  if (!aiInjections.length && !manualInjections.length) return
61
+ if (window.PlayPilotLinkInjections.no_affiliate) return
61
62
 
62
63
  window.PlayPilotLinkInjections.ads = await fetchAds()
63
64
  }
@@ -77,6 +78,7 @@
77
78
 
78
79
  if (config?.custom_style) insertCustomStyle(config.custom_style || '')
79
80
  if (config?.explore_navigation_selector) insertExploreIntoNavigation()
81
+ if (config?.no_affiliate) window.PlayPilotLinkInjections.no_affiliate = true
80
82
 
81
83
  isUrlExcluded = isUrlExcludedViaConfig(config)
82
84
  if (isUrlExcluded) return
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { SplitTest } from '$lib/enums/SplitTest'
3
+ import { getPageTextAndElements } from '$lib/injectionElements'
3
4
  import { getFullUrlPath } from '$lib/url'
4
5
  import { onDestroy } from 'svelte'
5
6
 
@@ -160,6 +161,11 @@
160
161
 
161
162
  <hr />
162
163
 
164
+ <button onclick={() => console.log(getPageTextAndElements(window.PlayPilotLinkInjections.config))}>Output elements in console</button>
165
+ <button onclick={() => console.log(getPageTextAndElements(window.PlayPilotLinkInjections.config).pageText)}>Output text in console</button>
166
+
167
+ <hr />
168
+
163
169
  <button onclick={onrerender}>Re-inject</button>
164
170
 
165
171
  {#if isUsingBetaScript}
@@ -1,12 +1,9 @@
1
1
  <script lang="ts">
2
2
  import IconArrow from '../Icons/IconArrow.svelte'
3
- import ImageTitlesList from '$lib/images/titles-list.webp'
4
3
  import { openModal } from '$lib/modal'
5
4
  </script>
6
5
 
7
6
  <button class="call-to-action" onclick={() => openModal({ type: 'explore' })}>
8
- <img src={ImageTitlesList} alt="" width="70" height="57" />
9
-
10
7
  <div>
11
8
  <strong>Looking for something else?</strong>
12
9
  <div>Use our streamingguide</div>
@@ -11,6 +11,8 @@
11
11
  }
12
12
 
13
13
  const { title, onclick = () => null }: Props = $props()
14
+
15
+ const noAffiliate = !!window.PlayPilotLinkInjections?.no_affiliate
14
16
  </script>
15
17
 
16
18
  <button class="title" {onclick} data-testid="title">
@@ -35,11 +37,13 @@
35
37
  {/if}
36
38
  </div>
37
39
 
38
- <div class="description">
40
+ <div class="description" class:large={noAffiliate}>
39
41
  {title.description}
40
42
  </div>
41
43
 
42
- <PlaylinksCompact playlinks={title.providers} />
44
+ {#if !noAffiliate}
45
+ <PlaylinksCompact playlinks={title.providers} />
46
+ {/if}
43
47
  </div>
44
48
 
45
49
  <div class="action">
@@ -129,6 +133,11 @@
129
133
  -webkit-box-orient: vertical;
130
134
  font-size: theme(detail-font-size-small, font-size-small);
131
135
  color: theme(list-item-description-text-color, text-color);
136
+
137
+ &.large {
138
+ line-clamp: 2;
139
+ -webkit-line-clamp: 2;
140
+ }
132
141
  }
133
142
 
134
143
  .action {
@@ -24,9 +24,11 @@
24
24
 
25
25
  const { title, small = false }: Props = $props()
26
26
 
27
+ const noAffiliate = !!window.PlayPilotLinkInjections?.no_affiliate
28
+
27
29
  setContext('title', title)
28
30
 
29
- const showDescription = $derived(!small && title.description)
31
+ const showDescription = $derived((!small || noAffiliate) && title.description)
30
32
 
31
33
  let posterLoaded = $state(false)
32
34
  let backgroundLoaded = $state(false)
@@ -71,7 +73,9 @@
71
73
  <Description text={title.description!} blurb={title.blurb} onclick={() => track(TrackingEvent.ExpandTitleDescription, title)} />
72
74
  {/if}
73
75
 
74
- <Playlinks playlinks={title.providers} {title} />
76
+ {#if !noAffiliate}
77
+ <Playlinks playlinks={title.providers} {title} />
78
+ {/if}
75
79
 
76
80
  <ParticipantsRail {title} />
77
81
  <SimilarRail {title} />
@@ -81,7 +81,7 @@ describe('$lib/api/ads', () => {
81
81
  expect(getFirstAdOfType('top_scroll')).toBe(null)
82
82
  })
83
83
 
84
- it('Should not return ads if valid ad was given but user did not consent to ads', async () => {
84
+ it('Should not return ads if valid ad was given but user did not consent to ads', () => {
85
85
  vi.mocked(hasConsentedTo).mockImplementation(() => false)
86
86
 
87
87
  // @ts-ignore
@@ -89,5 +89,12 @@ describe('$lib/api/ads', () => {
89
89
 
90
90
  expect(getFirstAdOfType('top_scroll')).toBe(null)
91
91
  })
92
+
93
+ it('Should not return ads even if given if no_affiliate is true', () => {
94
+ // @ts-ignore
95
+ window.PlayPilotLinkInjections = { ads: [{ campaign_format: 'top_scroll' }], no_affiliate: true }
96
+
97
+ expect(getFirstAdOfType('top_scroll')).toBe(null)
98
+ })
92
99
  })
93
100
  })
@@ -21,6 +21,7 @@ describe('$lib/tracking', () => {
21
21
  window.PlayPilotLinkInjections = {}
22
22
 
23
23
  vi.resetAllMocks()
24
+ vi.useRealTimers()
24
25
 
25
26
  fakeFetch()
26
27
  vi.mocked(hasConsentedTo).mockImplementation(() => true)
@@ -72,7 +73,7 @@ describe('$lib/tracking', () => {
72
73
  expect(global.fetch).toHaveBeenCalledWith(
73
74
  expect.any(String),
74
75
  expect.objectContaining({
75
- // eslint-disable-next-line no-undef
76
+
76
77
  body: expect.stringContaining(`"version":"${__SCRIPT_VERSION__}"`),
77
78
  }),
78
79
  )
@@ -175,6 +176,36 @@ describe('$lib/tracking', () => {
175
176
 
176
177
  expect(global.fetch).not.toHaveBeenCalled()
177
178
  })
179
+
180
+ it('Should include time_since_initialize', async () => {
181
+ vi.useFakeTimers()
182
+
183
+ window.PlayPilotLinkInjections.time_at_initialize = Date.now()
184
+
185
+ vi.advanceTimersByTime(150)
186
+
187
+ track('Some Event')
188
+ expect(global.fetch).toHaveBeenCalledWith(
189
+ expect.any(String),
190
+ expect.objectContaining({
191
+ body: expect.stringContaining('"time_since_initialize":150'),
192
+ }),
193
+ )
194
+ })
195
+
196
+ it('Should include time_since_initialize set to 0 if window object was not present', async () => {
197
+ vi.useFakeTimers()
198
+
199
+ vi.advanceTimersByTime(150)
200
+
201
+ track('Some Event')
202
+ expect(global.fetch).toHaveBeenCalledWith(
203
+ expect.any(String),
204
+ expect.objectContaining({
205
+ body: expect.stringContaining('"time_since_initialize":0'),
206
+ }),
207
+ )
208
+ })
178
209
  })
179
210
 
180
211
  describe('setTrackingSids', () => {
@@ -1,48 +1,143 @@
1
1
  import { setConsent } from '$lib/consent'
2
2
  import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'
3
+ import { fakeFetch } from './helpers'
4
+ import { fetchConfig } from '$lib/api/config'
5
+ import { waitFor } from '@testing-library/svelte'
6
+ import { isUrlExcludedViaConfig } from '$lib/url'
7
+ import { pollLinkInjections } from '$lib/api/externalPages'
8
+ import { getAuthToken, isEditorialModeEnabled } from '$lib/api/auth'
3
9
 
4
10
  vi.mock('$lib/consent', () => ({
5
11
  setConsent: vi.fn(),
6
12
  hasConsentedTo: vi.fn(),
7
13
  }))
8
14
 
15
+ vi.mock('$lib/api/config', () => ({
16
+ fetchConfig: vi.fn(),
17
+ }))
18
+
19
+ vi.mock('$lib/api/externalPages', () => ({
20
+ pollLinkInjections: vi.fn(),
21
+ }))
22
+
23
+ vi.mock('$lib/url', () => ({
24
+ isUrlExcludedViaConfig: vi.fn(),
25
+ }))
26
+
27
+ vi.mock('$lib/api/auth', () => ({
28
+ getAuthToken: vi.fn(),
29
+ isEditorialModeEnabled: vi.fn(),
30
+ }))
31
+
9
32
  describe('main.ts', () => {
10
- beforeEach(() => {
33
+ beforeEach(async () => {
34
+ document.body.innerHTML = ''
35
+
36
+ fakeFetch()
37
+
11
38
  vi.resetModules()
12
39
  vi.resetAllMocks()
40
+
41
+ await import('../main')
13
42
  })
14
43
 
15
44
  afterAll(() => {
16
45
  vi.resetModules()
17
46
  })
18
47
 
19
- it('Should not call setConsent when only a token is passed', async () => {
20
- await import('../main')
48
+ describe('initialize', () => {
49
+ it('Should not call setConsent when only a token is passed', () => {
50
+ window.PlayPilotLinkInjections.initialize({ token: 'a' })
51
+
52
+ expect(setConsent).not.toHaveBeenCalled()
53
+
54
+ expect(window.PlayPilotLinkInjections.consents).toEqual({
55
+ ads: false,
56
+ pixels: false,
57
+ tracking: false,
58
+ split_tests: false,
59
+ affiliate: false,
60
+ })
61
+ })
21
62
 
22
- window.PlayPilotLinkInjections.initialize({ token: 'a' })
63
+ it('Should call setConsent when require_consent is false', () => {
64
+ window.PlayPilotLinkInjections.initialize({ token: 'a', require_consent: false })
23
65
 
24
- expect(setConsent).not.toHaveBeenCalled()
66
+ expect(setConsent).toHaveBeenCalledWith({
67
+ ads: true,
68
+ pixels: true,
69
+ tracking: true,
70
+ split_tests: true,
71
+ affiliate: true,
72
+ })
73
+ })
74
+
75
+ it('Should call config object and set the result to the window object', async () => {
76
+ vi.mocked(fetchConfig).mockResolvedValue({ html_selector: 'some_value' })
77
+
78
+ window.PlayPilotLinkInjections.initialize({ token: 'a' })
25
79
 
26
- expect(window.PlayPilotLinkInjections.consents).toEqual({
27
- ads: false,
28
- pixels: false,
29
- tracking: false,
30
- split_tests: false,
31
- affiliate: false,
80
+ expect(fetchConfig).toHaveBeenCalled()
81
+
82
+ await waitFor(() => {
83
+ expect(window.PlayPilotLinkInjections.config).toEqual({ html_selector: 'some_value' })
84
+ })
85
+ })
86
+
87
+ it('Should not call pollLinkInjections if config object excludes page', async () => {
88
+ vi.mocked(isUrlExcludedViaConfig).mockReturnValue(true)
89
+
90
+ window.PlayPilotLinkInjections.initialize({ token: 'a' })
91
+
92
+ expect(fetchConfig).toHaveBeenCalled()
93
+
94
+ await new Promise(res => setTimeout(res))
95
+
96
+ expect(pollLinkInjections).not.toHaveBeenCalled()
97
+ })
98
+
99
+ it('Should call pollLinkInjections if config object does not page', async () => {
100
+ vi.mocked(isUrlExcludedViaConfig).mockReturnValue(false)
101
+
102
+ window.PlayPilotLinkInjections.initialize({ token: 'a' })
103
+
104
+ expect(fetchConfig).toHaveBeenCalled()
105
+
106
+ await new Promise(res => setTimeout(res))
107
+
108
+ expect(pollLinkInjections).toHaveBeenCalled()
32
109
  })
33
110
  })
34
111
 
35
- it('Should call setConsent when require_consent is false', async () => {
36
- await import('../main')
112
+ describe('mount', () => {
113
+ it('Should insert script tag', () => {
114
+ window.PlayPilotLinkInjections.mount()
115
+
116
+ expect(document.querySelector('script#playpilot-mount')).toBeTruthy()
117
+ })
118
+
119
+ it('Should insert script tag with editorial mode if editiorial mode is enabled', () => {
120
+ vi.mocked(isEditorialModeEnabled).mockReturnValueOnce(true)
121
+
122
+ window.PlayPilotLinkInjections.mount()
123
+ expect(/** @type {HTMLScriptElement} */(document.querySelector('script#playpilot-mount')).src).toContain('editorial.mount.js')
124
+ })
125
+
126
+ it('Should insert script tag with editorial mode if editiorial mode is disabled but auth token is present', () => {
127
+ vi.mocked(isEditorialModeEnabled).mockReturnValueOnce(false)
128
+ vi.mocked(getAuthToken).mockReturnValueOnce('abc')
129
+
130
+ window.PlayPilotLinkInjections.mount()
131
+ expect(/** @type {HTMLScriptElement} */(document.querySelector('script#playpilot-mount')).src).toContain('editorial.mount.js')
132
+ })
37
133
 
38
- window.PlayPilotLinkInjections.initialize({ token: 'a', require_consent: false })
134
+ it('Should insert script tag without editorial mode if editiorial mode is disabled but auth token is present', () => {
135
+ vi.mocked(isEditorialModeEnabled).mockReturnValueOnce(false)
136
+ vi.mocked(getAuthToken).mockReturnValueOnce('')
39
137
 
40
- expect(setConsent).toHaveBeenCalledWith({
41
- ads: true,
42
- pixels: true,
43
- tracking: true,
44
- split_tests: true,
45
- affiliate: true,
138
+ window.PlayPilotLinkInjections.mount()
139
+ expect(/** @type {HTMLScriptElement} */(document.querySelector('script#playpilot-mount')).src).not.toContain('editorial.mount.js')
140
+ expect(/** @type {HTMLScriptElement} */(document.querySelector('script#playpilot-mount')).src).toContain('mount.js')
46
141
  })
47
142
  })
48
143
  })
@@ -0,0 +1,63 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { fakeFetch } from './helpers'
3
+ import { clearLinkInjections } from '$lib/injection'
4
+ import { mount } from 'svelte'
5
+
6
+ vi.mock('$lib/injection', () => ({
7
+ clearLinkInjections: vi.fn(),
8
+ }))
9
+
10
+ vi.mock('svelte', () => ({
11
+ mount: vi.fn(),
12
+ }))
13
+
14
+ describe('mount.ts', () => {
15
+ beforeEach(async () => {
16
+ // @ts-ignore
17
+ window.PlayPilotLinkInjections = {}
18
+ document.body.innerHTML = ''
19
+
20
+ fakeFetch()
21
+
22
+ vi.resetModules()
23
+ vi.resetAllMocks()
24
+
25
+ await import('../mount')
26
+ })
27
+
28
+ describe('mount', () => {
29
+ it('Should mount app', () => {
30
+ window.PlayPilotMount.mount()
31
+
32
+ expect(mount).toHaveBeenCalledWith(expect.any(Function), { target: expect.any(HTMLElement) })
33
+ })
34
+
35
+ it('Should assign app to window PlayPilotLinkInjections object', () => {
36
+ vi.mocked(mount).mockReturnValueOnce({ some_key: 'Some value' })
37
+
38
+ window.PlayPilotMount.mount()
39
+
40
+ expect(window.PlayPilotLinkInjections.app).toEqual({ some_key: 'Some value' })
41
+ })
42
+ })
43
+
44
+ describe('destroy', () => {
45
+ it('Should call clearLinkInjections and remove link injections element', () => {
46
+ document.body.innerHTML = '<div id="playpilot-link-injection"></div>'
47
+
48
+ window.PlayPilotMount.destroy()
49
+
50
+ expect(document.body.innerHTML).toBe('')
51
+ expect(clearLinkInjections).toHaveBeenCalled()
52
+ })
53
+
54
+ it('Should clear app on window PlayPilotLinkInjections object', () => {
55
+ // @ts-ignore
56
+ window.PlayPilotLinkInjections = { app: {} }
57
+
58
+ window.PlayPilotMount.destroy()
59
+
60
+ expect(window.PlayPilotLinkInjections.app).toBe(null)
61
+ })
62
+ })
63
+ })
@@ -475,6 +475,19 @@ describe('$routes/+page.svelte', () => {
475
475
  expect(fetchAds).not.toHaveBeenCalled()
476
476
  })
477
477
 
478
+ it('Should not fetch ads if no_affilite is true', async () => {
479
+ // @ts-ignore
480
+ vi.mocked(pollLinkInjections).mockResolvedValueOnce({ ai_injections: [{}], manual_injections: [{}] })
481
+ // @ts-ignore
482
+ window.PlayPilotLinkInjections = { no_affiliate: true }
483
+
484
+ render(page)
485
+
486
+ await new Promise(res => setTimeout(res, 100)) // Await possible fetches
487
+
488
+ expect(fetchAds).not.toHaveBeenCalled()
489
+ })
490
+
478
491
  describe('Config', () => {
479
492
  describe('exclude_urls_pattern', () => {
480
493
  it('Should not inject if config exclude_urls_pattern matches current url', async () => {
@@ -633,5 +646,25 @@ describe('$routes/+page.svelte', () => {
633
646
  expect(document.querySelector('#playpilot-custom-style')).not.toBeTruthy()
634
647
  })
635
648
  })
649
+
650
+ describe('no_affiliate', () => {
651
+ it('Should not set window object if value is not given', async () => {
652
+ vi.mocked(fetchConfig).mockResolvedValueOnce({})
653
+
654
+ render(page)
655
+
656
+ await new Promise(res => setTimeout(res, 100)) // Await all fetches
657
+ expect(window.PlayPilotLinkInjections.no_affiliate).not.toBe(true)
658
+ })
659
+
660
+ it('Should set window object if value is true', async () => {
661
+ vi.mocked(fetchConfig).mockResolvedValueOnce({ no_affiliate: true })
662
+
663
+ render(page)
664
+
665
+ await new Promise(res => setTimeout(res, 100)) // Await all fetches
666
+ expect(window.PlayPilotLinkInjections.no_affiliate).toBe(true)
667
+ })
668
+ })
636
669
  })
637
670
  })
@@ -15,6 +15,8 @@ vi.mock('svelte', async (importActual) => ({
15
15
 
16
16
  describe('ListTitle.svelte', () => {
17
17
  beforeEach(() => {
18
+ // @ts-ignore
19
+ window.PlayPilotLinkInjections = {}
18
20
  vi.resetAllMocks()
19
21
  })
20
22
 
@@ -80,5 +82,14 @@ describe('ListTitle.svelte', () => {
80
82
 
81
83
  expect(onclick).not.toHaveBeenCalled()
82
84
  })
85
+
86
+ it('Should not render playlinks if no_affiliate is given in window object', () => {
87
+ window.PlayPilotLinkInjections.no_affiliate = true
88
+
89
+ // @ts-ignore
90
+ const { container } = render(ListTitle, { title: { ...title, providers: playlinks } })
91
+
92
+ expect(container.querySelectorAll('.playlink')).toHaveLength(0)
93
+ })
83
94
  })
84
95
  })
@@ -26,6 +26,8 @@ vi.mock('svelte', async (importActual) => ({
26
26
 
27
27
  describe('Title.svelte', () => {
28
28
  beforeEach(() => {
29
+ // @ts-ignore
30
+ window.PlayPilotLinkInjections = {}
29
31
  vi.resetAllMocks()
30
32
  })
31
33
 
@@ -104,4 +106,26 @@ describe('Title.svelte', () => {
104
106
 
105
107
  expect(queryByText('Watch trailer')).not.toBeTruthy()
106
108
  })
109
+
110
+ it('Should render playlinks by default', () => {
111
+ const { getByText } = render(Title, { title })
112
+
113
+ expect(getByText('Where to stream online')).toBeTruthy()
114
+ })
115
+
116
+ it('Should not render playlinks if no_affiliate is true on config', () => {
117
+ window.PlayPilotLinkInjections.no_affiliate = true
118
+
119
+ const { queryByText } = render(Title, { title })
120
+
121
+ expect(queryByText('Where to stream online')).not.toBeTruthy()
122
+ })
123
+
124
+ it('Should render description if no_affiliate is true even if small is true', () => {
125
+ window.PlayPilotLinkInjections.no_affiliate = true
126
+
127
+ const { getByText } = render(Title, { title: { ...title, description: 'Some description' }, small: true })
128
+
129
+ expect(getByText('Some description')).toBeTruthy()
130
+ })
107
131
  })