@playpilot/tpi 8.5.10 → 8.6.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 +9 -9
- package/dist/link-injections.js +1 -1
- package/dist/mount.js +6 -6
- package/events.md +3 -0
- package/package.json +1 -1
- package/src/lib/enums/TrackingEvent.ts +1 -0
- package/src/lib/types/explore.d.ts +0 -1
- package/src/routes/components/Explore/ExploreLayout.svelte +14 -17
- package/src/routes/components/Explore/ExploreRouter.svelte +7 -8
- package/src/routes/components/Explore/Routes/ExploreResults.svelte +39 -20
- package/src/routes/components/Modals/RailModal.svelte +11 -5
- package/src/routes/components/Modals/TitlesRailModal.svelte +3 -1
- package/src/tests/lib/api/api.test.js +17 -0
- package/src/tests/main.test.js +4 -0
- package/src/tests/routes/components/Explore/ExploreLayout.test.js +8 -26
- package/src/tests/routes/components/Explore/ExploreRouter.test.js +3 -2
- package/src/tests/routes/components/Explore/Routes/ExploreResults.test.js +13 -25
package/events.md
CHANGED
|
@@ -18,8 +18,10 @@ All events share a common payload:
|
|
|
18
18
|
Events related to titles share an additional set of data (referred to below as `Title`):
|
|
19
19
|
|
|
20
20
|
- `original_title`
|
|
21
|
+
- `title`
|
|
21
22
|
- `title_sid`
|
|
22
23
|
- `title_type`: "movie" or "series"
|
|
24
|
+
- `genres`: An array of all genres for the title
|
|
23
25
|
- `providers`: An array of provider names
|
|
24
26
|
|
|
25
27
|
Events may have additional data in their payload.
|
|
@@ -112,4 +114,5 @@ Event | Action | Info | Payload
|
|
|
112
114
|
`explore_search` | _Fires any time the user searches for something_ | | `query`
|
|
113
115
|
`venus_title_rail_modal_view` | _Fires any time a title is clicked in a rail, opening the rail modal_ | | `rail` (heading key of the rail)
|
|
114
116
|
`venus_title_rail_expand_click` | _Fires any time a title is expanded in a rail directly via a click_ | Does not fire when a title opens automatically on load or navigate | `Title`, `rail` (heading key of the rail)
|
|
117
|
+
`venus_title_rail_set_index` | _Fires when navigating in the titles rail modal, either via arrows, swipe, or clicking titles_ | | `Title`, `index` (index of the new position of the slider)
|
|
115
118
|
`venus_navigate` | _Fires when navigating on the explore page_ | Does not fire on initial load | `route` (the key of the given route)
|
package/package.json
CHANGED
|
@@ -73,6 +73,7 @@ export const TrackingEvent = {
|
|
|
73
73
|
ExploreFetchProvidersFailed: 'venus_fetch_providers_failed',
|
|
74
74
|
ExploreTitleRailModalView: 'venus_title_rail_modal_view',
|
|
75
75
|
ExploreTitleRailExpandClick: 'venus_title_rail_expand_click',
|
|
76
|
+
ExploreTitleRailSetIndex: 'venus_title_rail_set_index',
|
|
76
77
|
ExploreNavigate: 'venus_navigate',
|
|
77
78
|
} as const
|
|
78
79
|
|
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { ExploreFilter } from '$lib/types/filter'
|
|
2
3
|
import { heading } from '$lib/actions/heading'
|
|
3
4
|
import { exploreParentSelector, useExploreRouter } from '$lib/explore'
|
|
4
5
|
import { t } from '$lib/localization'
|
|
5
|
-
import type { ExploreRoute } from '$lib/types/explore'
|
|
6
6
|
import { track } from '$lib/tracking'
|
|
7
7
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
8
8
|
import { fetchAds } from '$lib/api/ads'
|
|
9
9
|
import { onMount, type Snippet } from 'svelte'
|
|
10
10
|
import Search from './Filter/Search.svelte'
|
|
11
|
-
import
|
|
11
|
+
import Filter from './Filter/Filter.svelte'
|
|
12
12
|
|
|
13
13
|
interface Props {
|
|
14
|
-
|
|
15
|
-
currentRoute: ExploreRoute
|
|
16
|
-
navigate?: (route: ExploreRoute) => void
|
|
14
|
+
navigate?: (key: string) => void
|
|
17
15
|
searchQuery?: string
|
|
16
|
+
filter?: ExploreFilter
|
|
18
17
|
children: Snippet
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
let {
|
|
20
|
+
let { navigate = () => null, searchQuery = $bindable(''), filter = $bindable({}), children }: Props = $props()
|
|
22
21
|
|
|
23
22
|
let element: HTMLElement | null = null
|
|
24
23
|
let height: string | null = $state(null)
|
|
25
|
-
let clientWidth: number = $state(
|
|
24
|
+
let clientWidth: number = $state(window.innerWidth)
|
|
26
25
|
|
|
27
26
|
$effect(() => {
|
|
28
27
|
// Set the height of this element to match that of it's container. That way we can
|
|
@@ -54,18 +53,14 @@
|
|
|
54
53
|
</div>
|
|
55
54
|
</div>
|
|
56
55
|
|
|
56
|
+
<Search oninput={(query) => searchQuery = query} />
|
|
57
|
+
|
|
57
58
|
{#if useExploreRouter()}
|
|
58
|
-
<div class="
|
|
59
|
-
{
|
|
60
|
-
<Button variant="border" size="large" active={currentRoute.key === route.key} onclick={() => navigate(route)}>
|
|
61
|
-
{t(route.label)}
|
|
62
|
-
</Button>
|
|
63
|
-
{/each}
|
|
59
|
+
<div class="filter">
|
|
60
|
+
<Filter {filter} limit={clientWidth < 500} showSorting={!searchQuery} onchange={() => navigate('results')} />
|
|
64
61
|
</div>
|
|
65
62
|
{/if}
|
|
66
63
|
|
|
67
|
-
<Search oninput={(query) => searchQuery = query} />
|
|
68
|
-
|
|
69
64
|
{@render children()}
|
|
70
65
|
</div>
|
|
71
66
|
|
|
@@ -120,9 +115,11 @@
|
|
|
120
115
|
line-height: 1.35;
|
|
121
116
|
}
|
|
122
117
|
|
|
123
|
-
.
|
|
118
|
+
.filter {
|
|
124
119
|
display: flex;
|
|
120
|
+
flex-direction: column;
|
|
125
121
|
gap: margin(0.5);
|
|
126
|
-
|
|
122
|
+
width: 100%;
|
|
123
|
+
margin: margin(0.5) 0 margin(1.5);
|
|
127
124
|
}
|
|
128
125
|
</style>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ExploreRoute } from '$lib/types/explore'
|
|
3
|
+
import type { ExploreFilter } from '$lib/types/filter'
|
|
3
4
|
import { useExploreRouter } from '$lib/explore'
|
|
4
5
|
import { track } from '$lib/tracking'
|
|
5
|
-
import { t } from '$lib/localization'
|
|
6
6
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
7
7
|
import ExploreHome from './Routes/ExploreHome.svelte'
|
|
8
8
|
import ExploreResults from './Routes/ExploreResults.svelte'
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
const routes: ExploreRoute[] = [
|
|
12
12
|
{
|
|
13
13
|
key: 'results',
|
|
14
|
-
label: t('Explore'),
|
|
15
14
|
component: ExploreResults,
|
|
16
15
|
},
|
|
17
16
|
]
|
|
@@ -19,13 +18,13 @@
|
|
|
19
18
|
if (useExploreRouter()) {
|
|
20
19
|
routes.unshift({
|
|
21
20
|
key: 'home',
|
|
22
|
-
label: t('Home'),
|
|
23
21
|
component: ExploreHome,
|
|
24
22
|
})
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
let currentRoute: ExploreRoute = $state(routes[0])
|
|
28
26
|
let searchQuery: string = $state('')
|
|
27
|
+
let filter: ExploreFilter = $state({})
|
|
29
28
|
|
|
30
29
|
const CurrentRouteComponent = $derived(currentRoute.component)
|
|
31
30
|
|
|
@@ -33,13 +32,13 @@
|
|
|
33
32
|
if (searchQuery) currentRoute = routes.find(route => route.key === 'results')!
|
|
34
33
|
})
|
|
35
34
|
|
|
36
|
-
function navigate(
|
|
37
|
-
currentRoute = route
|
|
35
|
+
function navigate(key: string): void {
|
|
36
|
+
currentRoute = routes.find(route => route.key === key) || routes[0]
|
|
38
37
|
|
|
39
|
-
track(TrackingEvent.ExploreNavigate, null, { route:
|
|
38
|
+
track(TrackingEvent.ExploreNavigate, null, { route: currentRoute.key })
|
|
40
39
|
}
|
|
41
40
|
</script>
|
|
42
41
|
|
|
43
|
-
<ExploreLayout {
|
|
44
|
-
<CurrentRouteComponent {searchQuery} />
|
|
42
|
+
<ExploreLayout {navigate} bind:searchQuery bind:filter>
|
|
43
|
+
<CurrentRouteComponent {searchQuery} {filter} {navigate} />
|
|
45
44
|
</ExploreLayout>
|
|
@@ -7,28 +7,31 @@
|
|
|
7
7
|
import type { ExploreFilter } from '$lib/types/filter'
|
|
8
8
|
import type { TitleData } from '$lib/types/title'
|
|
9
9
|
import { hasConsentedTo } from '$lib/consent'
|
|
10
|
-
import {
|
|
10
|
+
import { useExploreRouter } from '$lib/explore'
|
|
11
11
|
import { t } from '$lib/localization'
|
|
12
|
+
import { trackViaPixel } from '@playpilot/retargeting-tracking'
|
|
12
13
|
import Button from '../../Button.svelte'
|
|
13
14
|
import GridTitle from '../../GridTitle.svelte'
|
|
14
15
|
import GridTitleSkeleton from '../../GridTitleSkeleton.svelte'
|
|
15
16
|
import ListTitle from '../../ListTitle.svelte'
|
|
16
17
|
import ListTitleSkeleton from '../../ListTitleSkeleton.svelte'
|
|
17
|
-
import Filter from '../Filter/Filter.svelte'
|
|
18
18
|
import Empty from '../Empty.svelte'
|
|
19
|
+
import IconArrow from '../../Icons/IconArrow.svelte'
|
|
19
20
|
|
|
20
21
|
interface Props {
|
|
21
|
-
searchQuery?: string
|
|
22
|
+
searchQuery?: string,
|
|
23
|
+
filter?: ExploreFilter,
|
|
24
|
+
navigate?: (key: string) => void
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
const { searchQuery = '' }: Props = $props()
|
|
25
|
-
|
|
26
|
-
const filter: ExploreFilter = $state({})
|
|
27
|
+
const { searchQuery = '', filter = {}, navigate = () => null }: Props = $props()
|
|
27
28
|
|
|
29
|
+
// svelte-ignore non_reactive_update
|
|
30
|
+
let page = 1
|
|
28
31
|
let titles: TitleData[] = $state([])
|
|
29
|
-
let page = $state(1)
|
|
30
32
|
let debounce: ReturnType<typeof setTimeout> | null = null
|
|
31
33
|
let latestRequestId = 0
|
|
34
|
+
let lastRequestAsString = JSON.stringify(filter) + searchQuery
|
|
32
35
|
let promise = $state(getTitlesForFilter())
|
|
33
36
|
let width = $state(0)
|
|
34
37
|
|
|
@@ -46,6 +49,10 @@
|
|
|
46
49
|
if (searchQuery || latestRequestId != 0) search(searchQuery)
|
|
47
50
|
})
|
|
48
51
|
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (filter) setFilter()
|
|
54
|
+
})
|
|
55
|
+
|
|
49
56
|
async function getTitlesForFilter(): Promise<APIPaginatedResult<TitleData>> {
|
|
50
57
|
latestRequestId += 1
|
|
51
58
|
const requestId = latestRequestId
|
|
@@ -75,6 +82,8 @@
|
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
async function search(query: string): Promise<void> {
|
|
85
|
+
if (!isNewRequest()) return
|
|
86
|
+
|
|
78
87
|
if (debounce) clearTimeout(debounce)
|
|
79
88
|
|
|
80
89
|
debounce = setTimeout(() => {
|
|
@@ -89,6 +98,8 @@
|
|
|
89
98
|
}
|
|
90
99
|
|
|
91
100
|
function setFilter(): void {
|
|
101
|
+
if (!isNewRequest()) return
|
|
102
|
+
|
|
92
103
|
resetTitles()
|
|
93
104
|
promise = getTitlesForFilter()
|
|
94
105
|
}
|
|
@@ -109,13 +120,28 @@
|
|
|
109
120
|
openModal({ event, data: title })
|
|
110
121
|
track(TrackingEvent.ExploreTitleClick, title)
|
|
111
122
|
}
|
|
123
|
+
|
|
124
|
+
function clearFilter(): void {
|
|
125
|
+
for (const key of Object.keys(filter)) {
|
|
126
|
+
delete filter[key]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function isNewRequest(): boolean {
|
|
131
|
+
const requestAsString = JSON.stringify(filter) + searchQuery
|
|
132
|
+
if (requestAsString === lastRequestAsString) return false
|
|
133
|
+
|
|
134
|
+
lastRequestAsString = requestAsString
|
|
135
|
+
return true
|
|
136
|
+
}
|
|
112
137
|
</script>
|
|
113
138
|
|
|
114
|
-
{#
|
|
115
|
-
<
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
139
|
+
{#if useExploreRouter()}
|
|
140
|
+
<Button variant="link" onclick={() => { navigate('home'); clearFilter() }}>
|
|
141
|
+
<IconArrow direction="left" />
|
|
142
|
+
{t('Home')}
|
|
143
|
+
</Button>
|
|
144
|
+
{/if}
|
|
119
145
|
|
|
120
146
|
<div class="titles" class:grid role="main" style:--grid-columns={gridColumns} bind:clientWidth={width} data-testid="explore-results">
|
|
121
147
|
{#each titles as title}
|
|
@@ -148,14 +174,6 @@
|
|
|
148
174
|
{/await}
|
|
149
175
|
|
|
150
176
|
<style lang="scss">
|
|
151
|
-
.filter {
|
|
152
|
-
display: flex;
|
|
153
|
-
flex-direction: column;
|
|
154
|
-
gap: margin(0.5);
|
|
155
|
-
width: 100%;
|
|
156
|
-
margin: margin(0.5) 0 margin(2);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
177
|
.titles {
|
|
160
178
|
--playpilot-list-item-padding: 0;
|
|
161
179
|
--playpilot-list-item-background: transparent;
|
|
@@ -164,6 +182,7 @@
|
|
|
164
182
|
display: flex;
|
|
165
183
|
flex-direction: column;
|
|
166
184
|
gap: margin(0.5);
|
|
185
|
+
margin-top: margin(1.5);
|
|
167
186
|
|
|
168
187
|
&.grid {
|
|
169
188
|
display: grid;
|
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
interface Props {
|
|
12
12
|
items: Record<string, any>[]
|
|
13
13
|
initialIndex?: number
|
|
14
|
+
onchange?: (index: number) => void
|
|
14
15
|
each: Snippet<[item: any, currentIndex: number]>
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const { items, initialIndex = 0, each }: Props = $props()
|
|
18
|
+
const { items, initialIndex = 0, onchange = () => null, each }: Props = $props()
|
|
18
19
|
|
|
19
20
|
const transitionDuration = 300
|
|
20
21
|
|
|
@@ -27,6 +28,11 @@
|
|
|
27
28
|
requestAnimationFrame(() => initialized = true)
|
|
28
29
|
})
|
|
29
30
|
})
|
|
31
|
+
|
|
32
|
+
function setSliderIndex(index: number): void {
|
|
33
|
+
onchange(index)
|
|
34
|
+
slider.setIndex(index)
|
|
35
|
+
}
|
|
30
36
|
</script>
|
|
31
37
|
|
|
32
38
|
<Modal blur>
|
|
@@ -37,7 +43,7 @@
|
|
|
37
43
|
{#each items as item, index}
|
|
38
44
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
39
45
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
40
|
-
<div class="item" class:active={index === currentIndex} onclick={() =>
|
|
46
|
+
<div class="item" class:active={index === currentIndex} onclick={() => setSliderIndex(index)}>
|
|
41
47
|
{#if Math.abs(index - currentIndex) === 1 || currentIndex === index}
|
|
42
48
|
<div class="content" transition:fade={{ duration: initialized ? transitionDuration : 0 }}>
|
|
43
49
|
{@render each(item, currentIndex)}
|
|
@@ -47,15 +53,15 @@
|
|
|
47
53
|
{/each}
|
|
48
54
|
{/snippet}
|
|
49
55
|
|
|
50
|
-
{#snippet controls({
|
|
56
|
+
{#snippet controls({ currentIndex, reachedEnd })}
|
|
51
57
|
{#if currentIndex > 0}
|
|
52
|
-
<button class="arrow left" onclick={() =>
|
|
58
|
+
<button class="arrow left" onclick={() => setSliderIndex(currentIndex - 1)} aria-label="Previous" aria-live="polite">
|
|
53
59
|
<IconArrow size={21} direction="left" title="Previous" />
|
|
54
60
|
</button>
|
|
55
61
|
{/if}
|
|
56
62
|
|
|
57
63
|
{#if !reachedEnd}
|
|
58
|
-
<button class="arrow right" onclick={() =>
|
|
64
|
+
<button class="arrow right" onclick={() => setSliderIndex(currentIndex + 1)} aria-label="Next" aria-live="polite">
|
|
59
65
|
<IconArrow size={21} title="Next" />
|
|
60
66
|
</button>
|
|
61
67
|
{/if}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
3
|
+
import { track } from '$lib/tracking'
|
|
2
4
|
import type { TitleData } from '$lib/types/title'
|
|
3
5
|
import Title from '../Title.svelte'
|
|
4
6
|
import RailModal from './RailModal.svelte'
|
|
@@ -11,7 +13,7 @@
|
|
|
11
13
|
const { titles, initialIndex = 0 }: Props = $props()
|
|
12
14
|
</script>
|
|
13
15
|
|
|
14
|
-
<RailModal items={titles} {initialIndex}>
|
|
16
|
+
<RailModal items={titles} {initialIndex} onchange={(index) => track(TrackingEvent.ExploreTitleRailSetIndex, titles[index], { index })}>
|
|
15
17
|
{#snippet each(title, currentIndex)}
|
|
16
18
|
<Title {title} useVideoBackground={title.sid === titles[currentIndex]?.sid} />
|
|
17
19
|
{/snippet}
|
|
@@ -67,6 +67,23 @@ describe('$lib/api/api', () => {
|
|
|
67
67
|
expect(global.fetch).toHaveBeenCalledTimes(1)
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
+
it('Should fetch twice once for the same path if the first request failed', async () => {
|
|
71
|
+
fakeFetch({ ok: false })
|
|
72
|
+
try {
|
|
73
|
+
await api('/some-path')
|
|
74
|
+
} catch {
|
|
75
|
+
// Ignore me
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
await api('/some-path')
|
|
80
|
+
} catch {
|
|
81
|
+
// Ignore me
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
expect(global.fetch).toHaveBeenCalledTimes(2)
|
|
85
|
+
})
|
|
86
|
+
|
|
70
87
|
it('Should fetch multiple times if multiple requests are made for different paths', async () => {
|
|
71
88
|
await api('/some-path')
|
|
72
89
|
await api('/some-other-path')
|
package/src/tests/main.test.js
CHANGED
|
@@ -38,6 +38,9 @@ describe('main.ts', () => {
|
|
|
38
38
|
vi.resetModules()
|
|
39
39
|
vi.resetAllMocks()
|
|
40
40
|
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
delete window.PlayPilotLinkInjections
|
|
43
|
+
|
|
41
44
|
await import('../main')
|
|
42
45
|
})
|
|
43
46
|
|
|
@@ -48,6 +51,7 @@ describe('main.ts', () => {
|
|
|
48
51
|
describe('initialize', () => {
|
|
49
52
|
it('Should not call setConsent when only a token is passed', () => {
|
|
50
53
|
window.PlayPilotLinkInjections.initialize({ token: 'a' })
|
|
54
|
+
console.log(window.PlayPilotLinkInjections)
|
|
51
55
|
|
|
52
56
|
expect(setConsent).not.toHaveBeenCalled()
|
|
53
57
|
|
|
@@ -10,18 +10,6 @@ import { fetchAds } from '$lib/api/ads'
|
|
|
10
10
|
|
|
11
11
|
const children = createRawSnippet(() => ({ render: () => '<div></div>' }))
|
|
12
12
|
|
|
13
|
-
const routes = [
|
|
14
|
-
{
|
|
15
|
-
key: 'route-1',
|
|
16
|
-
label: 'Route 1',
|
|
17
|
-
component: {},
|
|
18
|
-
}, {
|
|
19
|
-
key: 'route-2',
|
|
20
|
-
label: 'Route 2',
|
|
21
|
-
component: {},
|
|
22
|
-
},
|
|
23
|
-
]
|
|
24
|
-
|
|
25
13
|
vi.mock('$lib/tracking', () => ({
|
|
26
14
|
track: vi.fn(),
|
|
27
15
|
}))
|
|
@@ -38,20 +26,14 @@ describe('ExploreLayout.svelte', () => {
|
|
|
38
26
|
window.PlayPilotLinkInjections = { config: { explore_use_router: true } }
|
|
39
27
|
})
|
|
40
28
|
|
|
41
|
-
it('Should
|
|
42
|
-
const { getByText } = render(ExploreLayout, { routes, currentRoute: routes[0], children })
|
|
43
|
-
|
|
44
|
-
expect(getByText('Route 1')).toBeTruthy()
|
|
45
|
-
expect(getByText('Route 2')).toBeTruthy()
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('Should call navigate when route is clicked', async () => {
|
|
29
|
+
it('Should call navigate when filter is changed', async () => {
|
|
49
30
|
const navigate = vi.fn()
|
|
50
|
-
const { getByText } = render(ExploreLayout, {
|
|
31
|
+
const { getByText } = render(ExploreLayout, { navigate, children })
|
|
51
32
|
|
|
52
|
-
await fireEvent.click(getByText('
|
|
33
|
+
await fireEvent.click(getByText('Genres'))
|
|
34
|
+
await fireEvent.click(getByText('Drama'))
|
|
53
35
|
|
|
54
|
-
expect(navigate).toHaveBeenCalledWith(
|
|
36
|
+
expect(navigate).toHaveBeenCalledWith('results')
|
|
55
37
|
})
|
|
56
38
|
|
|
57
39
|
it('Should should set its height if parent has height', async () => {
|
|
@@ -59,7 +41,7 @@ describe('ExploreLayout.svelte', () => {
|
|
|
59
41
|
|
|
60
42
|
const { container } = render(
|
|
61
43
|
ExploreLayout,
|
|
62
|
-
{
|
|
44
|
+
{ children },
|
|
63
45
|
{ baseElement: /** @type {HTMLElement} */ (document.querySelector(exploreParentSelector)),
|
|
64
46
|
})
|
|
65
47
|
|
|
@@ -69,7 +51,7 @@ describe('ExploreLayout.svelte', () => {
|
|
|
69
51
|
})
|
|
70
52
|
|
|
71
53
|
it('Should call tracking event and fetchAds on mount', () => {
|
|
72
|
-
render(ExploreLayout, {
|
|
54
|
+
render(ExploreLayout, { children })
|
|
73
55
|
|
|
74
56
|
expect(track).toHaveBeenCalledWith(TrackingEvent.ExplorePageView)
|
|
75
57
|
expect(fetchAds).toHaveBeenCalled()
|
|
@@ -79,7 +61,7 @@ describe('ExploreLayout.svelte', () => {
|
|
|
79
61
|
// @ts-ignore
|
|
80
62
|
window.PlayPilotLinkInjections.ads = ['a']
|
|
81
63
|
|
|
82
|
-
render(ExploreLayout, {
|
|
64
|
+
render(ExploreLayout, { children })
|
|
83
65
|
|
|
84
66
|
expect(fetchAds).not.toHaveBeenCalled()
|
|
85
67
|
})
|
|
@@ -23,10 +23,11 @@ describe('ExploreRouter.svelte', () => {
|
|
|
23
23
|
expect(getByTestId('explore-home')).toBeTruthy()
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
it('Should render results
|
|
26
|
+
it('Should render results after navigate', async () => {
|
|
27
27
|
const { getByText, getByTestId } = render(ExploreRouter)
|
|
28
28
|
|
|
29
|
-
await fireEvent.click(getByText('
|
|
29
|
+
await fireEvent.click(getByText('Genres'))
|
|
30
|
+
await fireEvent.click(getByText('Drama'))
|
|
30
31
|
|
|
31
32
|
expect(getByTestId('explore-results')).toBeTruthy()
|
|
32
33
|
})
|
|
@@ -6,6 +6,7 @@ import { fetchTitles } from '$lib/api/titles'
|
|
|
6
6
|
import { title } from '$lib/fakeData'
|
|
7
7
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
8
8
|
import { track } from '$lib/tracking'
|
|
9
|
+
import { clearCache } from '$lib/api/api'
|
|
9
10
|
|
|
10
11
|
vi.mock('$lib/api/titles', () => ({
|
|
11
12
|
fetchTitles: vi.fn(),
|
|
@@ -24,12 +25,14 @@ describe('ExploreResults.svelte', () => {
|
|
|
24
25
|
// @ts-ignore
|
|
25
26
|
window.PlayPilotLinkInjections = {}
|
|
26
27
|
|
|
28
|
+
clearCache()
|
|
27
29
|
vi.resetAllMocks()
|
|
28
30
|
})
|
|
29
31
|
|
|
30
32
|
it('Should call fetchTitles on mount', () => {
|
|
31
33
|
render(ExploreResults)
|
|
32
34
|
|
|
35
|
+
expect(fetchTitles).toHaveBeenCalledTimes(1)
|
|
33
36
|
expect(fetchTitles).toHaveBeenCalledWith({ page_size: 24, page: 1 })
|
|
34
37
|
})
|
|
35
38
|
|
|
@@ -102,13 +105,8 @@ describe('ExploreResults.svelte', () => {
|
|
|
102
105
|
it('Should call fetchTitles with string of comma separated values when filtering with array value', async () => {
|
|
103
106
|
vi.mocked(fetchTitles).mockResolvedValue({ results: [title], next: 'truthy', previous: null })
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
await fireEvent.click(getByText('Genres'))
|
|
108
|
-
await fireEvent.click(getByText('Action'))
|
|
109
|
-
await fireEvent.click(getByText('Adventure'))
|
|
108
|
+
render(ExploreResults, { filter: { genres: { type: 'array', value: ['101', '109'] } } })
|
|
110
109
|
|
|
111
|
-
expect(fetchTitles).toHaveBeenCalledWith({ page: 1, page_size: 24, genres: '101' })
|
|
112
110
|
expect(fetchTitles).toHaveBeenCalledWith({ page: 1, page_size: 24, genres: '101,109' })
|
|
113
111
|
})
|
|
114
112
|
|
|
@@ -137,11 +135,7 @@ describe('ExploreResults.svelte', () => {
|
|
|
137
135
|
it('Should call fetchTitles with min and max values when filtering with range value', async () => {
|
|
138
136
|
vi.mocked(fetchTitles).mockResolvedValue({ results: [title], next: 'truthy', previous: null })
|
|
139
137
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
await fireEvent.click(getByText('IMDb'))
|
|
143
|
-
// @ts-ignore
|
|
144
|
-
await fireEvent.input(container.querySelector('#lower'), { target: { value: 2 } })
|
|
138
|
+
render(ExploreResults, { filter: { imdb_score: { type: 'range', value: [2, 10] } } })
|
|
145
139
|
|
|
146
140
|
await waitFor(() => {
|
|
147
141
|
expect(fetchTitles).toHaveBeenCalledWith({ page: 1, page_size: 24, imdb_score_min: 2, imdb_score_max: 10 })
|
|
@@ -151,15 +145,12 @@ describe('ExploreResults.svelte', () => {
|
|
|
151
145
|
it('Should call fetchTitles with multiple values when given', async () => {
|
|
152
146
|
vi.mocked(fetchTitles).mockResolvedValue({ results: [title], next: 'truthy', previous: null })
|
|
153
147
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
await fireEvent.click(getByText('IMDb'))
|
|
161
|
-
// @ts-ignore
|
|
162
|
-
await fireEvent.input(container.querySelector('#lower'), { target: { value: 2 } })
|
|
148
|
+
render(ExploreResults, {
|
|
149
|
+
filter: {
|
|
150
|
+
genres: { type: 'array', value: ['101', '109'] },
|
|
151
|
+
imdb_score: { type: 'range', value: [2, 10] },
|
|
152
|
+
},
|
|
153
|
+
})
|
|
163
154
|
|
|
164
155
|
await waitFor(() => {
|
|
165
156
|
expect(fetchTitles).toHaveBeenCalledWith({ page: 1, page_size: 24, genres: '101,109', imdb_score_min: 2, imdb_score_max: 10 })
|
|
@@ -169,10 +160,7 @@ describe('ExploreResults.svelte', () => {
|
|
|
169
160
|
it('Should call fetchTitles with string for sorting when sorting is used', async () => {
|
|
170
161
|
vi.mocked(fetchTitles).mockResolvedValue({ results: [title], next: 'truthy', previous: null })
|
|
171
162
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
await fireEvent.click(getByText('Sort by'))
|
|
175
|
-
await fireEvent.click(getByText('New'))
|
|
163
|
+
render(ExploreResults, { filter: { ordering: { type: 'string', value: '-new' } } })
|
|
176
164
|
|
|
177
165
|
expect(fetchTitles).toHaveBeenCalledWith({ page: 1, page_size: 24, ordering: '-new' })
|
|
178
166
|
})
|
|
@@ -204,7 +192,7 @@ describe('ExploreResults.svelte', () => {
|
|
|
204
192
|
})
|
|
205
193
|
|
|
206
194
|
it('Should show error message if api responded with error', async () => {
|
|
207
|
-
vi.mocked(fetchTitles).
|
|
195
|
+
vi.mocked(fetchTitles).mockRejectedValue(null)
|
|
208
196
|
|
|
209
197
|
const { getByText } = render(ExploreResults)
|
|
210
198
|
|