@playpilot/tpi 8.22.0-beta.1 → 8.23.0-beta.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/dist/editorial.mount.js +11 -11
- package/dist/link-injections.js +2 -2
- package/dist/mount.js +8 -8
- package/package.json +2 -2
- package/src/lib/api/api.ts +2 -3
- package/src/lib/modal.ts +3 -1
- package/src/lib/types/global.d.ts +2 -0
- package/src/lib/types/language.d.ts +1 -1
- package/src/routes/components/Explore/Routes/ExploreHome.svelte +36 -30
- package/src/routes/components/Explore/Routes/ExploreModal.svelte +3 -1
- package/src/routes/components/Modals/ModalCloseOverlay.svelte +65 -0
- package/src/routes/components/Modals/RailModal.svelte +8 -51
- package/src/routes/components/Modals/TitlesReelModal.svelte +188 -0
- package/src/routes/components/Playlinks/Playlinks.svelte +2 -38
- package/src/routes/components/Playlinks/PlaylinksDisclaimer.svelte +51 -0
- package/src/routes/components/Title.svelte +4 -142
- package/src/routes/components/TitleBackground.svelte +110 -0
- package/src/routes/components/TitleInfo.svelte +69 -0
- package/src/routes/components/YouTubeEmbed.svelte +13 -2
- package/src/routes/components/YouTubeEmbedBackground.svelte +3 -2
- package/src/routes/elements/+page.svelte +10 -0
- package/src/tests/routes/components/Explore/Routes/ExploreHome.test.js +3 -14
- package/src/tests/routes/components/Playlinks/Playlinks.test.js +0 -37
- package/src/tests/routes/components/Playlinks/PlaylinksDisclaimer.test.js +49 -0
- package/src/tests/routes/components/YouTubeEmbed.test.js +6 -0
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import Genres from './Genres.svelte'
|
|
3
2
|
import Playlinks from './Playlinks/Playlinks.svelte'
|
|
4
3
|
import Description from './Description.svelte'
|
|
5
|
-
import IconIMDb from './Icons/IconIMDb.svelte'
|
|
6
4
|
import ParticipantsRail from './Rails/ParticipantsRail.svelte'
|
|
7
5
|
import SimilarRail from './Rails/SimilarRail.svelte'
|
|
8
6
|
import TitlePoster from './TitlePoster.svelte'
|
|
9
7
|
import Share from './Share.svelte'
|
|
10
8
|
import Trailer from './Trailer.svelte'
|
|
11
|
-
import YouTubeEmbedBackground from './YouTubeEmbedBackground.svelte'
|
|
12
|
-
import { t } from '$lib/localization'
|
|
13
9
|
import type { TitleData } from '$lib/types/title'
|
|
14
10
|
import { heading } from '$lib/actions/heading'
|
|
15
|
-
import { removeImageUrlPrefix } from '$lib/image'
|
|
16
11
|
import { titleUrl } from '$lib/routes'
|
|
17
12
|
import { getContext, setContext } from 'svelte'
|
|
18
13
|
import { track } from '$lib/tracking'
|
|
19
14
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
20
|
-
import
|
|
21
|
-
import
|
|
15
|
+
import TitleBackground from './TitleBackground.svelte'
|
|
16
|
+
import TitleInfo from './TitleInfo.svelte'
|
|
22
17
|
|
|
23
18
|
interface Props {
|
|
24
19
|
title: TitleData
|
|
@@ -36,18 +31,6 @@
|
|
|
36
31
|
const showDescription = $derived((!small || noAffiliate) && title.description)
|
|
37
32
|
|
|
38
33
|
let posterLoaded = $state(false)
|
|
39
|
-
let backgroundLoaded = $state(false)
|
|
40
|
-
let useBackgroundFallback = $state(false)
|
|
41
|
-
let hasYouTubeVideoAvailable = $state(false)
|
|
42
|
-
|
|
43
|
-
$effect(() => {
|
|
44
|
-
if (!useVideoBackground) return
|
|
45
|
-
if (!title.embeddable_url) return
|
|
46
|
-
|
|
47
|
-
isYouTubeVideoAvailableInRegion(getVideoId(title.embeddable_url)!).then(isAvailable => {
|
|
48
|
-
hasYouTubeVideoAvailable = isAvailable
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
34
|
</script>
|
|
52
35
|
|
|
53
36
|
<div class="content" class:small>
|
|
@@ -58,21 +41,7 @@
|
|
|
58
41
|
|
|
59
42
|
<div class="heading" use:heading={2} class:truncate={small} id="heading">{title.title}</div>
|
|
60
43
|
|
|
61
|
-
<
|
|
62
|
-
<div class="imdb">
|
|
63
|
-
<IconIMDb />
|
|
64
|
-
{title.imdb_score}
|
|
65
|
-
</div>
|
|
66
|
-
|
|
67
|
-
<Genres genres={title.genres} />
|
|
68
|
-
|
|
69
|
-
<div>{title.year}</div>
|
|
70
|
-
<div class="capitalize">{t(`Type: ${title.type}`)}</div>
|
|
71
|
-
|
|
72
|
-
{#if !small && title.length}
|
|
73
|
-
<div>{title.length} {t('Minutes')}</div>
|
|
74
|
-
{/if}
|
|
75
|
-
</div>
|
|
44
|
+
<TitleInfo {title} {small} />
|
|
76
45
|
|
|
77
46
|
<div class="actions">
|
|
78
47
|
{#if title.embeddable_url}
|
|
@@ -97,21 +66,7 @@
|
|
|
97
66
|
</div>
|
|
98
67
|
</div>
|
|
99
68
|
|
|
100
|
-
<
|
|
101
|
-
{#if useVideoBackground && hasYouTubeVideoAvailable}
|
|
102
|
-
<YouTubeEmbedBackground embeddable_url={title.embeddable_url!} />
|
|
103
|
-
{:else}
|
|
104
|
-
{#key useBackgroundFallback}
|
|
105
|
-
<img
|
|
106
|
-
src={removeImageUrlPrefix(useBackgroundFallback ? title.standing_poster : title.medium_poster)}
|
|
107
|
-
alt=""
|
|
108
|
-
class:loaded={backgroundLoaded}
|
|
109
|
-
class:blur={useBackgroundFallback}
|
|
110
|
-
onload={() => backgroundLoaded = true}
|
|
111
|
-
onerror={() => useBackgroundFallback = true} />
|
|
112
|
-
{/key}
|
|
113
|
-
{/if}
|
|
114
|
-
</div>
|
|
69
|
+
<TitleBackground {title} {small} {useVideoBackground} offsetMute={modal === 'title'} />
|
|
115
70
|
|
|
116
71
|
<style lang="scss">
|
|
117
72
|
$poster-width: margin(4.5);
|
|
@@ -199,45 +154,11 @@
|
|
|
199
154
|
}
|
|
200
155
|
}
|
|
201
156
|
|
|
202
|
-
.info {
|
|
203
|
-
z-index: 1;
|
|
204
|
-
position: relative;
|
|
205
|
-
grid-area: info;
|
|
206
|
-
display: flex;
|
|
207
|
-
flex-wrap: wrap;
|
|
208
|
-
align-items: center;
|
|
209
|
-
gap: margin(0.25) margin(0.75);
|
|
210
|
-
|
|
211
|
-
@include desktop {
|
|
212
|
-
gap: margin(0.5) margin(1);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
.small & {
|
|
216
|
-
gap: margin(0.5) margin(0.75);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
157
|
.main {
|
|
221
158
|
z-index: 1;
|
|
222
159
|
position: relative;
|
|
223
160
|
}
|
|
224
161
|
|
|
225
|
-
.imdb {
|
|
226
|
-
display: flex;
|
|
227
|
-
align-items: center;
|
|
228
|
-
gap: margin(0.25);
|
|
229
|
-
|
|
230
|
-
:global(svg) {
|
|
231
|
-
margin-top: margin(-0.125);
|
|
232
|
-
|
|
233
|
-
.small & {
|
|
234
|
-
margin-top: 0;
|
|
235
|
-
width: margin(0.75);
|
|
236
|
-
height: margin(0.75);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
162
|
.actions {
|
|
242
163
|
z-index: 1;
|
|
243
164
|
position: relative;
|
|
@@ -247,65 +168,6 @@
|
|
|
247
168
|
margin-top: margin(1);
|
|
248
169
|
}
|
|
249
170
|
|
|
250
|
-
.background {
|
|
251
|
-
position: absolute;
|
|
252
|
-
top: 0;
|
|
253
|
-
left: 0;
|
|
254
|
-
width: 100%;
|
|
255
|
-
height: theme(detail-background-height, margin(20));
|
|
256
|
-
border-radius: theme(detail-background-border-radius, 0px);
|
|
257
|
-
overflow: hidden;
|
|
258
|
-
background: theme(detail-background, lighter);
|
|
259
|
-
mask-image: linear-gradient(to bottom, black 40%, transparent);
|
|
260
|
-
opacity: theme(detail-background-opacity, 1);
|
|
261
|
-
|
|
262
|
-
@include desktop() {
|
|
263
|
-
height: theme(detail-background-height, margin(12));
|
|
264
|
-
mask-image: linear-gradient(to bottom, black 60%, transparent);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
@include mobile {
|
|
268
|
-
:global(.video-background) {
|
|
269
|
-
width: 225%;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
&.small {
|
|
274
|
-
:global(.video-background) {
|
|
275
|
-
width: 150%;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
&.offset-mute {
|
|
280
|
-
--mute-top: #{margin(3)};
|
|
281
|
-
--mute-right: #{margin(0.25)};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
img {
|
|
285
|
-
width: 100%;
|
|
286
|
-
height: 100%;
|
|
287
|
-
object-fit: cover;
|
|
288
|
-
object-position: center;
|
|
289
|
-
margin: 0;
|
|
290
|
-
opacity: 0;
|
|
291
|
-
z-index: 0;
|
|
292
|
-
|
|
293
|
-
&.loaded {
|
|
294
|
-
opacity: 1;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
&.blur {
|
|
298
|
-
opacity: 0.5;
|
|
299
|
-
transform: scale(1.1);
|
|
300
|
-
filter: blur(0.75rem);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.capitalize {
|
|
306
|
-
text-transform: capitalize;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
171
|
.truncate {
|
|
310
172
|
overflow: hidden;
|
|
311
173
|
text-overflow: ellipsis;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { isYouTubeVideoAvailableInRegion } from '$lib/api/youtubeAvailability'
|
|
3
|
+
import { removeImageUrlPrefix } from '$lib/image'
|
|
4
|
+
import { getVideoId } from '$lib/trailer'
|
|
5
|
+
import type { TitleData } from '$lib/types/title'
|
|
6
|
+
import YouTubeEmbedBackground from './YouTubeEmbedBackground.svelte'
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
title: TitleData,
|
|
10
|
+
small?: boolean,
|
|
11
|
+
offsetMute?: boolean,
|
|
12
|
+
useVideoBackground?: boolean,
|
|
13
|
+
autoplay?: boolean,
|
|
14
|
+
inline?: boolean,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { title, small = false, offsetMute = false, useVideoBackground = true, autoplay = true, inline = false }: Props = $props()
|
|
18
|
+
|
|
19
|
+
let backgroundLoaded = $state(false)
|
|
20
|
+
let useBackgroundFallback = $state(false)
|
|
21
|
+
let hasYouTubeVideoAvailable = $state(false)
|
|
22
|
+
|
|
23
|
+
$effect(() => {
|
|
24
|
+
if (!useVideoBackground) return
|
|
25
|
+
if (!title.embeddable_url) return
|
|
26
|
+
|
|
27
|
+
isYouTubeVideoAvailableInRegion(getVideoId(title.embeddable_url)!).then(isAvailable => {
|
|
28
|
+
hasYouTubeVideoAvailable = isAvailable
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<div class="background" class:small class:inline class:offset-mute={offsetMute}>
|
|
34
|
+
{#if useVideoBackground && hasYouTubeVideoAvailable}
|
|
35
|
+
<YouTubeEmbedBackground embeddable_url={title.embeddable_url!} {autoplay} />
|
|
36
|
+
{:else}
|
|
37
|
+
{#key useBackgroundFallback}
|
|
38
|
+
<img
|
|
39
|
+
src={removeImageUrlPrefix(useBackgroundFallback ? title.standing_poster : title.medium_poster)}
|
|
40
|
+
alt=""
|
|
41
|
+
class:loaded={backgroundLoaded}
|
|
42
|
+
class:blur={useBackgroundFallback}
|
|
43
|
+
onload={() => backgroundLoaded = true}
|
|
44
|
+
onerror={() => useBackgroundFallback = true} />
|
|
45
|
+
{/key}
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<style lang="scss">
|
|
50
|
+
.background {
|
|
51
|
+
position: absolute;
|
|
52
|
+
top: 0;
|
|
53
|
+
left: 0;
|
|
54
|
+
width: 100%;
|
|
55
|
+
height: theme(detail-background-height, margin(20));
|
|
56
|
+
border-radius: theme(detail-background-border-radius, 0px);
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
background: theme(detail-background, lighter);
|
|
59
|
+
mask-image: linear-gradient(to bottom, black 40%, transparent);
|
|
60
|
+
opacity: theme(detail-background-opacity, 1);
|
|
61
|
+
|
|
62
|
+
@include desktop() {
|
|
63
|
+
height: theme(detail-background-height, margin(12));
|
|
64
|
+
mask-image: linear-gradient(to bottom, black 60%, transparent);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@include mobile {
|
|
68
|
+
:global(.video-background) {
|
|
69
|
+
width: theme(detail-background-width, 225%);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
&.small {
|
|
74
|
+
:global(.video-background) {
|
|
75
|
+
width: 150%;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&.inline {
|
|
80
|
+
overflow: visible;
|
|
81
|
+
position: unset;
|
|
82
|
+
mask-image: unset;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&.offset-mute {
|
|
86
|
+
--mute-top: #{margin(3)};
|
|
87
|
+
--mute-right: #{margin(0.25)};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
img {
|
|
91
|
+
width: 100%;
|
|
92
|
+
height: 100%;
|
|
93
|
+
object-fit: cover;
|
|
94
|
+
object-position: center;
|
|
95
|
+
margin: 0;
|
|
96
|
+
opacity: 0;
|
|
97
|
+
z-index: 0;
|
|
98
|
+
|
|
99
|
+
&.loaded {
|
|
100
|
+
opacity: 1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&.blur {
|
|
104
|
+
opacity: 0.5;
|
|
105
|
+
transform: scale(1.1);
|
|
106
|
+
filter: blur(0.75rem);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { t } from '$lib/localization'
|
|
3
|
+
import type { TitleData } from '$lib/types/title'
|
|
4
|
+
import Genres from './Genres.svelte'
|
|
5
|
+
import IconIMDb from './Icons/IconIMDb.svelte'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
title: TitleData,
|
|
9
|
+
small?: boolean,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { title, small = false }: Props = $props()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<div class="info" class:small>
|
|
16
|
+
<div class="imdb">
|
|
17
|
+
<IconIMDb />
|
|
18
|
+
{title.imdb_score}
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<Genres genres={title.genres} />
|
|
22
|
+
|
|
23
|
+
<div>{title.year}</div>
|
|
24
|
+
<div class="capitalize">{t(`Type: ${title.type}`)}</div>
|
|
25
|
+
|
|
26
|
+
{#if !small && title.length}
|
|
27
|
+
<div>{title.length} {t('Minutes')}</div>
|
|
28
|
+
{/if}
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<style lang="scss">
|
|
32
|
+
.info {
|
|
33
|
+
z-index: 1;
|
|
34
|
+
position: relative;
|
|
35
|
+
grid-area: info;
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-wrap: wrap;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: margin(0.25) margin(0.75);
|
|
40
|
+
|
|
41
|
+
@include desktop {
|
|
42
|
+
gap: margin(0.5) margin(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&.small {
|
|
46
|
+
gap: margin(0.5) margin(0.75);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.imdb {
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: margin(0.25);
|
|
54
|
+
|
|
55
|
+
:global(svg) {
|
|
56
|
+
margin-top: margin(-0.125);
|
|
57
|
+
|
|
58
|
+
.small & {
|
|
59
|
+
margin-top: 0;
|
|
60
|
+
width: margin(0.75);
|
|
61
|
+
height: margin(0.75);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.capitalize {
|
|
67
|
+
text-transform: capitalize;
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
muted?: boolean
|
|
10
10
|
loop?: boolean
|
|
11
11
|
captions?: boolean
|
|
12
|
+
autoplay?: boolean
|
|
12
13
|
showMuteControls?: boolean
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
const { embeddable_url = '', controls = [], muted = false, loop = false, captions = false, showMuteControls = false }: Props = $props()
|
|
16
|
+
const { embeddable_url = '', controls = [], muted = false, loop = false, captions = false, autoplay = true, showMuteControls = false }: Props = $props()
|
|
16
17
|
|
|
17
18
|
const videoId = $derived(getVideoId(embeddable_url))
|
|
18
19
|
const startTime = $derived((window?.PlayPilotLinkInjections?.video_playtimes?.[videoId || ''] || 1) - 1)
|
|
@@ -39,14 +40,24 @@
|
|
|
39
40
|
if (isMuted != state) iframe?.contentWindow?.postMessage('mute', '*')
|
|
40
41
|
isMuted = state
|
|
41
42
|
}
|
|
43
|
+
|
|
44
|
+
export function play(event: CustomEvent): void {
|
|
45
|
+
if (event.detail === embeddable_url) iframe?.contentWindow?.postMessage('play', '*')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function pause(event: CustomEvent): void {
|
|
49
|
+
if (event.detail === embeddable_url) iframe?.contentWindow?.postMessage('pause', '*')
|
|
50
|
+
}
|
|
42
51
|
</script>
|
|
43
52
|
|
|
53
|
+
<svelte:window onplayembed={play} onpauseembed={pause} />
|
|
54
|
+
|
|
44
55
|
{#if videoId}
|
|
45
56
|
<iframe
|
|
46
57
|
bind:this={iframe}
|
|
47
58
|
width="600"
|
|
48
59
|
height="338"
|
|
49
|
-
src="https://video.playpilot.net/?video_id={videoId}&color={color}&muted={muted}&loop={loop}&captions={captions}&controls={controls.join(',')}&start_time={startTime}&autoplay=
|
|
60
|
+
src="https://video.playpilot.net/?video_id={videoId}&color={color}&muted={muted}&loop={loop}&captions={captions}&controls={controls.join(',')}&start_time={startTime}&autoplay={autoplay}&playsinline=true"
|
|
50
61
|
title="YouTube video player"
|
|
51
62
|
frameborder="0"
|
|
52
63
|
referrerpolicy="strict-origin-when-cross-origin"
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
5
|
embeddable_url: string
|
|
6
|
+
autoplay?: boolean
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
const { embeddable_url = '' }: Props = $props()
|
|
9
|
+
const { embeddable_url = '', autoplay = true }: Props = $props()
|
|
9
10
|
|
|
10
11
|
let clientWidth = $state(0)
|
|
11
12
|
let clientHeight = $state(0)
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
</script>
|
|
15
16
|
|
|
16
17
|
<div class="video-background" style="--height: {height}px; --width: {clientWidth}px;" bind:clientWidth bind:clientHeight data-testid="video-background">
|
|
17
|
-
<YouTubeEmbed {embeddable_url} muted loop showMuteControls />
|
|
18
|
+
<YouTubeEmbed {embeddable_url} {autoplay} muted loop showMuteControls />
|
|
18
19
|
</div>
|
|
19
20
|
|
|
20
21
|
<style lang="scss">
|
|
@@ -87,6 +87,16 @@
|
|
|
87
87
|
<button onclick={() => openModal({ type: 'participant', data: participants[0] })}>Show participant modal</button>
|
|
88
88
|
</div>
|
|
89
89
|
</div>
|
|
90
|
+
|
|
91
|
+
<div>
|
|
92
|
+
<h3>TitlesReelModal.svelte</h3>
|
|
93
|
+
|
|
94
|
+
{#await fetchSimilarTitles(title) then titles}
|
|
95
|
+
<div class="item">
|
|
96
|
+
<button onclick={() => openModal({ type: 'titles-reel', data: titles })}>Show titles reel modal</button>
|
|
97
|
+
</div>
|
|
98
|
+
{/await}
|
|
99
|
+
</div>
|
|
90
100
|
</div>
|
|
91
101
|
|
|
92
102
|
<h2>Title elements</h2>
|
|
@@ -1,34 +1,23 @@
|
|
|
1
|
-
import { render
|
|
1
|
+
import { render } from '@testing-library/svelte'
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
3
|
|
|
4
4
|
import ExploreHome from '../../../../../routes/components/Explore/Routes/ExploreHome.svelte'
|
|
5
5
|
import { fetchTitles } from '$lib/api/titles'
|
|
6
|
-
import { api } from '$lib/api/api'
|
|
7
6
|
|
|
8
7
|
vi.mock('$lib/api/titles', () => ({
|
|
9
8
|
fetchTitles: vi.fn(),
|
|
10
9
|
}))
|
|
11
10
|
|
|
12
|
-
vi.mock('$lib/api/api', () => ({
|
|
13
|
-
api: vi.fn(),
|
|
14
|
-
}))
|
|
15
|
-
|
|
16
11
|
describe('ExploreResults.svelte', () => {
|
|
17
12
|
beforeEach(() => {
|
|
18
13
|
vi.resetAllMocks()
|
|
19
14
|
})
|
|
20
15
|
|
|
21
|
-
it('Should fetch
|
|
22
|
-
const rail = { headings: {}, properties: {}, params: {} }
|
|
23
|
-
|
|
24
|
-
vi.mocked(api).mockResolvedValue([rail, rail, rail, rail, rail])
|
|
16
|
+
it('Should fetch titles for each rail', () => {
|
|
25
17
|
vi.mocked(fetchTitles).mockResolvedValue({ next: null, previous: null, results: [] })
|
|
26
18
|
|
|
27
19
|
render(ExploreHome)
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
expect(api).toHaveBeenCalledOnce()
|
|
31
|
-
expect(fetchTitles).toHaveBeenCalledTimes(5)
|
|
32
|
-
})
|
|
21
|
+
expect(fetchTitles).toHaveBeenCalledTimes(5)
|
|
33
22
|
})
|
|
34
23
|
})
|
|
@@ -38,43 +38,6 @@ describe('Playlinks.svelte', () => {
|
|
|
38
38
|
expect(getByText('Some other playlink')).toBeTruthy()
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
it('Should show disclaimer text when playlinks are given', () => {
|
|
42
|
-
const playlinks = [
|
|
43
|
-
{ name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
|
|
44
|
-
{ name: 'Some other playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
|
|
45
|
-
]
|
|
46
|
-
// @ts-ignore
|
|
47
|
-
const { getByTestId, getByText } = render(Playlinks, { playlinks, title })
|
|
48
|
-
|
|
49
|
-
expect(getByTestId('commission-disclaimer')).toBeTruthy()
|
|
50
|
-
expect(getByText('We may earn a commission', { exact: false })).toBeTruthy()
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('Should replace default disclaimer text with config value when given', () => {
|
|
54
|
-
// @ts-ignore
|
|
55
|
-
window.PlayPilotLinkInjections = {
|
|
56
|
-
config: { playlinks_disclaimer_text: 'Some disclaimer' },
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const playlinks = [
|
|
60
|
-
{ name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
|
|
61
|
-
]
|
|
62
|
-
|
|
63
|
-
// @ts-ignore
|
|
64
|
-
const { getByText } = render(Playlinks, { playlinks, title })
|
|
65
|
-
|
|
66
|
-
expect(getByText('Some disclaimer')).toBeTruthy()
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('Should show empty state without commission disclaimer when no playlinks are given', () => {
|
|
70
|
-
/** @type {import('$lib/types/playlink').PlaylinkData[]} */
|
|
71
|
-
const playlinks = []
|
|
72
|
-
const { queryByTestId, getByText } = render(Playlinks, { playlinks, title })
|
|
73
|
-
|
|
74
|
-
expect(getByText('This title is not currently available to stream.')).toBeTruthy()
|
|
75
|
-
expect(queryByTestId('commission-disclaimer')).not.toBeTruthy()
|
|
76
|
-
})
|
|
77
|
-
|
|
78
41
|
it('Should call track function when clicked', async () => {
|
|
79
42
|
const playlinks = [
|
|
80
43
|
{ name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { render } from '@testing-library/svelte'
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import Playlinks from '../../../../routes/components/Playlinks/Playlinks.svelte'
|
|
5
|
+
import { title } from '$lib/fakeData'
|
|
6
|
+
|
|
7
|
+
describe('Playlinks.svelte', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
window.PlayPilotLinkInjections = {}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('Should show disclaimer text when playlinks are given', () => {
|
|
14
|
+
const playlinks = [
|
|
15
|
+
{ name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
|
|
16
|
+
{ name: 'Some other playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
|
|
17
|
+
]
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
const { getByTestId, getByText } = render(Playlinks, { playlinks, title })
|
|
20
|
+
|
|
21
|
+
expect(getByTestId('commission-disclaimer')).toBeTruthy()
|
|
22
|
+
expect(getByText('We may earn a commission', { exact: false })).toBeTruthy()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('Should replace default disclaimer text with config value when given', () => {
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
window.PlayPilotLinkInjections = {
|
|
28
|
+
config: { playlinks_disclaimer_text: 'Some disclaimer' },
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const playlinks = [
|
|
32
|
+
{ name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } },
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
const { getByText } = render(Playlinks, { playlinks, title })
|
|
37
|
+
|
|
38
|
+
expect(getByText('Some disclaimer')).toBeTruthy()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('Should show empty state without commission disclaimer when no playlinks are given', () => {
|
|
42
|
+
/** @type {import('$lib/types/playlink').PlaylinkData[]} */
|
|
43
|
+
const playlinks = []
|
|
44
|
+
const { queryByTestId, getByText } = render(Playlinks, { playlinks, title })
|
|
45
|
+
|
|
46
|
+
expect(getByText('This title is not currently available to stream.')).toBeTruthy()
|
|
47
|
+
expect(queryByTestId('commission-disclaimer')).not.toBeTruthy()
|
|
48
|
+
})
|
|
49
|
+
})
|
|
@@ -40,6 +40,12 @@ describe('YouTubeEmbed.svelte', () => {
|
|
|
40
40
|
expect(/** @type {HTMLIFrameElement} */ (container.querySelector('iframe')).src).toContain('&captions=true')
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
+
it('Should render embed iframe with autoplay set to false if given', () => {
|
|
44
|
+
const { container } = render(YouTubeEmbed, { embeddable_url: 'youtube.com/watch?v=abc', autoplay: false })
|
|
45
|
+
|
|
46
|
+
expect(/** @type {HTMLIFrameElement} */ (container.querySelector('iframe')).src).toContain('&autoplay=false')
|
|
47
|
+
})
|
|
48
|
+
|
|
43
49
|
it('Should render error message if embeddable_url is invalid', () => {
|
|
44
50
|
const { container, getByText } = render(YouTubeEmbed, { embeddable_url: '-' })
|
|
45
51
|
|