@playpilot/tpi 8.17.1 → 8.19.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/.env +1 -1
- package/dist/editorial.mount.js +11 -11
- package/dist/link-injections.js +2 -2
- package/dist/mount.js +9 -9
- package/package.json +1 -1
- package/src/lib/api/titles.ts +2 -2
- package/src/lib/constants.ts +1 -0
- package/src/lib/data/translations.ts +5 -0
- package/src/lib/enums/TrackingEvent.ts +4 -4
- package/src/lib/fakeData.ts +44 -32
- package/src/lib/injection.ts +10 -9
- package/src/lib/modal.ts +3 -1
- package/src/lib/popover.ts +13 -12
- package/src/lib/scss/_mixins.scss +29 -0
- package/src/lib/scss/global.scss +0 -27
- package/src/lib/tracking.ts +2 -0
- package/src/lib/types/injection.d.ts +5 -0
- package/src/main.ts +2 -1
- package/src/routes/+page.svelte +3 -3
- package/src/routes/components/Editorial/Editor.svelte +8 -5
- package/src/routes/components/Editorial/EditorItem.svelte +23 -9
- package/src/routes/components/Editorial/ManualInjection.svelte +1 -0
- package/src/routes/components/Explore/ExploreLayout.svelte +5 -3
- package/src/routes/components/Explore/Filter/Dropdown.svelte +3 -1
- package/src/routes/components/Explore/Filter/Filter.svelte +5 -3
- package/src/routes/components/Explore/Routes/ExploreHome.svelte +4 -4
- package/src/routes/components/{TitlePopover.svelte → InjectionPopover.svelte} +26 -9
- package/src/routes/components/ListTitle.svelte +27 -6
- package/src/routes/components/Modals/Modal.svelte +2 -0
- package/src/routes/components/Modals/RailModal.svelte +9 -3
- package/src/routes/components/Participant.svelte +18 -6
- package/src/routes/components/Playlinks/Playlink.svelte +1 -1
- package/src/routes/components/Playlinks/PlaylinkIcon.svelte +1 -1
- package/src/routes/components/Playlinks/Playlinks.svelte +21 -5
- package/src/routes/components/Popover.svelte +2 -1
- package/src/routes/components/Title.svelte +1 -1
- package/src/routes/elements/+page.svelte +3 -3
- package/src/tests/helpers.js +1 -0
- package/src/tests/lib/api/titles.test.js +4 -4
- package/src/tests/lib/injection.test.js +44 -3
- package/src/tests/lib/popover.test.js +7 -7
- package/src/tests/lib/tracking.test.js +8 -0
- package/src/tests/routes/components/Editorial/EditorItem.test.js +10 -0
- package/src/tests/routes/components/Editorial/ManualInjection.test.js +4 -0
- package/src/tests/routes/components/Editorial/PlaylinkTypeSelect.test.js +2 -0
- package/src/tests/routes/components/InjectionPopover.test.js +117 -0
- package/src/tests/routes/components/ListTitle.test.js +7 -0
- package/src/tests/routes/components/Participant.test.js +7 -0
- package/src/tests/routes/components/Playlinks/AfterArticlePlaylinks.test.js +4 -0
- package/src/tests/routes/components/Playlinks/Playlinks.test.js +51 -1
- package/src/tests/routes/components/{TrackAnyClick.test.js → Tracking/TrackAnyClick.test.js} +1 -1
- package/src/tests/routes/components/{TrackingPixels.test.js → Tracking/TrackingPixels.test.js} +1 -1
- package/src/tests/routes/components/TitlePopover.test.js +0 -78
- /package/src/routes/components/{TrackAnyClick.svelte → Tracking/TrackAnyClick.svelte} +0 -0
- /package/src/routes/components/{TrackScrollDistance.svelte → Tracking/TrackScrollDistance.svelte} +0 -0
- /package/src/routes/components/{TrackingPixels.svelte → Tracking/TrackingPixels.svelte} +0 -0
- /package/src/routes/components/{TrackingPixelsForTitleDataPerLink.svelte → Tracking/TrackingPixelsForTitleDataPerLink.svelte} +0 -0
- /package/src/routes/components/{UserJourney.svelte → Tracking/UserJourney.svelte} +0 -0
|
@@ -3,35 +3,48 @@
|
|
|
3
3
|
import { track } from '$lib/tracking'
|
|
4
4
|
import { getFirstAdOfType } from '$lib/api/ads'
|
|
5
5
|
import type { TitleData } from '$lib/types/title'
|
|
6
|
-
import {
|
|
6
|
+
import type { ParticipantData } from '$lib/types/participant'
|
|
7
|
+
import type { LinkInjectionDataType } from '$lib/types/injection'
|
|
8
|
+
import { onMount, setContext } from 'svelte'
|
|
7
9
|
import { isPixelAllowed } from '$lib/pixel'
|
|
8
10
|
import { trackViaPixel } from '@playpilot/retargeting-tracking'
|
|
9
11
|
import Popover from './Popover.svelte'
|
|
10
12
|
import Title from './Title.svelte'
|
|
13
|
+
import Participant from './Participant.svelte'
|
|
11
14
|
import TopScroll from './Ads/TopScroll.svelte'
|
|
12
15
|
import Display from './Ads/Display.svelte'
|
|
13
16
|
|
|
14
17
|
interface Props {
|
|
15
18
|
event: MouseEvent
|
|
16
|
-
|
|
19
|
+
data: TitleData | ParticipantData
|
|
20
|
+
type?: LinkInjectionDataType
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
const { event, title }: Props = $props()
|
|
23
|
+
const { event, data, type = 'title' }: Props = $props()
|
|
24
|
+
|
|
25
|
+
setContext('type', type)
|
|
20
26
|
|
|
21
27
|
const topScroll = getFirstAdOfType('top_scroll')
|
|
22
28
|
const displayAd = getFirstAdOfType('card')
|
|
23
29
|
|
|
24
30
|
let element: HTMLElement | null = $state(null)
|
|
25
31
|
|
|
26
|
-
track(TrackingEvent.
|
|
32
|
+
if (type === 'title') track(TrackingEvent.InjectionPopoverView, data as TitleData, { type })
|
|
33
|
+
if (type === 'participant') track(TrackingEvent.InjectionPopoverView, null, { type, participant: (data as ParticipantData).name })
|
|
27
34
|
|
|
28
|
-
if (isPixelAllowed()) trackViaPixel(MetaEvent.TitleInterest, { title:
|
|
35
|
+
if (isPixelAllowed() && type === 'title') trackViaPixel(MetaEvent.TitleInterest, { title: (data as TitleData).title, source: MetaSource.Card })
|
|
29
36
|
|
|
30
37
|
onMount(() => {
|
|
31
38
|
setOffset()
|
|
32
39
|
|
|
33
40
|
const openTimestamp = Date.now()
|
|
34
|
-
|
|
41
|
+
|
|
42
|
+
return () => {
|
|
43
|
+
const endTimestamp = Date.now() - openTimestamp
|
|
44
|
+
|
|
45
|
+
if (type === 'title') track(TrackingEvent.InjectionPopoverClose, data as TitleData, { type, time_spent: endTimestamp })
|
|
46
|
+
if (type === 'participant') track(TrackingEvent.InjectionPopoverClose, null, { type, participant: (data as ParticipantData).name, time_spent: endTimestamp })
|
|
47
|
+
}
|
|
35
48
|
})
|
|
36
49
|
|
|
37
50
|
/**
|
|
@@ -68,14 +81,18 @@
|
|
|
68
81
|
{/if}
|
|
69
82
|
{/snippet}
|
|
70
83
|
|
|
71
|
-
<div class="
|
|
84
|
+
<div class="injection-popover" bind:this={element} data-playpilot-injection-popover role="region" aria-labelledby="heading">
|
|
72
85
|
<Popover append={displayAd ? append : null} bubble={topScroll ? bubble : null}>
|
|
73
|
-
|
|
86
|
+
{#if type === 'title'}
|
|
87
|
+
<Title title={data as TitleData} small />
|
|
88
|
+
{:else if type === 'participant'}
|
|
89
|
+
<Participant participant={data as ParticipantData} small />
|
|
90
|
+
{/if}
|
|
74
91
|
</Popover>
|
|
75
92
|
</div>
|
|
76
93
|
|
|
77
94
|
<style lang="scss">
|
|
78
|
-
.
|
|
95
|
+
.injection-popover {
|
|
79
96
|
position: absolute;
|
|
80
97
|
}
|
|
81
98
|
</style>
|
|
@@ -8,15 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
title: TitleData
|
|
11
|
+
compact?: boolean
|
|
11
12
|
onclick?: (event: MouseEvent) => void
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
const { title, onclick = () => null }: Props = $props()
|
|
15
|
+
const { title, compact = false, onclick = () => null }: Props = $props()
|
|
15
16
|
|
|
16
17
|
const noAffiliate = !!window.PlayPilotLinkInjections?.no_affiliate
|
|
17
18
|
</script>
|
|
18
19
|
|
|
19
|
-
<button class="title" {onclick} data-testid="title">
|
|
20
|
+
<button class="title" class:compact {onclick} data-testid="title">
|
|
20
21
|
<div class="poster">
|
|
21
22
|
<TitlePoster {title} width={30} height={43} lazy />
|
|
22
23
|
</div>
|
|
@@ -38,12 +39,14 @@
|
|
|
38
39
|
{/if}
|
|
39
40
|
</div>
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
{
|
|
43
|
-
|
|
42
|
+
{#if !compact}
|
|
43
|
+
<div class="description" class:large={noAffiliate}>
|
|
44
|
+
{title.description}
|
|
45
|
+
</div>
|
|
46
|
+
{/if}
|
|
44
47
|
|
|
45
48
|
{#if !noAffiliate}
|
|
46
|
-
<PlaylinksCompact playlinks={title.providers} />
|
|
49
|
+
<PlaylinksCompact playlinks={title.providers} size={compact ? 24 : 30} />
|
|
47
50
|
{/if}
|
|
48
51
|
</div>
|
|
49
52
|
|
|
@@ -84,6 +87,10 @@
|
|
|
84
87
|
border-radius: theme(detail-image-border-radius, border-radius);
|
|
85
88
|
background: theme(detail-image-background, content);
|
|
86
89
|
overflow: hidden;
|
|
90
|
+
|
|
91
|
+
.compact & {
|
|
92
|
+
width: margin(3.5);
|
|
93
|
+
}
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
.content {
|
|
@@ -97,11 +104,17 @@
|
|
|
97
104
|
@include desktop() {
|
|
98
105
|
padding-right: margin(1);
|
|
99
106
|
}
|
|
107
|
+
|
|
108
|
+
.compact & {
|
|
109
|
+
padding-left: margin(0.75);
|
|
110
|
+
padding-right: 0;
|
|
111
|
+
}
|
|
100
112
|
}
|
|
101
113
|
|
|
102
114
|
.heading {
|
|
103
115
|
color: theme(list-item-title-text-color, text-color);
|
|
104
116
|
font-weight: theme(list-item-title-font-weight, font-bold);
|
|
117
|
+
line-height: 1.2;
|
|
105
118
|
}
|
|
106
119
|
|
|
107
120
|
.meta {
|
|
@@ -116,6 +129,10 @@
|
|
|
116
129
|
> div {
|
|
117
130
|
text-transform: capitalize;
|
|
118
131
|
}
|
|
132
|
+
|
|
133
|
+
.compact & {
|
|
134
|
+
margin: margin(0.125) 0 margin(0.25) 0;
|
|
135
|
+
}
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
.imdb {
|
|
@@ -152,5 +169,9 @@
|
|
|
152
169
|
&:active {
|
|
153
170
|
color: theme(list-item-action-hover-color, text-color);
|
|
154
171
|
}
|
|
172
|
+
|
|
173
|
+
.compact & {
|
|
174
|
+
display: none;
|
|
175
|
+
}
|
|
155
176
|
}
|
|
156
177
|
</style>
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
</Modal>
|
|
80
80
|
|
|
81
81
|
<style lang="scss">
|
|
82
|
-
$size: min(
|
|
82
|
+
$size: min(800px, 85vw);
|
|
83
83
|
|
|
84
84
|
.rail-modal {
|
|
85
85
|
--gap: #{margin(0.25)};
|
|
@@ -132,12 +132,18 @@
|
|
|
132
132
|
border-radius: theme(rail-modal-item-border-radius, border-radius-huge) theme(rail-modal-item-border-radius, border-radius-huge) 0 0;
|
|
133
133
|
background: theme(rail-modal-item-background, light);
|
|
134
134
|
box-shadow: none;
|
|
135
|
-
height: 90vh;
|
|
135
|
+
height: calc(90vh - env(safe-area-inset-top));
|
|
136
136
|
overflow: auto;
|
|
137
137
|
overscroll-behavior: contain;
|
|
138
138
|
transition: box-shadow var(--transition-duration);
|
|
139
139
|
|
|
140
|
-
@include
|
|
140
|
+
@include styled-scrollbar;
|
|
141
|
+
|
|
142
|
+
@supports (height: 1dvh) {
|
|
143
|
+
height: calc(90dvh - env(safe-area-inset-top));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@include desktop {
|
|
141
147
|
height: auto;
|
|
142
148
|
max-height: 90vh;
|
|
143
149
|
border-radius: theme(rail-modal-item-border-radius, border-radius-huge);
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
interface Props {
|
|
15
15
|
participant: ParticipantData
|
|
16
|
+
small?: boolean
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const { participant }: Props = $props()
|
|
19
|
+
const { participant, small = false }: Props = $props()
|
|
19
20
|
|
|
20
21
|
const { name, birth_date, death_date } = $derived(participant)
|
|
21
22
|
|
|
@@ -52,8 +53,8 @@
|
|
|
52
53
|
}
|
|
53
54
|
</script>
|
|
54
55
|
|
|
55
|
-
<div class="header">
|
|
56
|
-
<div class="heading" use:heading={2} id="
|
|
56
|
+
<div class="header" class:small>
|
|
57
|
+
<div class="heading" use:heading={2} id="heading">{name}</div>
|
|
57
58
|
|
|
58
59
|
{#if birth_date}
|
|
59
60
|
<p class="dates">
|
|
@@ -63,11 +64,13 @@
|
|
|
63
64
|
</div>
|
|
64
65
|
|
|
65
66
|
<div class="content">
|
|
66
|
-
|
|
67
|
+
{#if !small}
|
|
68
|
+
<div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
|
|
69
|
+
{/if}
|
|
67
70
|
|
|
68
71
|
<div class="list">
|
|
69
72
|
{#each titles as title}
|
|
70
|
-
<ListTitle {title} onclick={(event) => openModal({ event, data: title })} />
|
|
73
|
+
<ListTitle {title} compact={small} onclick={(event) => openModal({ event, data: title })} />
|
|
71
74
|
{/each}
|
|
72
75
|
|
|
73
76
|
{#if loading}
|
|
@@ -88,6 +91,10 @@
|
|
|
88
91
|
font-size: theme(detail-font-size, font-size-base);
|
|
89
92
|
line-height: theme(participant-description-line-height, normal);
|
|
90
93
|
color: theme(detail-text-color, text-color);
|
|
94
|
+
|
|
95
|
+
&.small {
|
|
96
|
+
padding: margin(1);
|
|
97
|
+
}
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
.heading {
|
|
@@ -98,7 +105,7 @@
|
|
|
98
105
|
line-height: normal;
|
|
99
106
|
font-style: theme(detail-title-font-style, normal);
|
|
100
107
|
|
|
101
|
-
&.
|
|
108
|
+
&.subheading {
|
|
102
109
|
margin: 0 0 margin(0.5);
|
|
103
110
|
font-size: theme(detail-title-small-font-size, margin(1.25));
|
|
104
111
|
}
|
|
@@ -116,6 +123,11 @@
|
|
|
116
123
|
font-size: theme(detail-font-size, font-size-base);
|
|
117
124
|
line-height: normal;
|
|
118
125
|
font-style: normal;
|
|
126
|
+
|
|
127
|
+
::view-transition-old(content),
|
|
128
|
+
::view-transition-new(content) {
|
|
129
|
+
height: 100%;
|
|
130
|
+
}
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
.list {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Disclaimer from '../Ads/Disclaimer.svelte'
|
|
3
|
-
import TrackingPixels from '../TrackingPixels.svelte'
|
|
3
|
+
import TrackingPixels from '../Tracking/TrackingPixels.svelte'
|
|
4
4
|
import { hasConsentedTo } from '$lib/consent'
|
|
5
5
|
import { removeImageUrlPrefix } from '$lib/image'
|
|
6
6
|
import { t } from '$lib/localization'
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { campaignToPlaylink, getFirstAdOfType } from '$lib/api/ads'
|
|
10
10
|
import { getContext } from 'svelte'
|
|
11
11
|
import Playlink from './Playlink.svelte'
|
|
12
|
+
import Button from '../Button.svelte'
|
|
12
13
|
|
|
13
14
|
type Category = PlaylinkCategory | '' | 'Other'
|
|
14
15
|
type CategorizedPlaylinks = Partial<Record<Category, PlaylinkData[]>>
|
|
@@ -17,19 +18,22 @@
|
|
|
17
18
|
interface Props {
|
|
18
19
|
playlinks: PlaylinkData[]
|
|
19
20
|
title: TitleData
|
|
21
|
+
limit?: number
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
const { playlinks, title }: Props = $props()
|
|
24
|
+
const { playlinks, title, limit = 3 }: Props = $props()
|
|
23
25
|
|
|
24
26
|
const isModal = getContext('scope') === 'modal'
|
|
27
|
+
const type = getContext('type')
|
|
25
28
|
const displayAd = getFirstAdOfType('card')
|
|
26
29
|
const categorize = !!window.PlayPilotLinkInjections?.config?.categorize_playlinks
|
|
27
30
|
|
|
28
31
|
let outerWidth = $state(0)
|
|
32
|
+
let limited = $state(!categorize)
|
|
29
33
|
|
|
30
34
|
const mergedPlaylinks = $derived(mergePlaylinks(playlinks))
|
|
31
35
|
const categorizedPlaylinks = $derived(categorizePlaylinks(playlinks))
|
|
32
|
-
const
|
|
36
|
+
const sortedPlaylinks: SortedPlaylinks = $derived(categorize ? sortCategories(categorizedPlaylinks) : [['' as Category, mergedPlaylinks]])
|
|
33
37
|
|
|
34
38
|
// Grid turns into a list when the playlinks container is small enough
|
|
35
39
|
// It is also a list by default if a display ad is present, as that would
|
|
@@ -37,7 +41,7 @@
|
|
|
37
41
|
const list = $derived(outerWidth < 500 || !!displayAd || mergedPlaylinks.length === 1)
|
|
38
42
|
|
|
39
43
|
function onclick(playlink: string): void {
|
|
40
|
-
track(isModal ? TrackingEvent.TitleModalPlaylinkClick : TrackingEvent.
|
|
44
|
+
track(isModal ? TrackingEvent.TitleModalPlaylinkClick : TrackingEvent.InjectionPopoverPlaylinkClick, title, { playlink, type })
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
function categorizePlaylinks(playlinks: PlaylinkData[]): CategorizedPlaylinks {
|
|
@@ -86,13 +90,13 @@
|
|
|
86
90
|
</div>
|
|
87
91
|
{/if}
|
|
88
92
|
|
|
89
|
-
{#each
|
|
93
|
+
{#each sortedPlaylinks as [category, playlinks]}
|
|
90
94
|
{#if category && playlinks.length}
|
|
91
95
|
<div class="heading category" use:heading={4}>{t(`Category: ${category}`)}</div>
|
|
92
96
|
{/if}
|
|
93
97
|
|
|
94
98
|
<div class="playlinks" data-testid="category-{category}" class:list bind:clientWidth={outerWidth}>
|
|
95
|
-
{#each playlinks as playlink, index}
|
|
99
|
+
{#each playlinks.slice(0, limited ? limit : playlinks.length) as playlink, index}
|
|
96
100
|
<Playlink {playlink} onclick={() => onclick(playlink.name)} />
|
|
97
101
|
|
|
98
102
|
<!-- A fake highlighted playlink as part of the display ad, to be shown after the first playlink -->
|
|
@@ -101,6 +105,14 @@
|
|
|
101
105
|
{/if}
|
|
102
106
|
{/each}
|
|
103
107
|
</div>
|
|
108
|
+
|
|
109
|
+
{#if playlinks.length > limit && limited}
|
|
110
|
+
<div class="show-more" data-testid="show-more">
|
|
111
|
+
<Button onclick={() => limited = !limited} size="large">
|
|
112
|
+
{t('Show Count More').replace('[[count]]', (playlinks.length - limit).toString())}
|
|
113
|
+
</Button>
|
|
114
|
+
</div>
|
|
115
|
+
{/if}
|
|
104
116
|
{/each}
|
|
105
117
|
|
|
106
118
|
<style lang="scss">
|
|
@@ -152,6 +164,10 @@
|
|
|
152
164
|
}
|
|
153
165
|
}
|
|
154
166
|
|
|
167
|
+
.show-more {
|
|
168
|
+
margin-top: margin(0.5);
|
|
169
|
+
}
|
|
170
|
+
|
|
155
171
|
.empty {
|
|
156
172
|
padding: margin(0.75);
|
|
157
173
|
margin-top: margin(0.5);
|
|
@@ -194,11 +194,12 @@
|
|
|
194
194
|
background: theme(popover-background, light);
|
|
195
195
|
box-shadow: theme(popover-shadow, shadow-large);
|
|
196
196
|
max-height: min(var(--max-height, 100vh), margin(36)); // --max-height defaults to 100vh to make it unused while it's not defined
|
|
197
|
-
scrollbar-width: thin;
|
|
198
197
|
overflow-y: overlay;
|
|
199
198
|
overflow-x: hidden;
|
|
200
199
|
view-transition-name: playpilot-title-content;
|
|
201
200
|
|
|
201
|
+
@include styled-scrollbar;
|
|
202
|
+
|
|
202
203
|
.has-bubble & {
|
|
203
204
|
border-top-left-radius: 0;
|
|
204
205
|
border-top-right-radius: 0;
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
<TitlePoster {title} onload={() => posterLoaded = true} lazy={false} />
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
|
-
<div class="heading" use:heading={2} class:truncate={small} id="
|
|
59
|
+
<div class="heading" use:heading={2} class:truncate={small} id="heading">{title.title}</div>
|
|
60
60
|
|
|
61
61
|
<div class="info">
|
|
62
62
|
<div class="imdb">
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import RoundButton from '../components/RoundButton.svelte'
|
|
17
17
|
import SkeletonText from '../components/SkeletonText.svelte'
|
|
18
18
|
import Title from '../components/Title.svelte'
|
|
19
|
-
import
|
|
19
|
+
import InjectionPopover from '../components/InjectionPopover.svelte'
|
|
20
20
|
import Tooltip from '../components/Tooltip.svelte'
|
|
21
21
|
import ExploreRouter from '../components/Explore/ExploreRouter.svelte'
|
|
22
22
|
import TitlesRail from '../components/Rails/TitlesRail.svelte'
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
</div>
|
|
70
70
|
|
|
71
71
|
<div>
|
|
72
|
-
<h3>
|
|
72
|
+
<h3>InjectionPopover.svelte</h3>
|
|
73
73
|
<div class="item">
|
|
74
74
|
<div style:height="500px"></div>
|
|
75
|
-
<
|
|
75
|
+
<InjectionPopover data={title} event={{} as MouseEvent} />
|
|
76
76
|
</div>
|
|
77
77
|
</div>
|
|
78
78
|
</div>
|
package/src/tests/helpers.js
CHANGED
|
@@ -67,20 +67,20 @@ describe('$lib/api/titles', () => {
|
|
|
67
67
|
expect(api).toHaveBeenCalledWith('/titles/browse?api-token=some-token®ion=be&language=en-US&include_count=false')
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
-
it('Should
|
|
70
|
+
it('Should fetch for global region if user region is not supported', async () => {
|
|
71
71
|
vi.mocked(isUserRegionSupported).mockResolvedValue(false)
|
|
72
72
|
|
|
73
73
|
await fetchTitles()
|
|
74
74
|
|
|
75
|
-
expect(api).
|
|
75
|
+
expect(api).toHaveBeenCalledWith(expect.stringContaining('region=gl'))
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
it('Should fetch if region is unsupported but region was given as null', async () => {
|
|
78
|
+
it('Should fetch without region if region is unsupported but region was given as null', async () => {
|
|
79
79
|
vi.mocked(isUserRegionSupported).mockResolvedValue(false)
|
|
80
80
|
|
|
81
81
|
await fetchTitles({ region: null })
|
|
82
82
|
|
|
83
|
-
expect(api).
|
|
83
|
+
expect(api).not.toHaveBeenCalledWith(expect.stringContaining('region=gl'))
|
|
84
84
|
})
|
|
85
85
|
})
|
|
86
86
|
|
|
@@ -1,11 +1,12 @@
|
|
|
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, isAvailableAsManualInjection, filterRemovedAndInactiveInjections, isEquivalentInjection, filterInvalidInTextInjections, filterInvalidAfterArticleInjections, isValidInjection, isValidPlaylinkType, removePlayPilotTitleLinks } from '$lib/injection'
|
|
4
|
+
import { injectLinksInDocument, clearLinkInjections, clearLinkInjection, isAvailableAsManualInjection, filterRemovedAndInactiveInjections, isEquivalentInjection, filterInvalidInTextInjections, filterInvalidAfterArticleInjections, isValidInjection, isValidPlaylinkType, removePlayPilotTitleLinks, hasValidTypeData } from '$lib/injection'
|
|
5
5
|
import { mount, unmount } from 'svelte'
|
|
6
6
|
import { fakeFetch, generateInjection } from '../helpers'
|
|
7
7
|
import { openModalForInjectedLink } from '$lib/modal'
|
|
8
8
|
import { getLinkInjectionElements } from '$lib/injectionElements'
|
|
9
|
+
import { participants } from '$lib/fakeData'
|
|
9
10
|
import { titleUrl } from '$lib/routes'
|
|
10
11
|
|
|
11
12
|
vi.mock('svelte', () => ({
|
|
@@ -572,7 +573,7 @@ describe('injection.ts', () => {
|
|
|
572
573
|
})
|
|
573
574
|
|
|
574
575
|
it('Should remove all popovers that may not have been removed properly in previous destroy attempts', async () => {
|
|
575
|
-
document.body.innerHTML = '<div data-playpilot-
|
|
576
|
+
document.body.innerHTML = '<div data-playpilot-injection-popover></div> <p>This is a sentence with an injection.</p>'
|
|
576
577
|
|
|
577
578
|
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
578
579
|
const injection = generateInjection('This is a sentence with an injection.', 'an injection')
|
|
@@ -587,7 +588,7 @@ describe('injection.ts', () => {
|
|
|
587
588
|
await fireEvent.mouseMove(document.body)
|
|
588
589
|
vi.advanceTimersByTime(200)
|
|
589
590
|
|
|
590
|
-
expect(document.querySelectorAll('[data-playpilot-
|
|
591
|
+
expect(document.querySelectorAll('[data-playpilot-injection-popover]')).toHaveLength(0)
|
|
591
592
|
})
|
|
592
593
|
|
|
593
594
|
it('Should inject links of the same phrase when multiple are present', () => {
|
|
@@ -951,6 +952,20 @@ describe('injection.ts', () => {
|
|
|
951
952
|
expect(document.querySelector('a')?.closest('[data-playpilot-injection-key]')).toBeTruthy()
|
|
952
953
|
})
|
|
953
954
|
|
|
955
|
+
describe('Participants', () => {
|
|
956
|
+
it('Should inject participants just as a title', () => {
|
|
957
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
958
|
+
|
|
959
|
+
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
960
|
+
const injection = { ...generateInjection('This is a sentence with an injection.', 'an injection'), type: 'participant', participant_details: participants[0] }
|
|
961
|
+
|
|
962
|
+
// @ts-ignore
|
|
963
|
+
injectLinksInDocument(elements, { aiInjections: [injection], manualInjections: [] })
|
|
964
|
+
|
|
965
|
+
expect(document.querySelector('[data-playpilot-injection-key]')).toBeTruthy()
|
|
966
|
+
})
|
|
967
|
+
})
|
|
968
|
+
|
|
954
969
|
describe('config.open_tpi_links_in_explore', () => {
|
|
955
970
|
beforeEach(() => {
|
|
956
971
|
window.PlayPilotLinkInjections.config = {
|
|
@@ -1168,6 +1183,32 @@ describe('injection.ts', () => {
|
|
|
1168
1183
|
})
|
|
1169
1184
|
})
|
|
1170
1185
|
|
|
1186
|
+
describe('hasValidTypeData', () => {
|
|
1187
|
+
it('Should return true if injection has type title and has title_details', () => {
|
|
1188
|
+
// @ts-ignore
|
|
1189
|
+
expect(hasValidTypeData({ type: 'title', title_details: {} })).toBe(true)
|
|
1190
|
+
})
|
|
1191
|
+
|
|
1192
|
+
it('Should return true if injection has type participant and has participant_details', () => {
|
|
1193
|
+
// @ts-ignore
|
|
1194
|
+
expect(hasValidTypeData({ type: 'participant', participant_details: {} })).toBe(true)
|
|
1195
|
+
})
|
|
1196
|
+
|
|
1197
|
+
it('Should return false if injection has type title but has no title_details', () => {
|
|
1198
|
+
// @ts-ignore
|
|
1199
|
+
expect(hasValidTypeData({ type: 'title', title_details: null })).toBe(false)
|
|
1200
|
+
// @ts-ignore
|
|
1201
|
+
expect(hasValidTypeData({ type: 'title' })).toBe(false)
|
|
1202
|
+
})
|
|
1203
|
+
|
|
1204
|
+
it('Should return false if injection has type participant but has no participant_details', () => {
|
|
1205
|
+
// @ts-ignore
|
|
1206
|
+
expect(hasValidTypeData({ type: 'participant', participant_details: null })).toBe(false)
|
|
1207
|
+
// @ts-ignore
|
|
1208
|
+
expect(hasValidTypeData({ type: 'participant' })).toBe(false)
|
|
1209
|
+
})
|
|
1210
|
+
})
|
|
1211
|
+
|
|
1171
1212
|
describe('removePlayPilotTitleLinks', () => {
|
|
1172
1213
|
it('Should remove existing title links', () => {
|
|
1173
1214
|
document.body.innerHTML = `
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
|
2
2
|
import { linkInjections } from '$lib/fakeData'
|
|
3
3
|
import { mount, unmount } from 'svelte'
|
|
4
4
|
import { clearCurrentlyHoveredInjection, currentlyHoveredInjection, destroyLinkPopover, isPopoverActive, openPopoverForInjectedLink } from '$lib/popover'
|
|
5
|
-
import
|
|
5
|
+
import InjectionPopover from '../../routes/components/InjectionPopover.svelte'
|
|
6
6
|
import { waitFor } from '@testing-library/svelte'
|
|
7
7
|
|
|
8
8
|
vi.mock('svelte', () => ({
|
|
@@ -22,17 +22,17 @@ describe('popover.js', () => {
|
|
|
22
22
|
describe('openPopoverForInjectedLink', () => {
|
|
23
23
|
it('Should mount popover', async () => {
|
|
24
24
|
// @ts-ignore
|
|
25
|
-
openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections)
|
|
25
|
+
openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections[0])
|
|
26
26
|
|
|
27
27
|
await waitFor(() => {
|
|
28
|
-
expect(mount).toHaveBeenCalledWith(
|
|
28
|
+
expect(mount).toHaveBeenCalledWith(InjectionPopover, expect.any(Object))
|
|
29
29
|
expect(currentlyHoveredInjection).toBeTruthy()
|
|
30
30
|
})
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
it('Should not mount popover if user is no longer hovering after delay', async () => {
|
|
34
34
|
// @ts-ignore
|
|
35
|
-
openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections)
|
|
35
|
+
openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections[0])
|
|
36
36
|
|
|
37
37
|
await new Promise(res => setTimeout(res, 50))
|
|
38
38
|
|
|
@@ -46,19 +46,19 @@ describe('popover.js', () => {
|
|
|
46
46
|
|
|
47
47
|
describe('destroyLinkPopover', () => {
|
|
48
48
|
it('Should not call unmount but still remove potential popover elements if no active popover is set', async () => {
|
|
49
|
-
document.body.innerHTML = '<div data-playpilot-
|
|
49
|
+
document.body.innerHTML = '<div data-playpilot-injection-popover></div> <div data-playpilot-injection-popover></div>'
|
|
50
50
|
|
|
51
51
|
destroyLinkPopover()
|
|
52
52
|
|
|
53
53
|
expect(unmount).not.toHaveBeenCalled()
|
|
54
|
-
expect(document.querySelectorAll('[data-playpilot-
|
|
54
|
+
expect(document.querySelectorAll('[data-playpilot-injection-popover]')).toHaveLength(0)
|
|
55
55
|
})
|
|
56
56
|
})
|
|
57
57
|
|
|
58
58
|
describe('clearCurrentlyHoveredInjection', () => {
|
|
59
59
|
it('Should clear currentlyHoveredInjection', async () => {
|
|
60
60
|
// @ts-ignore
|
|
61
|
-
openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections)
|
|
61
|
+
openPopoverForInjectedLink({ currentTarget: document.querySelector('a') }, linkInjections[0])
|
|
62
62
|
|
|
63
63
|
await waitFor(() => expect(currentlyHoveredInjection).toBeTruthy())
|
|
64
64
|
|
|
@@ -233,6 +233,14 @@ describe('$lib/tracking', () => {
|
|
|
233
233
|
}),
|
|
234
234
|
)
|
|
235
235
|
})
|
|
236
|
+
|
|
237
|
+
it('Should not track when a very large amount of events have already been fired', () => {
|
|
238
|
+
window.PlayPilotLinkInjections.tracked_events = new Array(600).fill(1)
|
|
239
|
+
|
|
240
|
+
track('Some event')
|
|
241
|
+
|
|
242
|
+
expect(global.fetch).not.toHaveBeenCalled()
|
|
243
|
+
})
|
|
236
244
|
})
|
|
237
245
|
|
|
238
246
|
describe('setTrackingSids', () => {
|
|
@@ -6,6 +6,7 @@ import { injectLinksInDocument } from '$lib/injection'
|
|
|
6
6
|
import { track } from '$lib/tracking'
|
|
7
7
|
import { generateInjection } from '../../../helpers'
|
|
8
8
|
import { removeImageUrlPrefix } from '$lib/image'
|
|
9
|
+
import { linkInjections as fakeLinkInjections } from '$lib/fakeData'
|
|
9
10
|
|
|
10
11
|
vi.mock('$lib/tracking', () => ({
|
|
11
12
|
track: vi.fn(),
|
|
@@ -200,4 +201,13 @@ describe('EditorItem.svelte', () => {
|
|
|
200
201
|
|
|
201
202
|
expect(getByText('What would you like to report?')).toBeTruthy()
|
|
202
203
|
})
|
|
204
|
+
|
|
205
|
+
it('Should render as participant when type is participant', () => {
|
|
206
|
+
const { container, queryByLabelText, getByText } = render(EditorItem, { linkInjection: fakeLinkInjections[4] })
|
|
207
|
+
|
|
208
|
+
expect(queryByLabelText('Expand')).not.toBeTruthy()
|
|
209
|
+
expect(getByText(fakeLinkInjections[4].participant_details?.name || '-')).toBeTruthy()
|
|
210
|
+
expect(container.querySelector('.poster')).not.toBeTruthy()
|
|
211
|
+
expect(container.querySelector('.placeholder-image')).toBeTruthy()
|
|
212
|
+
})
|
|
203
213
|
})
|
|
@@ -99,6 +99,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
99
99
|
manual: true,
|
|
100
100
|
phrase_before: 'Some',
|
|
101
101
|
phrase_after: 'in a',
|
|
102
|
+
type: 'title',
|
|
102
103
|
})
|
|
103
104
|
})
|
|
104
105
|
|
|
@@ -141,6 +142,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
141
142
|
manual: true,
|
|
142
143
|
phrase_before: '',
|
|
143
144
|
phrase_after: '',
|
|
145
|
+
type: 'title',
|
|
144
146
|
})
|
|
145
147
|
})
|
|
146
148
|
|
|
@@ -183,6 +185,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
183
185
|
manual: true,
|
|
184
186
|
phrase_before: expect.any(String),
|
|
185
187
|
phrase_after: expect.any(String),
|
|
188
|
+
type: 'title',
|
|
186
189
|
})
|
|
187
190
|
})
|
|
188
191
|
|
|
@@ -225,6 +228,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
225
228
|
manual: true,
|
|
226
229
|
phrase_before: expect.any(String),
|
|
227
230
|
phrase_after: expect.any(String),
|
|
231
|
+
type: 'title',
|
|
228
232
|
})
|
|
229
233
|
})
|
|
230
234
|
|