@playpilot/tpi 6.1.1 → 6.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "6.1.1",
3
+ "version": "6.2.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -19,6 +19,7 @@
19
19
  "@sveltejs/kit": "^2.0.0",
20
20
  "@sveltejs/vite-plugin-svelte": "^4.0.0",
21
21
  "@testing-library/svelte": "^5.2.6",
22
+ "@types/node": "^25.2.0",
22
23
  "@typescript-eslint/eslint-plugin": "^8.32.1",
23
24
  "@typescript-eslint/parser": "^8.32.1",
24
25
  "eslint": "^9.27.0",
@@ -33,12 +33,17 @@ export type ConfigResponse = {
33
33
  */
34
34
  disable_public_injections?: boolean
35
35
 
36
- /*
36
+ /**
37
37
  * Region to fall back on if other methods of getting the region fail. This should be equal to the region most relevant
38
38
  * to the partner. Should be lowercase.
39
39
  */
40
40
  fallback_region?: string
41
41
 
42
+ /**
43
+ * Disclaimer text that is shown above playlinks in title cards used to replace the default text
44
+ */
45
+ playlinks_disclaimer_text?: string
46
+
42
47
  /**
43
48
  * The following options are all relevant for in text disclaimers, which renders as a disclaimer text within the article,
44
49
  * rather than only inside of title cards.
@@ -25,7 +25,7 @@
25
25
 
26
26
  <div>
27
27
  {#if !aiEnabled}
28
- <strong>AI processing is disabled.</strong> Enable AI from the <a href="https://partner.playpilot.net">Partner Portal</a>
28
+ <strong>AI processing is disabled.</strong>
29
29
  {:else if aiRunning}
30
30
  <strong>AI links are currently processing.</strong> This can take several minutes. We'll insert all found injections once ready.
31
31
 
@@ -70,10 +70,6 @@
70
70
  {/if}
71
71
 
72
72
  <style lang="scss">
73
- a {
74
- color: currentColor;
75
- }
76
-
77
73
  p {
78
74
  margin: 0;
79
75
  }
@@ -22,6 +22,7 @@
22
22
  background: theme(error-dark);
23
23
  font-size: margin(0.75);
24
24
  font-family: theme(font-family);
25
+ color: white;
25
26
 
26
27
  &.warning {
27
28
  border-color: theme(warning);
@@ -1,6 +1,5 @@
1
1
  <script lang="ts">
2
2
  import { heading } from '$lib/actions/heading'
3
- import { searchTitles } from '$lib/api/search'
4
3
  import { fetchTitles } from '$lib/api/titles'
5
4
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
6
5
  import { exploreParentSelector } from '$lib/explore'
@@ -52,40 +51,28 @@
52
51
  latestRequestId += 1
53
52
  const requestId = latestRequestId
54
53
 
55
- let response: APIPaginatedResult<TitleData>
54
+ const params: Record<string, string | number> = { page_size: 24, page }
56
55
 
57
- // If a search query is given we use searchTitles with just the query.
58
- // If not, we use fetchTitles with the given params for the filter.
59
- // This is because the backend does not support filters for search results yet.
60
- // In the future we will likely merge these two, either by adding filters in the backend
61
- // or by filtering results in the frontend (this seems like the less good option).
62
- if (searchQuery) {
63
- const results: TitleData[] = await searchTitles(searchQuery)
64
- response = { results, next: null, previous: null }
56
+ if (searchQuery) params.search = searchQuery
65
57
 
66
- if (requestId === latestRequestId) titles = results
67
- } else {
68
- const params: Record<string, string | number> = { page_size: 24, page }
58
+ Object.entries(filter).forEach(([key, { type, value }]) => {
59
+ if (type === 'string') {
60
+ params[key] = value as string
61
+ } else if (type === 'array') {
62
+ params[key] = (value as string[]).join(',')
63
+ } else if (type === 'range') {
64
+ const [min, max] = value as number[]
69
65
 
70
- Object.entries(filter).forEach(([key, { type, value }]) => {
71
- if (type === 'string') {
72
- params[key] = value as string
73
- } else if (type === 'array') {
74
- params[key] = (value as string[]).join(',')
75
- } else if (type === 'range') {
76
- const [min, max] = value as number[]
66
+ params[key + '_min'] = min
67
+ params[key + '_max'] = max
68
+ }
69
+ })
77
70
 
78
- params[key + '_min'] = min
79
- params[key + '_max'] = max
80
- }
81
- })
71
+ const response = await fetchTitles(params)
82
72
 
83
- response = await fetchTitles(params)
73
+ if (!response?.results) throw new Error('Something went wrong when fetching titles in Explore')
84
74
 
85
- if (!response?.results) throw new Error('Something went wrong when fetching titles in Explore')
86
-
87
- if (requestId === latestRequestId) titles = [...titles, ...response.results]
88
- }
75
+ if (requestId === latestRequestId) titles = [...titles, ...response.results]
89
76
 
90
77
  return response
91
78
  }
@@ -38,7 +38,7 @@
38
38
 
39
39
  {#if playlinks.length}
40
40
  <div class="disclaimer" data-testid="commission-disclaimer">
41
- {t('Commission Disclaimer')}
41
+ {window?.PlayPilotLinkInjections?.config?.playlinks_disclaimer_text || t('Commission Disclaimer')}
42
42
  <a href="https://playpilot.com/" target="_blank" rel="sponsored">PlayPilot.com</a>
43
43
  </div>
44
44
  {/if}
@@ -4,7 +4,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
4
4
  import Explore from '../../../../routes/components/Explore/Explore.svelte'
5
5
  import { fetchTitles } from '$lib/api/titles'
6
6
  import { title } from '$lib/fakeData'
7
- import { searchTitles } from '$lib/api/search'
8
7
  import { TrackingEvent } from '$lib/enums/TrackingEvent'
9
8
  import { track } from '$lib/tracking'
10
9
 
@@ -103,21 +102,6 @@ describe('Explore.svelte', () => {
103
102
  expect(queryByTestId('skeleton')).not.toBeTruthy()
104
103
  })
105
104
 
106
- it('Should fetch using searchTitles when query is given, resetting previous titles', async () => {
107
- vi.mocked(fetchTitles).mockResolvedValueOnce({ results: [title, title], next: 'truthy', previous: null })
108
-
109
- const { getByRole, getAllByTestId } = render(Explore)
110
-
111
- vi.mocked(searchTitles).mockResolvedValueOnce([title])
112
-
113
- fireEvent.input(getByRole('searchbox'), { target: { value: 'some query' } })
114
-
115
- await waitFor(() => {
116
- expect(searchTitles).toHaveBeenCalledWith('some query')
117
- expect(getAllByTestId('title')).toHaveLength(1)
118
- })
119
- })
120
-
121
105
  it('Should call fetchTitles with string of comma separated values when filtering with array value', async () => {
122
106
  vi.mocked(fetchTitles).mockResolvedValue({ results: [title], next: 'truthy', previous: null })
123
107
 
@@ -131,6 +115,21 @@ describe('Explore.svelte', () => {
131
115
  expect(fetchTitles).toHaveBeenCalledWith({ page: 1, page_size: 24, genres: '101,109' })
132
116
  })
133
117
 
118
+ it('Should include search param when query is given', async () => {
119
+ vi.mocked(fetchTitles).mockResolvedValueOnce({ results: [title, title], next: 'truthy', previous: null })
120
+
121
+ const { getByRole, getAllByTestId } = render(Explore)
122
+
123
+ vi.mocked(fetchTitles).mockResolvedValueOnce({ results: [title], next: 'truthy', previous: null })
124
+
125
+ fireEvent.input(getByRole('searchbox'), { target: { value: 'some query' } })
126
+
127
+ await waitFor(() => {
128
+ expect(fetchTitles).toHaveBeenCalledWith({ page: 1, page_size: 24, search: 'some query' })
129
+ expect(getAllByTestId('title')).toHaveLength(1)
130
+ })
131
+ })
132
+
134
133
  it('Should call fetchTitles with min and max values when filtering with range value', async () => {
135
134
  vi.mocked(fetchTitles).mockResolvedValue({ results: [title], next: 'truthy', previous: null })
136
135
 
@@ -84,26 +84,17 @@ describe('Modal.svelte', () => {
84
84
  expect(onscroll).toHaveBeenCalled()
85
85
  })
86
86
 
87
- it('Should set overflow on mount', () => {
87
+ it('Should set class on mount', () => {
88
88
  render(Modal, { children })
89
89
 
90
- expect(document.documentElement.style.overflowY).toBe('hidden')
90
+ expect(document.documentElement.classList).toContain('playpilot-modal-open')
91
91
  })
92
92
 
93
- it('Should remove overflow when component is unmounted', () => {
93
+ it('Should remove class when component is unmounted', () => {
94
94
  const { unmount } = render(Modal, { children })
95
95
  unmount()
96
96
 
97
- expect(document.documentElement.style.overflowY).toBe('')
98
- })
99
-
100
- it('Should use overflow as it was before the component was mounted when component is unmounted', () => {
101
- document.documentElement.style.overflowY = 'some-value'
102
-
103
- const { unmount } = render(Modal, { children })
104
- unmount()
105
-
106
- expect(document.documentElement.style.overflowY).toBe('some-value')
97
+ expect(document.documentElement.classList).not.toContain('playpilot-modal-open')
107
98
  })
108
99
 
109
100
  it('Should not have tall class by default', () => {
@@ -1,5 +1,5 @@
1
1
  import { fireEvent, render } from '@testing-library/svelte'
2
- import { describe, expect, it, vi } from 'vitest'
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
3
3
 
4
4
  import Playlinks from '../../../../routes/components/Playlinks/Playlinks.svelte'
5
5
  import { title } from '$lib/fakeData'
@@ -17,6 +17,11 @@ vi.mock('svelte', async (importActual) => ({
17
17
  }))
18
18
 
19
19
  describe('Playlinks.svelte', () => {
20
+ beforeEach(() => {
21
+ // @ts-ignore
22
+ window.PlayPilotLinkInjections = {}
23
+ })
24
+
20
25
  it('Should render each given playlink', () => {
21
26
  const playlinks = [
22
27
  { name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
@@ -35,9 +40,26 @@ describe('Playlinks.svelte', () => {
35
40
  { name: 'Some other playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
36
41
  ]
37
42
  // @ts-ignore
38
- const { getByTestId } = render(Playlinks, { playlinks, title })
43
+ const { getByTestId, getByText } = render(Playlinks, { playlinks, title })
39
44
 
40
45
  expect(getByTestId('commission-disclaimer')).toBeTruthy()
46
+ expect(getByText('We may earn a commission', { exact: false })).toBeTruthy()
47
+ })
48
+
49
+ it('Should replace default disclaimer text with config value when given', () => {
50
+ // @ts-ignore
51
+ window.PlayPilotLinkInjections = {
52
+ config: { playlinks_disclaimer_text: 'Some disclaimer' },
53
+ }
54
+
55
+ const playlinks = [
56
+ { name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
57
+ ]
58
+
59
+ // @ts-ignore
60
+ const { getByText } = render(Playlinks, { playlinks, title })
61
+
62
+ expect(getByText('Some disclaimer')).toBeTruthy()
41
63
  })
42
64
 
43
65
  it('Should show empty state without commission disclaimer when no playlinks were given', () => {
package/tsconfig.json CHANGED
@@ -9,7 +9,8 @@
9
9
  "skipLibCheck": true,
10
10
  "sourceMap": true,
11
11
  "strict": true,
12
- "moduleResolution": "bundler"
12
+ "moduleResolution": "bundler",
13
+ "types": ["node"]
13
14
  }
14
15
  // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
15
16
  // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files