@playpilot/tpi 8.27.1 → 8.28.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/.prettierrc +3 -2
- package/dist/editorial.mount.js +8 -8
- package/dist/link-injections.js +2 -2
- package/dist/mount.js +7 -7
- package/eslint.config.js +0 -1
- package/package.json +1 -1
- package/src/lib/api/externalPages.ts +1 -1
- package/src/lib/explore.ts +2 -1
- package/src/lib/types/config.d.ts +122 -120
- package/src/lib/types/position.d.ts +2 -0
- package/src/lib/types/script.d.ts +4 -2
- package/src/routes/components/Debugger.svelte +1 -1
- package/src/routes/components/Explore/Routes/ExploreModal.svelte +70 -19
- package/src/routes/components/Modals/Modal.svelte +2 -1
- package/src/routes/components/Participant.svelte +160 -160
- package/src/routes/explore/+page.svelte +1 -2
- package/src/tests/lib/api/externalPages.test.js +1 -1
- package/src/tests/lib/explore.test.js +4 -2
- package/src/tests/lib/injection.test.js +1 -1
- package/src/tests/lib/retargeting.test.js +2 -0
- package/src/tests/routes/components/Playlinks/Playlink.test.js +2 -0
- package/src/tests/setup.js +108 -108
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { onMount } from 'svelte'
|
|
3
|
-
import { heading } from '$lib/actions/heading'
|
|
4
|
-
import { fetchTitlesForParticipant } from '$lib/api/participants'
|
|
5
|
-
import { openModal } from '$lib/modal'
|
|
6
|
-
import type { ParticipantData } from '$lib/types/participant'
|
|
7
|
-
import type { TitleData } from '$lib/types/title'
|
|
8
|
-
import ListTitle from './ListTitle.svelte'
|
|
9
|
-
import { t } from '$lib/localization'
|
|
10
|
-
import { isRetargetingAllowed } from '$lib/retargeting'
|
|
11
|
-
import { trackViaPixel } from '@playpilot/retargeting-tracking'
|
|
12
|
-
import { MetaEvent } from '$lib/enums/TrackingEvent'
|
|
13
|
-
|
|
14
|
-
interface Props {
|
|
15
|
-
participant: ParticipantData
|
|
16
|
-
small?: boolean
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const { participant, small = false }: Props = $props()
|
|
20
|
-
|
|
21
|
-
const { name, birth_date, death_date } = $derived(participant)
|
|
22
|
-
|
|
23
|
-
const pageSize = 30
|
|
24
|
-
|
|
25
|
-
let titles: TitleData[] = $state([])
|
|
26
|
-
let page = $state(1)
|
|
27
|
-
let hasMorePages = $state(true)
|
|
28
|
-
let loading = $state(true)
|
|
29
|
-
|
|
30
|
-
onMount(loadMore)
|
|
31
|
-
|
|
32
|
-
if (isRetargetingAllowed()) trackViaPixel(MetaEvent.ParticipantView, { participant: participant.name })
|
|
33
|
-
|
|
34
|
-
function formatDate(dateString: string): string {
|
|
35
|
-
const date = new Date(dateString)
|
|
36
|
-
return date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function loadMore(): Promise<void> {
|
|
40
|
-
loading = true
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const results = await fetchTitlesForParticipant(participant, { page })
|
|
44
|
-
|
|
45
|
-
titles = [...titles, ...results]
|
|
46
|
-
hasMorePages = results?.length >= pageSize
|
|
47
|
-
} catch {
|
|
48
|
-
hasMorePages = false
|
|
49
|
-
} finally {
|
|
50
|
-
loading = false
|
|
51
|
-
page += 1
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
</script>
|
|
55
|
-
|
|
56
|
-
<div class="header" class:small>
|
|
57
|
-
<div class="heading" use:heading={2} id="heading">{name}</div>
|
|
58
|
-
|
|
59
|
-
{#if birth_date}
|
|
60
|
-
<p class="dates">
|
|
61
|
-
{t('Born On')} <strong>{formatDate(birth_date)}</strong>{#if death_date}, {t('Died On')} <strong>{formatDate(death_date)}</strong>{/if}
|
|
62
|
-
</p>
|
|
63
|
-
{/if}
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
<div class="content">
|
|
67
|
-
{#if !small}
|
|
68
|
-
<div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
|
|
69
|
-
{/if}
|
|
70
|
-
|
|
71
|
-
<div class="list">
|
|
72
|
-
{#each titles as title}
|
|
73
|
-
<ListTitle {title} compact={small} onclick={(event) => openModal({ event, data: title })} />
|
|
74
|
-
{/each}
|
|
75
|
-
|
|
76
|
-
{#if loading}
|
|
77
|
-
{#each { length: 6 }}
|
|
78
|
-
<div class="skeleton" data-testid="skeleton"></div>
|
|
79
|
-
{/each}
|
|
80
|
-
{:else if hasMorePages}
|
|
81
|
-
<button class="more" onclick={loadMore}>{t('Show More')}</button>
|
|
82
|
-
{/if}
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
|
|
86
|
-
<style lang="scss">
|
|
87
|
-
.header {
|
|
88
|
-
padding: margin(4) margin(1) margin(2);
|
|
89
|
-
font-family: theme(detail-font-family, font-family);
|
|
90
|
-
font-weight: theme(detail-font-weight, normal);
|
|
91
|
-
font-size: theme(detail-font-size, font-size-base);
|
|
92
|
-
line-height: theme(participant-description-line-height, normal);
|
|
93
|
-
color: theme(detail-text-color, text-color);
|
|
94
|
-
|
|
95
|
-
&.small {
|
|
96
|
-
padding: margin(1);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.heading {
|
|
101
|
-
margin: 0;
|
|
102
|
-
font-family: theme(detail-title-font-family, font-family);
|
|
103
|
-
font-weight: theme(detail-title-font-weight, lighter);
|
|
104
|
-
font-size: theme(detail-title-font-size, margin(1.5));
|
|
105
|
-
line-height: normal;
|
|
106
|
-
font-style: theme(detail-title-font-style, normal);
|
|
107
|
-
|
|
108
|
-
&.subheading {
|
|
109
|
-
margin: 0 0 margin(0.5);
|
|
110
|
-
font-size: theme(detail-title-small-font-size, margin(1.25));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.dates {
|
|
115
|
-
margin: margin(0.5) 0 0;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.content {
|
|
119
|
-
padding: 0 margin(1) margin(1);
|
|
120
|
-
color: theme(detail-text-color, text-color);
|
|
121
|
-
font-family: theme(detail-font-family, font-family);
|
|
122
|
-
font-weight: theme(detail-font-weight, normal);
|
|
123
|
-
font-size: theme(detail-font-size, font-size-base);
|
|
124
|
-
line-height: normal;
|
|
125
|
-
font-style: normal;
|
|
126
|
-
|
|
127
|
-
::view-transition-old(content),
|
|
128
|
-
::view-transition-new(content) {
|
|
129
|
-
height: 100%;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.list {
|
|
134
|
-
display: flex;
|
|
135
|
-
flex-direction: column;
|
|
136
|
-
gap: margin(0.5);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.skeleton {
|
|
140
|
-
min-height: margin(7.25);
|
|
141
|
-
border-radius: theme(playlink-border-radius, border-radius);
|
|
142
|
-
background: theme(detail-background-light, lighter);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.more {
|
|
146
|
-
cursor: pointer;
|
|
147
|
-
appearance: none;
|
|
148
|
-
padding: margin(0.5) margin(1);
|
|
149
|
-
border: 0;
|
|
150
|
-
border-radius: theme(participants-load-more-border-radius, border-radius);
|
|
151
|
-
background: theme(participants-load-more-background, content);
|
|
152
|
-
color: inherit;
|
|
153
|
-
font-size: inherit;
|
|
154
|
-
font-family: inherit;
|
|
155
|
-
|
|
156
|
-
&:hover {
|
|
157
|
-
filter: brightness(1.2);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
</style>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte'
|
|
3
|
+
import { heading } from '$lib/actions/heading'
|
|
4
|
+
import { fetchTitlesForParticipant } from '$lib/api/participants'
|
|
5
|
+
import { openModal } from '$lib/modal'
|
|
6
|
+
import type { ParticipantData } from '$lib/types/participant'
|
|
7
|
+
import type { TitleData } from '$lib/types/title'
|
|
8
|
+
import ListTitle from './ListTitle.svelte'
|
|
9
|
+
import { t } from '$lib/localization'
|
|
10
|
+
import { isRetargetingAllowed } from '$lib/retargeting'
|
|
11
|
+
import { trackViaPixel } from '@playpilot/retargeting-tracking'
|
|
12
|
+
import { MetaEvent } from '$lib/enums/TrackingEvent'
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
participant: ParticipantData
|
|
16
|
+
small?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { participant, small = false }: Props = $props()
|
|
20
|
+
|
|
21
|
+
const { name, birth_date, death_date } = $derived(participant)
|
|
22
|
+
|
|
23
|
+
const pageSize = 30
|
|
24
|
+
|
|
25
|
+
let titles: TitleData[] = $state([])
|
|
26
|
+
let page = $state(1)
|
|
27
|
+
let hasMorePages = $state(true)
|
|
28
|
+
let loading = $state(true)
|
|
29
|
+
|
|
30
|
+
onMount(loadMore)
|
|
31
|
+
|
|
32
|
+
if (isRetargetingAllowed()) trackViaPixel(MetaEvent.ParticipantView, { participant: participant.name })
|
|
33
|
+
|
|
34
|
+
function formatDate(dateString: string): string {
|
|
35
|
+
const date = new Date(dateString)
|
|
36
|
+
return date.toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function loadMore(): Promise<void> {
|
|
40
|
+
loading = true
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const results = await fetchTitlesForParticipant(participant, { page })
|
|
44
|
+
|
|
45
|
+
titles = [...titles, ...results]
|
|
46
|
+
hasMorePages = results?.length >= pageSize
|
|
47
|
+
} catch {
|
|
48
|
+
hasMorePages = false
|
|
49
|
+
} finally {
|
|
50
|
+
loading = false
|
|
51
|
+
page += 1
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<div class="header" class:small>
|
|
57
|
+
<div class="heading" use:heading={2} id="heading">{name}</div>
|
|
58
|
+
|
|
59
|
+
{#if birth_date}
|
|
60
|
+
<p class="dates">
|
|
61
|
+
{t('Born On')} <strong>{formatDate(birth_date)}</strong>{#if death_date}, {t('Died On')} <strong>{formatDate(death_date)}</strong>{/if}
|
|
62
|
+
</p>
|
|
63
|
+
{/if}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="content">
|
|
67
|
+
{#if !small}
|
|
68
|
+
<div class="heading subheading" use:heading={3} id="credits">{t('Credits')}</div>
|
|
69
|
+
{/if}
|
|
70
|
+
|
|
71
|
+
<div class="list">
|
|
72
|
+
{#each titles as title}
|
|
73
|
+
<ListTitle {title} compact={small} onclick={(event) => openModal({ event, data: title })} />
|
|
74
|
+
{/each}
|
|
75
|
+
|
|
76
|
+
{#if loading}
|
|
77
|
+
{#each { length: 6 }}
|
|
78
|
+
<div class="skeleton" data-testid="skeleton"></div>
|
|
79
|
+
{/each}
|
|
80
|
+
{:else if hasMorePages}
|
|
81
|
+
<button class="more" onclick={loadMore}>{t('Show More')}</button>
|
|
82
|
+
{/if}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<style lang="scss">
|
|
87
|
+
.header {
|
|
88
|
+
padding: margin(4) margin(1) margin(2);
|
|
89
|
+
font-family: theme(detail-font-family, font-family);
|
|
90
|
+
font-weight: theme(detail-font-weight, normal);
|
|
91
|
+
font-size: theme(detail-font-size, font-size-base);
|
|
92
|
+
line-height: theme(participant-description-line-height, normal);
|
|
93
|
+
color: theme(detail-text-color, text-color);
|
|
94
|
+
|
|
95
|
+
&.small {
|
|
96
|
+
padding: margin(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.heading {
|
|
101
|
+
margin: 0;
|
|
102
|
+
font-family: theme(detail-title-font-family, font-family);
|
|
103
|
+
font-weight: theme(detail-title-font-weight, lighter);
|
|
104
|
+
font-size: theme(detail-title-font-size, margin(1.5));
|
|
105
|
+
line-height: normal;
|
|
106
|
+
font-style: theme(detail-title-font-style, normal);
|
|
107
|
+
|
|
108
|
+
&.subheading {
|
|
109
|
+
margin: 0 0 margin(0.5);
|
|
110
|
+
font-size: theme(detail-title-small-font-size, margin(1.25));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.dates {
|
|
115
|
+
margin: margin(0.5) 0 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.content {
|
|
119
|
+
padding: 0 margin(1) margin(1);
|
|
120
|
+
color: theme(detail-text-color, text-color);
|
|
121
|
+
font-family: theme(detail-font-family, font-family);
|
|
122
|
+
font-weight: theme(detail-font-weight, normal);
|
|
123
|
+
font-size: theme(detail-font-size, font-size-base);
|
|
124
|
+
line-height: normal;
|
|
125
|
+
font-style: normal;
|
|
126
|
+
|
|
127
|
+
::view-transition-old(content),
|
|
128
|
+
::view-transition-new(content) {
|
|
129
|
+
height: 100%;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.list {
|
|
134
|
+
display: flex;
|
|
135
|
+
flex-direction: column;
|
|
136
|
+
gap: margin(0.5);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.skeleton {
|
|
140
|
+
min-height: margin(7.25);
|
|
141
|
+
border-radius: theme(playlink-border-radius, border-radius);
|
|
142
|
+
background: theme(detail-background-light, lighter);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.more {
|
|
146
|
+
cursor: pointer;
|
|
147
|
+
appearance: none;
|
|
148
|
+
padding: margin(0.5) margin(1);
|
|
149
|
+
border: 0;
|
|
150
|
+
border-radius: theme(participants-load-more-border-radius, border-radius);
|
|
151
|
+
background: theme(participants-load-more-background, content);
|
|
152
|
+
color: inherit;
|
|
153
|
+
font-size: inherit;
|
|
154
|
+
font-family: inherit;
|
|
155
|
+
|
|
156
|
+
&:hover {
|
|
157
|
+
filter: brightness(1.2);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
</style>
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
<button onclick={(() => {
|
|
37
37
|
destroyExplore()
|
|
38
|
-
window.PlayPilotLinkInjections.config
|
|
38
|
+
window.PlayPilotLinkInjections.config!.explore_use_router = !useExploreRouter()
|
|
39
39
|
insertExplore()
|
|
40
40
|
})}>Toggle new explore</button>
|
|
41
41
|
|
|
@@ -73,4 +73,3 @@
|
|
|
73
73
|
background: theme(light);
|
|
74
74
|
}
|
|
75
75
|
</style>
|
|
76
|
-
|
|
@@ -40,7 +40,7 @@ describe('$lib/api/externalPages', () => {
|
|
|
40
40
|
|
|
41
41
|
expect(response).toBe('Some response')
|
|
42
42
|
expect(api).toHaveBeenCalledWith(
|
|
43
|
-
'/external-pages/?api-token=a&include_title_details=true&language=en-US&url=https://some-url',
|
|
43
|
+
'/external-pages/?api-token=a&include_title_details=true&include_participant_details=true&language=en-US&url=https://some-url',
|
|
44
44
|
expect.objectContaining({
|
|
45
45
|
method: 'GET',
|
|
46
46
|
}),
|
|
@@ -120,7 +120,7 @@ describe('explore.js', () => {
|
|
|
120
120
|
|
|
121
121
|
it('Should not insert if config object is present but explore_navigation_selector is not given', () => {
|
|
122
122
|
window.PlayPilotLinkInjections.config = {
|
|
123
|
-
explore_navigation_selector:
|
|
123
|
+
explore_navigation_selector: '',
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
expect(insertExploreIntoNavigation()).toBe(false)
|
|
@@ -136,11 +136,12 @@ describe('explore.js', () => {
|
|
|
136
136
|
expect(insertExploreIntoNavigation()).toBe(false)
|
|
137
137
|
})
|
|
138
138
|
|
|
139
|
-
it('Should insert if explore_navigation_selector is given and selector
|
|
139
|
+
it('Should insert if explore_navigation_selector is given and selector and path are valid', () => {
|
|
140
140
|
document.body.innerHTML = '<nav><a></a></nav>'
|
|
141
141
|
|
|
142
142
|
window.PlayPilotLinkInjections.config = {
|
|
143
143
|
explore_navigation_selector: 'a',
|
|
144
|
+
explore_navigation_path: '/explore',
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
expect(insertExploreIntoNavigation()).toBe(true)
|
|
@@ -165,6 +166,7 @@ describe('explore.js', () => {
|
|
|
165
166
|
window.PlayPilotLinkInjections.config = {
|
|
166
167
|
explore_navigation_selector: 'a',
|
|
167
168
|
explore_navigation_label: 'Some label',
|
|
169
|
+
explore_navigation_path: '/explore',
|
|
168
170
|
}
|
|
169
171
|
|
|
170
172
|
insertExploreIntoNavigation()
|
|
@@ -984,7 +984,7 @@ describe('injection.ts', () => {
|
|
|
984
984
|
injectLinksInDocument(elements, { aiInjections: [injection], manualInjections: [] })
|
|
985
985
|
|
|
986
986
|
const link = /** @type {HTMLAnchorElement} */ (document.querySelector('a'))
|
|
987
|
-
expect(link.href).toBe(window.PlayPilotLinkInjections.config
|
|
987
|
+
expect(link.href).toBe(window.PlayPilotLinkInjections.config?.explore_navigation_path + `?route=modal&sid=${injection.title_details?.sid}`)
|
|
988
988
|
expect(link.target).not.toBeTruthy()
|
|
989
989
|
})
|
|
990
990
|
|
|
@@ -26,6 +26,7 @@ describe('retargeting.ts', () => {
|
|
|
26
26
|
describe('isPixelAllow', () => {
|
|
27
27
|
it('Should return false if no config object is set', () => {
|
|
28
28
|
vi.mocked(hasConsentedTo).mockImplementation(() => true)
|
|
29
|
+
// @ts-ignore
|
|
29
30
|
window.PlayPilotLinkInjections.config = null
|
|
30
31
|
|
|
31
32
|
expect(isRetargetingAllowed()).toBe(false)
|
|
@@ -33,6 +34,7 @@ describe('retargeting.ts', () => {
|
|
|
33
34
|
|
|
34
35
|
it('Should return false if config object is without allow_retargeting_pixels', () => {
|
|
35
36
|
vi.mocked(hasConsentedTo).mockImplementation(() => true)
|
|
37
|
+
// @ts-ignore
|
|
36
38
|
window.PlayPilotLinkInjections.config = { something: true }
|
|
37
39
|
|
|
38
40
|
expect(isRetargetingAllowed()).toBe(false)
|
|
@@ -118,6 +118,7 @@ describe('Playlink.svelte', () => {
|
|
|
118
118
|
})
|
|
119
119
|
|
|
120
120
|
it('Should render as link when user has not consented to affiliate but require_affiliate_consent is false', () => {
|
|
121
|
+
// @ts-ignore
|
|
121
122
|
window.PlayPilotLinkInjections.config.require_affiliate_consent = false
|
|
122
123
|
|
|
123
124
|
vi.mocked(hasConsentedTo).mockImplementation(() => false)
|
|
@@ -129,6 +130,7 @@ describe('Playlink.svelte', () => {
|
|
|
129
130
|
})
|
|
130
131
|
|
|
131
132
|
it('Should render as link when user has not consented to affiliate and require_affiliate_consent is true', () => {
|
|
133
|
+
// @ts-ignore
|
|
132
134
|
window.PlayPilotLinkInjections.config.require_affiliate_consent = true
|
|
133
135
|
|
|
134
136
|
vi.mocked(hasConsentedTo).mockImplementation(() => false)
|
package/src/tests/setup.js
CHANGED
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
import { cleanup } from '@testing-library/svelte'
|
|
2
|
-
import { afterEach, beforeAll, beforeEach, vi } from 'vitest'
|
|
3
|
-
import { fakeFetch } from './helpers'
|
|
4
|
-
|
|
5
|
-
vi.mock('svelte/transition')
|
|
6
|
-
|
|
7
|
-
// https://github.com/jsdom/jsdom/issues/3429#issuecomment-1936128876
|
|
8
|
-
const mockAnimations = () => {
|
|
9
|
-
Element.prototype.animate ??= vi.fn().mockReturnValue({
|
|
10
|
-
onfinish: null,
|
|
11
|
-
finished: Promise.resolve(),
|
|
12
|
-
cancel: vi.fn(),
|
|
13
|
-
startTime: null,
|
|
14
|
-
currentTime: null,
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
Element.prototype.getAnimations ??= vi.fn().mockReturnValue([])
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const mockLocalStorage = () => {
|
|
21
|
-
/** @type {Record<string, string>} */
|
|
22
|
-
let store = {}
|
|
23
|
-
|
|
24
|
-
// @ts-ignore
|
|
25
|
-
;(window).localStorage = {
|
|
26
|
-
getItem: function (key) {
|
|
27
|
-
return store[key] || null
|
|
28
|
-
},
|
|
29
|
-
setItem: function (key, value) {
|
|
30
|
-
store[key] = value.toString()
|
|
31
|
-
},
|
|
32
|
-
removeItem: function (key) {
|
|
33
|
-
delete store[key]
|
|
34
|
-
},
|
|
35
|
-
clear: function () {
|
|
36
|
-
store = {}
|
|
37
|
-
},
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const mockSessionStorage = () => {
|
|
42
|
-
/** @type {Record<string, string>} */
|
|
43
|
-
let store = {}
|
|
44
|
-
|
|
45
|
-
// @ts-ignore
|
|
46
|
-
;(window).sessionStorage = {
|
|
47
|
-
getItem: function (key) {
|
|
48
|
-
return store[key] || null
|
|
49
|
-
},
|
|
50
|
-
setItem: function (key, value) {
|
|
51
|
-
store[key] = value.toString()
|
|
52
|
-
},
|
|
53
|
-
removeItem: function (key) {
|
|
54
|
-
delete store[key]
|
|
55
|
-
},
|
|
56
|
-
clear: function () {
|
|
57
|
-
store = {}
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const mockIntersectionObserver = () => {
|
|
63
|
-
// @ts-ignore
|
|
64
|
-
global.IntersectionObserver = vi.fn((callback) => ({
|
|
65
|
-
disconnect: vi.fn(),
|
|
66
|
-
observe: vi.fn(() => callback([{ isIntersecting: true }])),
|
|
67
|
-
unobserve: vi.fn(),
|
|
68
|
-
}))
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
beforeAll(() => {
|
|
72
|
-
fakeFetch()
|
|
73
|
-
mockLocalStorage()
|
|
74
|
-
mockSessionStorage()
|
|
75
|
-
mockIntersectionObserver()
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
beforeEach(() => {
|
|
79
|
-
// @ts-ignore
|
|
80
|
-
window.PlayPilotLinkInjections = { token: 'some-token', require_consent: false }
|
|
81
|
-
|
|
82
|
-
// Enable all consent
|
|
83
|
-
// @ts-ignore
|
|
84
|
-
window.__tcfapi = (command, _version, callback) => {
|
|
85
|
-
if (command !== 'addEventListener') return
|
|
86
|
-
|
|
87
|
-
setTimeout(() => callback({
|
|
88
|
-
purpose: { consents: { 1: true, 2: true, 7: true, 8: true, 9: true, 10: true } },
|
|
89
|
-
eventStatus: 'tcloaded',
|
|
90
|
-
}, true), 0)
|
|
91
|
-
|
|
92
|
-
return 1
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Reset cookies
|
|
96
|
-
document.cookie.split(';').forEach((cookie) => document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, '=;'))
|
|
97
|
-
|
|
98
|
-
window.HTMLElement.prototype.scrollIntoView = vi.fn()
|
|
99
|
-
|
|
100
|
-
localStorage.clear()
|
|
101
|
-
sessionStorage.clear()
|
|
102
|
-
|
|
103
|
-
mockAnimations()
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
afterEach(() => {
|
|
107
|
-
cleanup()
|
|
108
|
-
})
|
|
1
|
+
import { cleanup } from '@testing-library/svelte'
|
|
2
|
+
import { afterEach, beforeAll, beforeEach, vi } from 'vitest'
|
|
3
|
+
import { fakeFetch } from './helpers'
|
|
4
|
+
|
|
5
|
+
vi.mock('svelte/transition')
|
|
6
|
+
|
|
7
|
+
// https://github.com/jsdom/jsdom/issues/3429#issuecomment-1936128876
|
|
8
|
+
const mockAnimations = () => {
|
|
9
|
+
Element.prototype.animate ??= vi.fn().mockReturnValue({
|
|
10
|
+
onfinish: null,
|
|
11
|
+
finished: Promise.resolve(),
|
|
12
|
+
cancel: vi.fn(),
|
|
13
|
+
startTime: null,
|
|
14
|
+
currentTime: null,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
Element.prototype.getAnimations ??= vi.fn().mockReturnValue([])
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const mockLocalStorage = () => {
|
|
21
|
+
/** @type {Record<string, string>} */
|
|
22
|
+
let store = {}
|
|
23
|
+
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
;(window).localStorage = {
|
|
26
|
+
getItem: function (key) {
|
|
27
|
+
return store[key] || null
|
|
28
|
+
},
|
|
29
|
+
setItem: function (key, value) {
|
|
30
|
+
store[key] = value.toString()
|
|
31
|
+
},
|
|
32
|
+
removeItem: function (key) {
|
|
33
|
+
delete store[key]
|
|
34
|
+
},
|
|
35
|
+
clear: function () {
|
|
36
|
+
store = {}
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const mockSessionStorage = () => {
|
|
42
|
+
/** @type {Record<string, string>} */
|
|
43
|
+
let store = {}
|
|
44
|
+
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
;(window).sessionStorage = {
|
|
47
|
+
getItem: function (key) {
|
|
48
|
+
return store[key] || null
|
|
49
|
+
},
|
|
50
|
+
setItem: function (key, value) {
|
|
51
|
+
store[key] = value.toString()
|
|
52
|
+
},
|
|
53
|
+
removeItem: function (key) {
|
|
54
|
+
delete store[key]
|
|
55
|
+
},
|
|
56
|
+
clear: function () {
|
|
57
|
+
store = {}
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const mockIntersectionObserver = () => {
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
global.IntersectionObserver = vi.fn((callback) => ({
|
|
65
|
+
disconnect: vi.fn(),
|
|
66
|
+
observe: vi.fn(() => callback([{ isIntersecting: true }])),
|
|
67
|
+
unobserve: vi.fn(),
|
|
68
|
+
}))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
beforeAll(() => {
|
|
72
|
+
fakeFetch()
|
|
73
|
+
mockLocalStorage()
|
|
74
|
+
mockSessionStorage()
|
|
75
|
+
mockIntersectionObserver()
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
// @ts-ignore
|
|
80
|
+
window.PlayPilotLinkInjections = { token: 'some-token', require_consent: false }
|
|
81
|
+
|
|
82
|
+
// Enable all consent
|
|
83
|
+
// @ts-ignore
|
|
84
|
+
window.__tcfapi = (command, _version, callback) => {
|
|
85
|
+
if (command !== 'addEventListener') return
|
|
86
|
+
|
|
87
|
+
setTimeout(() => callback({
|
|
88
|
+
purpose: { consents: { 1: true, 2: true, 7: true, 8: true, 9: true, 10: true } },
|
|
89
|
+
eventStatus: 'tcloaded',
|
|
90
|
+
}, true), 0)
|
|
91
|
+
|
|
92
|
+
return 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Reset cookies
|
|
96
|
+
document.cookie.split(';').forEach((cookie) => document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, '=;'))
|
|
97
|
+
|
|
98
|
+
window.HTMLElement.prototype.scrollIntoView = vi.fn()
|
|
99
|
+
|
|
100
|
+
localStorage.clear()
|
|
101
|
+
sessionStorage.clear()
|
|
102
|
+
|
|
103
|
+
mockAnimations()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
afterEach(() => {
|
|
107
|
+
cleanup()
|
|
108
|
+
})
|