@playpilot/tpi 7.0.0-beta.6 → 7.0.0

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/eslint.config.js CHANGED
@@ -19,6 +19,7 @@ export default [
19
19
  ...globals.node,
20
20
  PlayPilotLinkInjections: 'writable',
21
21
  __SCRIPT_VERSION__: 'readonly',
22
+ __IS_EDITORIAL_SCRIPT__: 'readonly',
22
23
  },
23
24
  parserOptions: {
24
25
  parser: tsParser,
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "7.0.0-beta.6",
3
+ "version": "7.0.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
7
7
  "dev-build": "npx http-server . /build.html -p 3000",
8
- "build": "vite build --config vite._main.config.js && vite build --config vite._mount.config.js",
8
+ "build": "vite build --config vite._main.config.js && vite build --config vite._mount.config.js && vite build --config vite._mount.config.js --mode editorial",
9
9
  "preview": "vite preview",
10
10
  "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
11
11
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
@@ -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
  */
@@ -1 +1,2 @@
1
1
  declare const __SCRIPT_VERSION__: string
2
+ declare const __IS_EDITORIAL_SCRIPT__: boolean
@@ -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/lib/url.ts CHANGED
@@ -16,6 +16,5 @@ export function paramsToString(params: Record<string, any>): string {
16
16
  }
17
17
 
18
18
  export function isUrlExcludedViaConfig(config: ConfigResponse): boolean {
19
- console.log(getFullUrlPath())
20
19
  return !!(config.exclude_urls_pattern && getFullUrlPath().match(new RegExp(config.exclude_urls_pattern)))
21
20
  }
package/src/main.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { getAuthToken, isEditorialModeEnabled } from '$lib/api/auth'
1
2
  import { fetchConfig } from '$lib/api/config'
2
3
  import { pollLinkInjections } from '$lib/api/externalPages'
3
4
  import { setConsent } from '$lib/consent'
@@ -21,8 +22,10 @@ window.PlayPilotLinkInjections = {
21
22
  split_test_identifiers: {},
22
23
  evaluated_link_injections: [],
23
24
  initial_link_injections_promise: null,
25
+ time_at_initialize: 0,
24
26
  ads: [],
25
27
  require_consent: true,
28
+ no_affiliate: false,
26
29
  consents: {
27
30
  ads: false,
28
31
  pixels: false,
@@ -33,7 +36,7 @@ window.PlayPilotLinkInjections = {
33
36
  config: {},
34
37
  app: null,
35
38
 
36
- 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> {
37
40
  if (!options.token) {
38
41
  console.error('An API token is required.')
39
42
  return
@@ -50,6 +53,8 @@ window.PlayPilotLinkInjections = {
50
53
  this.organization_sid = options.organization_sid
51
54
  this.domain_sid = options.domain_sid
52
55
  this.require_consent = options.require_consent
56
+ this.no_affiliate = options.no_affiliate
57
+ this.time_at_initialize = Date.now()
53
58
 
54
59
  if (this.require_consent === false) {
55
60
  setConsent({
@@ -77,17 +82,16 @@ window.PlayPilotLinkInjections = {
77
82
  },
78
83
 
79
84
  mount(): void {
85
+ const shouldLoadEditorial = isEditorialModeEnabled() || getAuthToken()
86
+
80
87
  const script = document.createElement('script')
81
88
 
82
- script.src = `https://cdn.jsdelivr.net/npm/@playpilot/tpi@${__SCRIPT_VERSION__}/dist/mount.js`
89
+ script.src = `https://cdn.jsdelivr.net/npm/@playpilot/tpi@${__SCRIPT_VERSION__}/dist/${shouldLoadEditorial ? 'editorial.' : ''}mount.js`
83
90
  // script.src = './dist/mount.js' // Use me during development of this script
84
91
 
85
- script.onload = () => {
86
- console.log('on load')
87
- window.PlayPilotMount?.mount()
88
- }
92
+ script.onload = () => window.PlayPilotMount?.mount()
89
93
 
90
- document.body.insertAdjacentElement('beforeend', script)
94
+ if (process.env.NODE_ENV !== 'test') document.body.insertAdjacentElement('beforeend', script)
91
95
  },
92
96
  }
93
97
 
@@ -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
@@ -191,31 +193,33 @@
191
193
  </script>
192
194
 
193
195
  <div class="playpilot-link-injections" data-playpilot-link-injections>
194
- {#if !isUrlExcluded && hasAuthToken && !isEditorialMode}
195
- <EditorTrigger
196
- onclick={openEditorialMode}
197
- onclose={() => { hasAuthToken = false; removeAuthCookie() }} />
198
- {/if}
199
-
200
- {#if !isUrlExcluded && isEditorialMode && authorized}
201
- <svelte:boundary onerror={(error) => track(TrackingEvent.EditorError, null, { message: (error as Error).message })}>
202
- <Editor
203
- bind:linkInjections
204
- {pageText}
205
- {loading}
206
- onreinitialize={reinitializeEditor}
207
- injectionsEnabled={response?.injections_enabled}
208
- aiStatus={{
209
- aiEnabled: response?.ai_enabled,
210
- aiRunning: response?.ai_running && response?.ai_enabled,
211
- message: response?.ai_progress_message,
212
- percentage: response?.ai_progress_percentage,
213
- }} />
214
-
215
- {#snippet failed()}
216
- <Alert fixed>An error has occured in the PlayPilot Linkinjections editor view. We've been notified and will investigate!</Alert>
217
- {/snippet}
218
- </svelte:boundary>
196
+ {#if __IS_EDITORIAL_SCRIPT__}
197
+ {#if !isUrlExcluded && hasAuthToken && !isEditorialMode}
198
+ <EditorTrigger
199
+ onclick={openEditorialMode}
200
+ onclose={() => { hasAuthToken = false; removeAuthCookie() }} />
201
+ {/if}
202
+
203
+ {#if !isUrlExcluded && isEditorialMode && authorized}
204
+ <svelte:boundary onerror={(error) => track(TrackingEvent.EditorError, null, { message: (error as Error).message })}>
205
+ <Editor
206
+ bind:linkInjections
207
+ {pageText}
208
+ {loading}
209
+ onreinitialize={reinitializeEditor}
210
+ injectionsEnabled={response?.injections_enabled}
211
+ aiStatus={{
212
+ aiEnabled: response?.ai_enabled,
213
+ aiRunning: response?.ai_running && response?.ai_enabled,
214
+ message: response?.ai_progress_message,
215
+ percentage: response?.ai_progress_percentage,
216
+ }} />
217
+
218
+ {#snippet failed()}
219
+ <Alert fixed>An error has occured in the PlayPilot Linkinjections editor view. We've been notified and will investigate!</Alert>
220
+ {/snippet}
221
+ </svelte:boundary>
222
+ {/if}
219
223
  {/if}
220
224
 
221
225
  <Debugger onrerender={rerender} />
@@ -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,24 +1,43 @@
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'
3
8
 
4
9
  vi.mock('$lib/consent', () => ({
5
10
  setConsent: vi.fn(),
6
11
  hasConsentedTo: vi.fn(),
7
12
  }))
8
13
 
14
+ vi.mock('$lib/api/config', () => ({
15
+ fetchConfig: vi.fn(),
16
+ }))
17
+
18
+ vi.mock('$lib/api/externalPages', () => ({
19
+ pollLinkInjections: vi.fn(),
20
+ }))
21
+
22
+ vi.mock('$lib/url', () => ({
23
+ isUrlExcludedViaConfig: vi.fn(),
24
+ }))
25
+
9
26
  describe('main.ts', () => {
10
- beforeEach(() => {
27
+ beforeEach(async () => {
28
+ fakeFetch()
29
+
11
30
  vi.resetModules()
12
31
  vi.resetAllMocks()
32
+
33
+ await import('../main')
13
34
  })
14
35
 
15
36
  afterAll(() => {
16
37
  vi.resetModules()
17
38
  })
18
39
 
19
- it('Should not call setConsent when only a token is passed', async () => {
20
- await import('../main')
21
-
40
+ it('Should not call setConsent when only a token is passed', () => {
22
41
  window.PlayPilotLinkInjections.initialize({ token: 'a' })
23
42
 
24
43
  expect(setConsent).not.toHaveBeenCalled()
@@ -32,9 +51,7 @@ describe('main.ts', () => {
32
51
  })
33
52
  })
34
53
 
35
- it('Should call setConsent when require_consent is false', async () => {
36
- await import('../main')
37
-
54
+ it('Should call setConsent when require_consent is false', () => {
38
55
  window.PlayPilotLinkInjections.initialize({ token: 'a', require_consent: false })
39
56
 
40
57
  expect(setConsent).toHaveBeenCalledWith({
@@ -45,4 +62,40 @@ describe('main.ts', () => {
45
62
  affiliate: true,
46
63
  })
47
64
  })
65
+
66
+ it('Should call config object and set the result to the window object', async () => {
67
+ vi.mocked(fetchConfig).mockResolvedValue({ html_selector: 'some_value' })
68
+
69
+ window.PlayPilotLinkInjections.initialize({ token: 'a' })
70
+
71
+ expect(fetchConfig).toHaveBeenCalled()
72
+
73
+ await waitFor(() => {
74
+ expect(window.PlayPilotLinkInjections.config).toEqual({ html_selector: 'some_value' })
75
+ })
76
+ })
77
+
78
+ it('Should not call pollLinkInjections if config object excludes page', async () => {
79
+ vi.mocked(isUrlExcludedViaConfig).mockReturnValue(true)
80
+
81
+ window.PlayPilotLinkInjections.initialize({ token: 'a' })
82
+
83
+ expect(fetchConfig).toHaveBeenCalled()
84
+
85
+ await new Promise(res => setTimeout(res))
86
+
87
+ expect(pollLinkInjections).not.toHaveBeenCalled()
88
+ })
89
+
90
+ it('Should call pollLinkInjections if config object does not page', async () => {
91
+ vi.mocked(isUrlExcludedViaConfig).mockReturnValue(false)
92
+
93
+ window.PlayPilotLinkInjections.initialize({ token: 'a' })
94
+
95
+ expect(fetchConfig).toHaveBeenCalled()
96
+
97
+ await new Promise(res => setTimeout(res))
98
+
99
+ expect(pollLinkInjections).toHaveBeenCalled()
100
+ })
48
101
  })
@@ -111,6 +111,18 @@ describe('$routes/+page.svelte', () => {
111
111
  await waitFor(() => expect(pollLinkInjections).toHaveBeenCalled())
112
112
  })
113
113
 
114
+ it('Should not call pollLinkInjections and fetchConfig on mount if both are already present in window object', async () => {
115
+ window.PlayPilotLinkInjections.config = {}
116
+ window.PlayPilotLinkInjections.initial_link_injections_promise = new Promise(res => res)
117
+
118
+ render(page)
119
+
120
+ await waitFor(() => new Promise(res => setTimeout(res, 100)))
121
+
122
+ expect(fetchConfig).not.toHaveBeenCalled()
123
+ expect(pollLinkInjections).not.toHaveBeenCalled()
124
+ })
125
+
114
126
  it('Should call pollLinkInjections again if ai_running is true while in editorial mode', async () => {
115
127
  vi.mocked(authorize).mockResolvedValueOnce(true)
116
128
  // @ts-ignore
@@ -463,6 +475,19 @@ describe('$routes/+page.svelte', () => {
463
475
  expect(fetchAds).not.toHaveBeenCalled()
464
476
  })
465
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
+
466
491
  describe('Config', () => {
467
492
  describe('exclude_urls_pattern', () => {
468
493
  it('Should not inject if config exclude_urls_pattern matches current url', async () => {
@@ -621,5 +646,25 @@ describe('$routes/+page.svelte', () => {
621
646
  expect(document.querySelector('#playpilot-custom-style')).not.toBeTruthy()
622
647
  })
623
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
+ })
624
669
  })
625
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
  })
@@ -7,35 +7,40 @@ import packageJson from './package.json'
7
7
 
8
8
  dotenv.config({ path: '.env' })
9
9
 
10
- export default defineConfig(() => ({
11
- plugins: [
12
- svelte(),
13
- cssInjectedByJsPlugin(),
14
- injectEnvVariables,
15
- ],
10
+ export default defineConfig(({ mode }) => {
11
+ const isEditiorial = mode === 'editorial'
16
12
 
17
- build: {
18
- emptyOutDir: false,
19
- rollupOptions: {
20
- input: './src/mount.ts',
21
- output: {
22
- format: 'iife',
23
- name: 'PlayPilotMount',
24
- entryFileNames: 'mount.js',
13
+ return {
14
+ plugins: [
15
+ svelte(),
16
+ cssInjectedByJsPlugin(),
17
+ injectEnvVariables,
18
+ ],
19
+
20
+ build: {
21
+ emptyOutDir: false,
22
+ rollupOptions: {
23
+ input: './src/mount.ts',
24
+ output: {
25
+ format: 'iife',
26
+ name: 'PlayPilotMount',
27
+ entryFileNames: isEditiorial ? 'editorial.mount.js' : 'mount.js',
28
+ },
25
29
  },
26
30
  },
27
- },
28
31
 
29
- resolve: {
30
- alias: {
31
- '$lib': path.resolve(__dirname, './src/lib'),
32
+ resolve: {
33
+ alias: {
34
+ '$lib': path.resolve(__dirname, './src/lib'),
35
+ },
32
36
  },
33
- },
34
37
 
35
- define: {
36
- __SCRIPT_VERSION__: JSON.stringify(packageJson.version),
37
- },
38
- }))
38
+ define: {
39
+ __SCRIPT_VERSION__: JSON.stringify(packageJson.version),
40
+ __IS_EDITORIAL_SCRIPT__: JSON.stringify(isEditiorial),
41
+ },
42
+ }
43
+ })
39
44
 
40
45
  const injectEnvVariables = {
41
46
  name: 'resolve-env-variables',