@playpilot/tpi 5.18.1-beta.2 → 5.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/dist/link-injections.js +10 -10
- package/events.md +3 -1
- package/package.json +1 -1
- package/src/lib/api/api.ts +21 -2
- package/src/lib/enums/TrackingEvent.ts +1 -0
- package/src/lib/injection.ts +10 -8
- package/src/lib/scss/_mixins.scss +12 -0
- package/src/routes/+page.svelte +26 -9
- package/src/routes/components/Ads/Display.svelte +2 -2
- package/src/routes/components/ListTitle.svelte +3 -3
- package/src/routes/components/Modal.svelte +23 -8
- package/src/routes/components/{Playlink.svelte → Playlinks/Playlink.svelte} +2 -2
- package/src/routes/components/Rails/ParticipantsRail.svelte +27 -14
- package/src/routes/components/Title.svelte +3 -3
- package/src/routes/elements/+page.svelte +3 -3
- package/src/tests/lib/api/api.test.js +41 -2
- package/src/tests/routes/+page.test.js +40 -1
- package/src/tests/routes/components/{AfterArticlePlaylinks.test.js → Playlinks/AfterArticlePlaylinks.test.js} +5 -2
- package/src/tests/routes/components/{Playlink.test.js → Playlinks/Playlink.test.js} +1 -1
- package/src/tests/routes/components/{PlaylinkIcon.test.js → Playlinks/PlaylinkIcon.test.js} +1 -1
- package/src/tests/routes/components/{PlaylinkLabel.test.js → Playlinks/PlaylinkLabel.test.js} +1 -1
- package/src/tests/routes/components/{Playlinks.test.js → Playlinks/Playlinks.test.js} +1 -1
- package/src/tests/routes/components/Rails/ParticipantsRail.test.js +17 -19
- /package/src/routes/components/{AfterArticlePlaylinks.svelte → Playlinks/AfterArticlePlaylinks.svelte} +0 -0
- /package/src/routes/components/{PlaylinkIcon.svelte → Playlinks/PlaylinkIcon.svelte} +0 -0
- /package/src/routes/components/{PlaylinkLabel.svelte → Playlinks/PlaylinkLabel.svelte} +0 -0
- /package/src/routes/components/{Playlinks.svelte → Playlinks/Playlinks.svelte} +0 -0
package/events.md
CHANGED
|
@@ -56,7 +56,9 @@ Event | Action | Info | Payload
|
|
|
56
56
|
Event | Action | Info | Payload
|
|
57
57
|
--- | --- | --- | ---
|
|
58
58
|
`ali_injection_failed` | _Fires only inside of the Editor for each injection that failed_ | Only includes visible failures, for instance, it will ignore failures because of already existing links. If a user is shown a message about a failed injection, this event will fire. Includes data on the phrase and sentence for the injection. | `Title`, `phrase`, `sentence`
|
|
59
|
-
`ali_injection_count` | _Fires the first time the Editor is shown_ | This logs the total amount of injections, the total amount of failed and manual injections, and the total amount of successful injections. | `total` (number of failed +
|
|
59
|
+
`ali_injection_count` | _Fires the first time the Editor is shown_ | This logs the total amount of injections, the total amount of failed and manual injections, and the total amount of successful injections. | `total` (number of failed + successful injections),
|
|
60
|
+
`ali_injections_failed_count` | _Fires any time an article included failed injections_ | `total`, `sentence_not_found`, `already_linked`, `broken_html`, `duplicate`, `unknown` (All separate counts of failed injection categories)
|
|
61
|
+
`failed_automatic`, `failed_manual`, `final_injected` (number of successful injections)
|
|
60
62
|
`ali_fetch_config_failed` | _Fires whenever the config object failed to fetch_ | When this happens, injections are aborted.
|
|
61
63
|
`ali_auth_failed` | _Fires whenever authentication for the Editor fails._
|
|
62
64
|
|
package/package.json
CHANGED
package/src/lib/api/api.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { apiBaseUrl } from '$lib/constants'
|
|
2
|
+
import { isEditorialModeEnabled } from './auth'
|
|
3
|
+
|
|
4
|
+
const cache: Record<string, any> = {}
|
|
2
5
|
|
|
3
6
|
type Options = {
|
|
4
7
|
headers?: Record<string, string>
|
|
@@ -7,6 +10,12 @@ type Options = {
|
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export async function api<T>(path: string, { headers = {}, method = 'GET', body = null }: Options = {}): Promise<T> {
|
|
13
|
+
// For GET methods we store all results in a cache object. This cache is super simple and has no invalidation at all.
|
|
14
|
+
// Once something is stored it's there forever since the page is only short lived.
|
|
15
|
+
// We never use the cache for editiorial mode since in the editor requests do change.
|
|
16
|
+
const useCache = !isEditorialModeEnabled() && method === 'GET'
|
|
17
|
+
if (useCache && (path in cache)) return cache[path]
|
|
18
|
+
|
|
10
19
|
const baseHeaders = { 'Content-Type': 'application/json' }
|
|
11
20
|
|
|
12
21
|
const response = await fetch(apiBaseUrl + path, {
|
|
@@ -19,9 +28,19 @@ export async function api<T>(path: string, { headers = {}, method = 'GET', body
|
|
|
19
28
|
|
|
20
29
|
const text = await response.text()
|
|
21
30
|
|
|
31
|
+
let parsed: unknown = text
|
|
32
|
+
|
|
22
33
|
try {
|
|
23
|
-
|
|
34
|
+
if (text) parsed = JSON.parse(text)
|
|
24
35
|
} catch {
|
|
25
|
-
|
|
36
|
+
// Ignore
|
|
26
37
|
}
|
|
38
|
+
|
|
39
|
+
if (useCache) cache[path] = parsed
|
|
40
|
+
|
|
41
|
+
return parsed as T
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function clearCache(): void {
|
|
45
|
+
for(const key in cache) delete cache[key]
|
|
27
46
|
}
|
|
@@ -30,6 +30,7 @@ export const TrackingEvent = Object.freeze({
|
|
|
30
30
|
// Fails
|
|
31
31
|
InjectionFailed: 'ali_injection_failed',
|
|
32
32
|
TotalInjectionsCount: 'ali_injection_count',
|
|
33
|
+
FailedInjectionsCount: 'ali_failed_injection_count',
|
|
33
34
|
FetchingConfigFailed: 'ali_fetch_config_failed',
|
|
34
35
|
AuthFailed: 'ali_auth_failed',
|
|
35
36
|
|
package/src/lib/injection.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mount, unmount } from 'svelte'
|
|
2
2
|
import TitlePopover from '../routes/components/TitlePopover.svelte'
|
|
3
|
-
import AfterArticlePlaylinks from '../routes/components/AfterArticlePlaylinks.svelte'
|
|
3
|
+
import AfterArticlePlaylinks from '../routes/components/Playlinks/AfterArticlePlaylinks.svelte'
|
|
4
4
|
import { cleanPhrase, findNumberOfMatchesInString, findShortestMatchBetweenPhrases, findTextNodeContaining, getIndexOfPhraseInElement, getNumberOfLeadingAndTrailingSpaces, isNodeInLink, replaceBetween, replaceStartingFrom } from './text'
|
|
5
5
|
import type { LinkInjection, LinkInjectionTypes } from './types/injection'
|
|
6
6
|
import { isHoldingSpecialKey } from './event'
|
|
@@ -322,13 +322,15 @@ function addCSSVariablesToLinks(): void {
|
|
|
322
322
|
'--playpilot-injection-background-color-hover',
|
|
323
323
|
]
|
|
324
324
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
325
|
+
for (const element of createdLinkElements) {
|
|
326
|
+
const style = getComputedStyle(element)
|
|
327
|
+
|
|
328
|
+
for (const value of variables) {
|
|
329
|
+
if (!style.getPropertyValue(value)) continue
|
|
330
|
+
|
|
331
|
+
element.dataset.usedCssVariables = `${element.dataset.usedCssVariables || ''} ${value}`
|
|
332
|
+
}
|
|
333
|
+
}
|
|
332
334
|
}
|
|
333
335
|
|
|
334
336
|
/**
|
package/src/routes/+page.svelte
CHANGED
|
@@ -132,15 +132,7 @@
|
|
|
132
132
|
window.PlayPilotLinkInjections.evaluated_link_injections = filteredInjections
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (!successfulInjections.length) return
|
|
138
|
-
if (isEditorialMode) return
|
|
139
|
-
|
|
140
|
-
track(TrackingEvent.ArticleInjected, null, {
|
|
141
|
-
manual: successfulInjections.filter(i => i.manual).length,
|
|
142
|
-
ai: successfulInjections.filter(i => !i.manual).length,
|
|
143
|
-
})
|
|
135
|
+
if (!isEditorialMode) trackInjections(filteredInjections)
|
|
144
136
|
}
|
|
145
137
|
|
|
146
138
|
// Set elements to be used by script, if a selector is passed from the config request we update
|
|
@@ -170,6 +162,31 @@
|
|
|
170
162
|
|
|
171
163
|
if (!existingElement) document.body.appendChild(styleElement)
|
|
172
164
|
}
|
|
165
|
+
|
|
166
|
+
function trackInjections(filteredInjections: LinkInjection[]): void {
|
|
167
|
+
const successfulInjections = filteredInjections.filter(i => !i.failed)
|
|
168
|
+
const failedInjections = filteredInjections.filter(i => i.failed)
|
|
169
|
+
|
|
170
|
+
if (successfulInjections.length) {
|
|
171
|
+
track(TrackingEvent.ArticleInjected, null, {
|
|
172
|
+
manual: successfulInjections.filter(i => i.manual).length,
|
|
173
|
+
ai: successfulInjections.filter(i => !i.manual).length,
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (failedInjections.length) {
|
|
178
|
+
const countForMessage = (message: string): number => failedInjections.filter(({ failed_message }) => failed_message?.includes(message)).length
|
|
179
|
+
|
|
180
|
+
track(TrackingEvent.FailedInjectionsCount, null, {
|
|
181
|
+
total: failedInjections.length,
|
|
182
|
+
sentence_not_found: countForMessage('sentence was not found'),
|
|
183
|
+
already_linked: countForMessage('already inside of a link'),
|
|
184
|
+
broken_html: countForMessage('broken HTML'),
|
|
185
|
+
duplicate: countForMessage('duplicate'),
|
|
186
|
+
unknown: countForMessage('unknown reasons'),
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
}
|
|
173
190
|
</script>
|
|
174
191
|
|
|
175
192
|
<div class="playpilot-link-injections" data-playpilot-link-injections>
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
font-size: margin(0.75);
|
|
105
105
|
text-decoration: underline;
|
|
106
106
|
|
|
107
|
-
@
|
|
107
|
+
@include desktop() {
|
|
108
108
|
padding: margin(1);
|
|
109
109
|
|
|
110
110
|
.compact & {
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
font-size: margin(0.875);
|
|
128
128
|
text-decoration: none;
|
|
129
129
|
|
|
130
|
-
@
|
|
130
|
+
@include desktop() {
|
|
131
131
|
padding: margin(1);
|
|
132
132
|
|
|
133
133
|
.compact & {
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
import type { TitleData } from '$lib/types/title'
|
|
8
8
|
import IconArrow from './Icons/IconArrow.svelte'
|
|
9
9
|
import IconIMDb from './Icons/IconIMDb.svelte'
|
|
10
|
-
import PlaylinkIcon from './PlaylinkIcon.svelte'
|
|
11
|
-
import PlaylinkLabel from './PlaylinkLabel.svelte'
|
|
10
|
+
import PlaylinkIcon from './Playlinks/PlaylinkIcon.svelte'
|
|
11
|
+
import PlaylinkLabel from './Playlinks/PlaylinkLabel.svelte'
|
|
12
12
|
import TitlePoster from './TitlePoster.svelte'
|
|
13
13
|
|
|
14
14
|
interface Props {
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
font-size: var(--playpilot-detail-font-size, 14px);
|
|
125
125
|
line-height: normal;
|
|
126
126
|
|
|
127
|
-
@
|
|
127
|
+
@include desktop() {
|
|
128
128
|
padding-right: margin(1);
|
|
129
129
|
}
|
|
130
130
|
}
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
}: Props = $props()
|
|
33
33
|
|
|
34
34
|
const inlineBubble = isInSplitTestVariant(SplitTest.TopScrollFormat, 1)
|
|
35
|
+
const historyHash = '#playpilot'
|
|
35
36
|
|
|
36
37
|
let windowWidth = $state(0)
|
|
37
38
|
let modalElement: HTMLElement | null = $state(null)
|
|
@@ -52,9 +53,20 @@
|
|
|
52
53
|
|
|
53
54
|
hasPreviousModal = !!getPreviousModal()
|
|
54
55
|
|
|
56
|
+
// Add modal state to the browser history. This allows us to close to modal when using the back button.
|
|
57
|
+
// Only do this for the very first modal opened in the stack. The back button always fully closes all modals.
|
|
58
|
+
if (!hasPreviousModal) window.history.pushState({ modal: true }, '', historyHash)
|
|
59
|
+
|
|
55
60
|
requestAnimationFrame(setInitialScrollPosition)
|
|
56
61
|
|
|
57
|
-
return () =>
|
|
62
|
+
return () => {
|
|
63
|
+
document.body.style.overflowY = baseOverflowStyle || ''
|
|
64
|
+
|
|
65
|
+
// Remove the modal entry from above from the browser history. This is done only once for the entire stack.
|
|
66
|
+
// We check for getPreviousModal not because we really care about the previous modal, but to check if all
|
|
67
|
+
// modals were closed and none will open again within this stack.
|
|
68
|
+
if (!getPreviousModal() && history.state?.modal) history.back()
|
|
69
|
+
}
|
|
58
70
|
})
|
|
59
71
|
|
|
60
72
|
function setInitialScrollPosition(): void {
|
|
@@ -70,7 +82,10 @@
|
|
|
70
82
|
}
|
|
71
83
|
</script>
|
|
72
84
|
|
|
73
|
-
<svelte:window
|
|
85
|
+
<svelte:window
|
|
86
|
+
onkeydown={({ key }) => { if (key === 'Escape') destroyAllModals() }}
|
|
87
|
+
onhashchange={() => destroyAllModals()}
|
|
88
|
+
bind:innerWidth={windowWidth} />
|
|
74
89
|
|
|
75
90
|
<div class="modal" style:--dialog-offset="{dialogOffset}px" transition:fade|global={{ duration: 150 }} bind:this={modalElement} class:has-bubble={!!bubble && inlineBubble} class:has-prepend={!!prepend}>
|
|
76
91
|
{#if prepend}
|
|
@@ -133,7 +148,7 @@
|
|
|
133
148
|
left: 0;
|
|
134
149
|
background: var(--playpilot-detail-backdrop, rgba(0, 0, 0, 0.65));
|
|
135
150
|
|
|
136
|
-
@
|
|
151
|
+
@include desktop() {
|
|
137
152
|
padding: margin(2) 0;
|
|
138
153
|
}
|
|
139
154
|
|
|
@@ -159,7 +174,7 @@
|
|
|
159
174
|
max-height: 80dvh;
|
|
160
175
|
}
|
|
161
176
|
|
|
162
|
-
@
|
|
177
|
+
@include desktop() {
|
|
163
178
|
max-height: unset;
|
|
164
179
|
margin-top: 0;
|
|
165
180
|
padding-bottom: 0;
|
|
@@ -217,7 +232,7 @@
|
|
|
217
232
|
top: calc(var(--dialog-offset) + margin(1));
|
|
218
233
|
right: margin(1);
|
|
219
234
|
|
|
220
|
-
@
|
|
235
|
+
@include desktop() {
|
|
221
236
|
position: absolute;
|
|
222
237
|
top: margin(1);
|
|
223
238
|
}
|
|
@@ -253,7 +268,7 @@
|
|
|
253
268
|
padding-top: margin(3);
|
|
254
269
|
margin: auto auto margin(0.5);
|
|
255
270
|
|
|
256
|
-
@
|
|
271
|
+
@include desktop() {
|
|
257
272
|
padding-top: 0;
|
|
258
273
|
margin-top: 0;
|
|
259
274
|
}
|
|
@@ -266,7 +281,7 @@
|
|
|
266
281
|
max-width: $max-width;
|
|
267
282
|
margin: margin(0.5);
|
|
268
283
|
|
|
269
|
-
@
|
|
284
|
+
@include desktop() {
|
|
270
285
|
width: 100%;
|
|
271
286
|
margin: 0 0 margin(0.5);
|
|
272
287
|
}
|
|
@@ -275,7 +290,7 @@
|
|
|
275
290
|
width: 100%;
|
|
276
291
|
margin: auto 0 0;
|
|
277
292
|
|
|
278
|
-
@
|
|
293
|
+
@include desktop() {
|
|
279
294
|
margin-top: 0;
|
|
280
295
|
}
|
|
281
296
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import Disclaimer from '
|
|
2
|
+
import Disclaimer from '../Ads/Disclaimer.svelte'
|
|
3
3
|
import { hasConsentedTo } from '$lib/consent'
|
|
4
4
|
import { removeImageUrlPrefix } from '$lib/image'
|
|
5
5
|
import { t } from '$lib/localization'
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
opacity: 0.35;
|
|
120
120
|
pointer-events: none;
|
|
121
121
|
|
|
122
|
-
@
|
|
122
|
+
@include motion() {
|
|
123
123
|
animation: sheen 4000ms ease-in-out infinite;
|
|
124
124
|
}
|
|
125
125
|
}
|
|
@@ -1,30 +1,43 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { participants } from '$lib/fakeData'
|
|
2
3
|
import { openModal } from '$lib/modal'
|
|
3
4
|
import type { ParticipantData } from '$lib/types/participant'
|
|
4
5
|
import Rail from './Rail.svelte'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
async function fetchParticipants(): Promise<ParticipantData[]> {
|
|
8
|
+
// This is just a fake loading state for now
|
|
9
|
+
await new Promise(res => setTimeout(res, 500))
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
return participants
|
|
12
|
+
}
|
|
11
13
|
</script>
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<
|
|
15
|
+
{#await fetchParticipants()}
|
|
16
|
+
<Rail heading="Cast">
|
|
17
|
+
{#each { length: 5 }}
|
|
18
|
+
<div class="participant"></div>
|
|
19
|
+
{/each}
|
|
20
|
+
</Rail>
|
|
21
|
+
{:then participants}
|
|
22
|
+
{#if participants?.length}
|
|
23
|
+
<Rail heading="Cast">
|
|
24
|
+
{#each participants.slice(0, 15) as participant}
|
|
25
|
+
<button class="participant" data-testid="participant" onclick={event => openModal({ event, type: 'participant', data: participant })}>
|
|
26
|
+
<span class="truncate">{participant.name}</span>
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
</Rail>
|
|
28
|
+
<div class="character truncate">{participant.character}</div>
|
|
29
|
+
</button>
|
|
30
|
+
{/each}
|
|
31
|
+
</Rail>
|
|
32
|
+
{/if}
|
|
33
|
+
{/await}
|
|
22
34
|
|
|
23
35
|
<style lang="scss">
|
|
24
36
|
.participant {
|
|
25
37
|
display: block;
|
|
26
38
|
flex: 0 0 10rem;
|
|
27
39
|
width: 10rem;
|
|
40
|
+
min-height: margin(3.375); // Matches 54 pixels, the height of a card with both name and character
|
|
28
41
|
padding: margin(0.5);
|
|
29
42
|
border: 0;
|
|
30
43
|
border-radius: var(--playpilot-cast-border-radius, var(--playpilot-playlink-border-radius, margin(0.5)));
|
|
@@ -36,8 +49,8 @@
|
|
|
36
49
|
font-size: var(--playpilot-cast-font-size, var(--playpilot-playlinks-font-size, margin(0.75)));
|
|
37
50
|
white-space: nowrap;
|
|
38
51
|
|
|
39
|
-
&:hover,
|
|
40
|
-
&:active {
|
|
52
|
+
&:is(button):hover,
|
|
53
|
+
&:is(button):active {
|
|
41
54
|
filter: var(--playpilot-cast-hover-filter, var(--playpilot-playlink-hover-filter, brightness(1.1)));
|
|
42
55
|
text-decoration: none !important;
|
|
43
56
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Genres from './Genres.svelte'
|
|
3
|
-
import Playlinks from './Playlinks.svelte'
|
|
3
|
+
import Playlinks from './Playlinks/Playlinks.svelte'
|
|
4
4
|
import Description from './Description.svelte'
|
|
5
5
|
import IconIMDb from './Icons/IconIMDb.svelte'
|
|
6
6
|
import ParticipantsRail from './Rails/ParticipantsRail.svelte'
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
|
|
59
59
|
<!-- Temporarily not available on production as there is not yet an API endpoint for either -->
|
|
60
60
|
{#if process.env.NODE_ENV !== 'production'}
|
|
61
|
-
{#if true
|
|
62
|
-
<ParticipantsRail
|
|
61
|
+
{#if true}
|
|
62
|
+
<ParticipantsRail />
|
|
63
63
|
{/if}
|
|
64
64
|
|
|
65
65
|
<SimilarRail />
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
import Disclaimer from '../components/Ads/Disclaimer.svelte'
|
|
6
6
|
import Display from '../components/Ads/Display.svelte'
|
|
7
7
|
import TopScroll from '../components/Ads/TopScroll.svelte'
|
|
8
|
-
import AfterArticlePlaylinks from '../components/AfterArticlePlaylinks.svelte'
|
|
8
|
+
import AfterArticlePlaylinks from '../components/Playlinks/AfterArticlePlaylinks.svelte'
|
|
9
9
|
import Description from '../components/Description.svelte'
|
|
10
10
|
import Genres from '../components/Genres.svelte'
|
|
11
|
-
import Playlink from '../components/Playlink.svelte'
|
|
12
|
-
import Playlinks from '../components/Playlinks.svelte'
|
|
11
|
+
import Playlink from '../components/Playlinks/Playlink.svelte'
|
|
12
|
+
import Playlinks from '../components/Playlinks/Playlinks.svelte'
|
|
13
13
|
import Popover from '../components/Popover.svelte'
|
|
14
14
|
import RoundButton from '../components/RoundButton.svelte'
|
|
15
15
|
import SkeletonText from '../components/SkeletonText.svelte'
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from 'vitest'
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
2
3
|
import { fakeFetch } from '../../helpers'
|
|
3
|
-
import { api } from '$lib/api/api'
|
|
4
|
+
import { api, clearCache } from '$lib/api/api'
|
|
5
|
+
import { isEditorialModeEnabled } from '$lib/api/auth'
|
|
6
|
+
|
|
7
|
+
vi.mock('$lib/api/auth', () => ({
|
|
8
|
+
isEditorialModeEnabled: vi.fn(() => false),
|
|
9
|
+
}))
|
|
4
10
|
|
|
5
11
|
describe('$lib/api/api', () => {
|
|
6
12
|
beforeEach(() => {
|
|
13
|
+
clearCache()
|
|
7
14
|
fakeFetch()
|
|
8
15
|
})
|
|
9
16
|
|
|
@@ -51,5 +58,37 @@ describe('$lib/api/api', () => {
|
|
|
51
58
|
await expect(async () => await api('/some-path')).rejects.toThrow()
|
|
52
59
|
})
|
|
53
60
|
})
|
|
61
|
+
|
|
62
|
+
describe('cache', () => {
|
|
63
|
+
it('Should fetch only once if multiple requests are made for the same path', async () => {
|
|
64
|
+
await api('/some-path')
|
|
65
|
+
await api('/some-path')
|
|
66
|
+
|
|
67
|
+
expect(global.fetch).toHaveBeenCalledTimes(1)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('Should fetch multiple times if multiple requests are made for different paths', async () => {
|
|
71
|
+
await api('/some-path')
|
|
72
|
+
await api('/some-other-path')
|
|
73
|
+
|
|
74
|
+
expect(global.fetch).toHaveBeenCalledTimes(2)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('Should not cache POST requests', async () => {
|
|
78
|
+
await api('/some-path', { method: 'POST' })
|
|
79
|
+
await api('/some-path', { method: 'POST' })
|
|
80
|
+
|
|
81
|
+
expect(global.fetch).toHaveBeenCalledTimes(2)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('Should not cache when in editorial mode', async () => {
|
|
85
|
+
vi.mocked(isEditorialModeEnabled).mockReturnValueOnce(true)
|
|
86
|
+
|
|
87
|
+
await api('/some-path')
|
|
88
|
+
await api('/some-path')
|
|
89
|
+
|
|
90
|
+
expect(global.fetch).toHaveBeenCalledTimes(2)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
54
93
|
})
|
|
55
94
|
})
|
|
@@ -344,7 +344,46 @@ describe('$routes/+page.svelte', () => {
|
|
|
344
344
|
|
|
345
345
|
await waitFor(() => expect(injectLinksInDocument).toHaveBeenCalled())
|
|
346
346
|
|
|
347
|
-
expect(track).
|
|
347
|
+
expect(track).not.toHaveBeenCalledWith(TrackingEvent.ArticleInjected, null, expect.any(Object))
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('Should track failed injections event when injections are injected with failures', async () => {
|
|
351
|
+
const injections = [
|
|
352
|
+
{ ...generateInjection('a', 'b'), failed: true, failed_message: 'sentence was not found' },
|
|
353
|
+
{ ...generateInjection('a', 'b'), failed: true, failed_message: 'already inside of a link' },
|
|
354
|
+
{ ...generateInjection('a', 'b'), failed: true, failed_message: 'already inside of a link' },
|
|
355
|
+
{ ...generateInjection('a', 'b'), failed: true, failed_message: 'broken HTML' },
|
|
356
|
+
{ ...generateInjection('a', 'b'), failed: true, failed_message: 'broken HTML' },
|
|
357
|
+
{ ...generateInjection('a', 'b'), failed: true, failed_message: 'duplicate' },
|
|
358
|
+
{ ...generateInjection('a', 'b'), failed: true, failed_message: 'unknown reasons' },
|
|
359
|
+
]
|
|
360
|
+
|
|
361
|
+
// @ts-ignore
|
|
362
|
+
vi.mocked(pollLinkInjections).mockResolvedValueOnce({ manual_injections: injections })
|
|
363
|
+
vi.mocked(injectLinksInDocument).mockReturnValueOnce(injections)
|
|
364
|
+
|
|
365
|
+
render(page)
|
|
366
|
+
|
|
367
|
+
await waitFor(() => {
|
|
368
|
+
expect(track).toHaveBeenCalledWith(TrackingEvent.FailedInjectionsCount, null, {
|
|
369
|
+
total: injections.length,
|
|
370
|
+
sentence_not_found: 1,
|
|
371
|
+
already_linked: 2,
|
|
372
|
+
broken_html: 2,
|
|
373
|
+
duplicate: 1,
|
|
374
|
+
unknown: 1,
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('Should not track failed injection event when no injections failed', async () => {
|
|
380
|
+
// @ts-ignore
|
|
381
|
+
vi.mocked(pollLinkInjections).mockResolvedValueOnce({ ai_injections: [generateInjection('a', 'b')], manual_injections: [{ ...generateInjection('a', 'b'), manual: true }] })
|
|
382
|
+
vi.mocked(injectLinksInDocument).mockReturnValueOnce([generateInjection('a', 'b'), { ...generateInjection('a', 'b'), manual: true }])
|
|
383
|
+
|
|
384
|
+
render(page)
|
|
385
|
+
|
|
386
|
+
expect(track).not.toHaveBeenCalledWith(TrackingEvent.FailedInjectionsCount, null, expect.any(Object))
|
|
348
387
|
})
|
|
349
388
|
|
|
350
389
|
it('Should not initialize if isCrawler is true', async () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest'
|
|
3
3
|
|
|
4
|
-
import AfterArticlePlaylinks from '
|
|
4
|
+
import AfterArticlePlaylinks from '../../../../routes/components/Playlinks/AfterArticlePlaylinks.svelte'
|
|
5
5
|
import { title } from '$lib/fakeData'
|
|
6
6
|
import { track } from '$lib/tracking'
|
|
7
7
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
@@ -69,7 +69,7 @@ describe('AfterArticlePlaylinks.svelte', () => {
|
|
|
69
69
|
})
|
|
70
70
|
|
|
71
71
|
it('Should not render "and" after final playlink when only 1 playlink is present', () => {
|
|
72
|
-
/** @type {LinkInjection} */
|
|
72
|
+
/** @type {import('$lib/types/injection').LinkInjection} */
|
|
73
73
|
const injection = { ...linkInjections[0], title_details: { ...linkInjections[0].title_details, providers: [linkInjections[0].title_details.providers[0]] }}
|
|
74
74
|
const { container } = render(AfterArticlePlaylinks, { linkInjections: [injection] })
|
|
75
75
|
|
|
@@ -88,6 +88,7 @@ describe('AfterArticlePlaylinks.svelte', () => {
|
|
|
88
88
|
})
|
|
89
89
|
|
|
90
90
|
it('Should render titles without playlinks as empty state', () => {
|
|
91
|
+
/** @type {import('$lib/types/injection').LinkInjection} */
|
|
91
92
|
const injection = { ...linkInjections[0], title_details: { ...linkInjections[0].title_details, providers: [] }}
|
|
92
93
|
const { getByText } = render(AfterArticlePlaylinks, { linkInjections: [injection] })
|
|
93
94
|
|
|
@@ -95,6 +96,7 @@ describe('AfterArticlePlaylinks.svelte', () => {
|
|
|
95
96
|
})
|
|
96
97
|
|
|
97
98
|
it('Should display as modal button if after_article_style is modal_button', () => {
|
|
99
|
+
/** @type {import('$lib/types/injection').LinkInjection} */
|
|
98
100
|
const injection = { ...linkInjections[0], after_article_style: 'modal_button' }
|
|
99
101
|
const { getByText } = render(AfterArticlePlaylinks, { linkInjections: [injection] })
|
|
100
102
|
|
|
@@ -104,6 +106,7 @@ describe('AfterArticlePlaylinks.svelte', () => {
|
|
|
104
106
|
|
|
105
107
|
it('Should fire given onclickmodal function when display as modal_button', async () => {
|
|
106
108
|
const onclickmodal = vi.fn()
|
|
109
|
+
/** @type {import('$lib/types/injection').LinkInjection} */
|
|
107
110
|
const injection = { ...linkInjections[0], after_article_style: 'modal_button' }
|
|
108
111
|
const { getByText } = render(AfterArticlePlaylinks, { linkInjections: [injection], onclickmodal })
|
|
109
112
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest'
|
|
3
3
|
|
|
4
|
-
import Playlink from '
|
|
4
|
+
import Playlink from '../../../../routes/components/Playlinks/Playlink.svelte'
|
|
5
5
|
import { hasConsentedTo } from '$lib/consent'
|
|
6
6
|
|
|
7
7
|
vi.mock('$lib/tracking', () => ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest'
|
|
3
3
|
|
|
4
|
-
import PlaylinkIcon from '
|
|
4
|
+
import PlaylinkIcon from '../../../../routes/components/Playlinks/PlaylinkIcon.svelte'
|
|
5
5
|
|
|
6
6
|
describe('PlaylinkIcon.svelte', () => {
|
|
7
7
|
const playlink = { name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } }
|
package/src/tests/routes/components/{PlaylinkLabel.test.js → Playlinks/PlaylinkLabel.test.js}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest'
|
|
3
3
|
|
|
4
|
-
import PlaylinkLabel from '
|
|
4
|
+
import PlaylinkLabel from '../../../../routes/components/Playlinks/PlaylinkLabel.svelte'
|
|
5
5
|
|
|
6
6
|
describe('PlaylinkLabel.svelte', () => {
|
|
7
7
|
const playlink = { name: 'Some playlink', logo_url: 'logo', extra_info: { category: 'SVOD' } }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest'
|
|
3
3
|
|
|
4
|
-
import Playlinks from '
|
|
4
|
+
import Playlinks from '../../../../routes/components/Playlinks/Playlinks.svelte'
|
|
5
5
|
import { title } from '$lib/fakeData'
|
|
6
6
|
import { track } from '$lib/tracking'
|
|
7
7
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|