@playpilot/tpi 5.20.2 → 5.21.0-beta.share.2
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 +9 -9
- package/package.json +1 -1
- package/src/lib/clipboard.ts +14 -0
- package/src/lib/data/translations.ts +15 -0
- package/src/lib/enums/TrackingEvent.ts +3 -0
- package/src/lib/injection.ts +2 -2
- package/src/lib/text.ts +0 -4
- package/src/routes/components/Genres.svelte +4 -0
- package/src/routes/components/Icons/IconEmail.svelte +3 -0
- package/src/routes/components/Icons/IconLink.svelte +3 -0
- package/src/routes/components/Icons/IconShare.svelte +3 -0
- package/src/routes/components/Share.svelte +138 -0
- package/src/routes/components/Title.svelte +33 -12
- package/src/tests/lib/injections.test.js +0 -44
- package/src/tests/lib/text.test.js +0 -26
- package/src/tests/routes/components/Share.test.js +74 -0
package/package.json
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function copyToClipboard(text: string): void {
|
|
2
|
+
const textarea = document.createElement("textarea")
|
|
3
|
+
|
|
4
|
+
textarea.value = text
|
|
5
|
+
textarea.style.position = "fixed"
|
|
6
|
+
textarea.style.left = "-9999px"
|
|
7
|
+
|
|
8
|
+
document.body.appendChild(textarea)
|
|
9
|
+
|
|
10
|
+
textarea.select()
|
|
11
|
+
|
|
12
|
+
document.execCommand("copy")
|
|
13
|
+
document.body.removeChild(textarea)
|
|
14
|
+
}
|
|
@@ -91,6 +91,21 @@ export const translations = {
|
|
|
91
91
|
[Language.Swedish]: 'Se',
|
|
92
92
|
[Language.Danish]: 'Se',
|
|
93
93
|
},
|
|
94
|
+
'Share': {
|
|
95
|
+
[Language.English]: 'Share',
|
|
96
|
+
[Language.Swedish]: 'Dela',
|
|
97
|
+
[Language.Danish]: 'Del',
|
|
98
|
+
},
|
|
99
|
+
'Copy URL': {
|
|
100
|
+
[Language.English]: 'Copy URL',
|
|
101
|
+
[Language.Swedish]: 'Kopiera URL',
|
|
102
|
+
[Language.Danish]: 'Kopiér URL',
|
|
103
|
+
},
|
|
104
|
+
'Email': {
|
|
105
|
+
[Language.English]: 'Email',
|
|
106
|
+
[Language.Swedish]: 'E-post',
|
|
107
|
+
[Language.Danish]: 'E-mail',
|
|
108
|
+
},
|
|
94
109
|
|
|
95
110
|
// Genres
|
|
96
111
|
'All': {
|
package/src/lib/injection.ts
CHANGED
|
@@ -254,7 +254,7 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
addLinkInjectionEventListeners(
|
|
257
|
+
addLinkInjectionEventListeners(validInjections)
|
|
258
258
|
addCSSVariablesToLinks()
|
|
259
259
|
|
|
260
260
|
const afterArticleInjections = filterInvalidAfterArticleInjections(mergedInjections)
|
|
@@ -269,7 +269,7 @@ export function injectLinksInDocument(elements: HTMLElement[], injections: LinkI
|
|
|
269
269
|
|
|
270
270
|
const matchingElement = document.querySelector(`[${keyDataAttribute}="${injection.key}"]`)
|
|
271
271
|
const failed = isValidPlaylinkType(injection) && !injection.inactive && !injection.removed && !injection.after_article && !matchingElement
|
|
272
|
-
const containsSentence =
|
|
272
|
+
const containsSentence = !!elements.find(element => cleanPhrase(element.innerText).includes(cleanPhrase(injection.sentence)))
|
|
273
273
|
const failedMessage =
|
|
274
274
|
!failed ? '' :
|
|
275
275
|
failedMessages[injection.key] ||
|
package/src/lib/text.ts
CHANGED
|
@@ -61,11 +61,7 @@ export function replaceBetween(text: string, replacement: string, startIndex: nu
|
|
|
61
61
|
export function cleanPhrase(phrase: string): string {
|
|
62
62
|
return decodeHtmlEntities(phrase)
|
|
63
63
|
.toLowerCase()
|
|
64
|
-
.normalize('NFD')
|
|
65
|
-
.replace(/\p{Diacritic}/gu, '') // Replace accents and other diacritic symbols (https://stackoverflow.com/a/51874002/1665157)
|
|
66
64
|
.replace(/\s+/g, '') // Replace any number of white space with nothing
|
|
67
|
-
.replace(/['"‘’“”]/g, '"') // Replace all qoutes with the same type of quote
|
|
68
|
-
.replace(/^['"]+|['"]+$/g, '') // Remove leading and trailing quotes
|
|
69
65
|
.replace(/\.*$/, '') // Remove trailing periods
|
|
70
66
|
.replaceAll('…', '...') // Replace ellipsis character with regular characters
|
|
71
67
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg viewBox="0 -960 960 960" width="18px" height="18px">
|
|
2
|
+
<path fill="currentColor" d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg viewBox="0 -960 960 960" width="18px" height="18px">
|
|
2
|
+
<path fill="currentColor" d="M440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm200 160v-80h160q50 0 85-35t35-85q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480q0 83-58.5 141.5T680-280H520Z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg viewBox="0 -960 960 960" width="18px" height="18px">
|
|
2
|
+
<path d="M680-80q-50 0-85-35t-35-85q0-6 3-28L282-392q-16 15-37 23.5t-45 8.5q-50 0-85-35t-35-85q0-50 35-85t85-35q24 0 45 8.5t37 23.5l281-164q-2-7-2.5-13.5T560-760q0-50 35-85t85-35q50 0 85 35t35 85q0 50-35 85t-85 35q-24 0-45-8.5T598-672L317-508q2 7 2.5 13.5t.5 14.5q0 8-.5 14.5T317-452l281 164q16-15 37-23.5t45-8.5q50 0 85 35t35 85q0 50-35 85t-85 35Zm0-80q17 0 28.5-11.5T720-200q0-17-11.5-28.5T680-240q-17 0-28.5 11.5T640-200q0 17 11.5 28.5T680-160ZM200-440q17 0 28.5-11.5T240-480q0-17-11.5-28.5T200-520q-17 0-28.5 11.5T160-480q0 17 11.5 28.5T200-440Zm480-280q17 0 28.5-11.5T720-760q0-17-11.5-28.5T680-800q-17 0-28.5 11.5T640-760q0 17 11.5 28.5T680-720Zm0 520ZM200-480Zm480-280Z" fill="currentColor" />
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { scale } from 'svelte/transition'
|
|
3
|
+
import { mobileBreakpoint } from '$lib/constants'
|
|
4
|
+
import { copyToClipboard } from '$lib/clipboard'
|
|
5
|
+
import { track } from '$lib/tracking'
|
|
6
|
+
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
7
|
+
import { t } from '$lib/localization'
|
|
8
|
+
import IconShare from './Icons/IconShare.svelte'
|
|
9
|
+
import IconLink from './Icons/IconLink.svelte'
|
|
10
|
+
import IconEmail from './Icons/IconEmail.svelte'
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
title: string
|
|
14
|
+
url: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { title, url }: Props = $props()
|
|
18
|
+
|
|
19
|
+
const isMobile = window.innerWidth < mobileBreakpoint
|
|
20
|
+
const useShareApi = isMobile && typeof navigator.share !== 'undefined'
|
|
21
|
+
|
|
22
|
+
let showContextMenu = $state(false)
|
|
23
|
+
|
|
24
|
+
async function toggle(event: MouseEvent): Promise<void> {
|
|
25
|
+
event.stopPropagation()
|
|
26
|
+
|
|
27
|
+
if (useShareApi) {
|
|
28
|
+
await navigator.share({ title, url })
|
|
29
|
+
|
|
30
|
+
track(TrackingEvent.ShareTitle, null, { title, url, method: 'native' })
|
|
31
|
+
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
showContextMenu = !showContextMenu
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function copy() {
|
|
39
|
+
copyToClipboard(url)
|
|
40
|
+
track(TrackingEvent.ShareTitle, null, { title, url, method: 'copy' })
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function email() {
|
|
44
|
+
window.location.href = `mailto:?body=${url}`
|
|
45
|
+
track(TrackingEvent.ShareTitle, null, { title, url, method: 'email' })
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<svelte:window onclick={() => showContextMenu = false} />
|
|
50
|
+
|
|
51
|
+
<div class="share">
|
|
52
|
+
<button class="button" onclick={toggle} aria-label={t('Share')}>
|
|
53
|
+
<IconShare />
|
|
54
|
+
</button>
|
|
55
|
+
|
|
56
|
+
{#if showContextMenu}
|
|
57
|
+
<div class="context-menu" transition:scale={{ duration: 50, start: 0.85 }}>
|
|
58
|
+
<button class="item" onclick={copy}>
|
|
59
|
+
<IconLink /> {t('Copy URL')}
|
|
60
|
+
</button>
|
|
61
|
+
|
|
62
|
+
<button class="item" onclick={email}>
|
|
63
|
+
<IconEmail /> {t('Email')}
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
{/if}
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<style lang="scss">
|
|
70
|
+
.share {
|
|
71
|
+
position: relative;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.button {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
appearance: none;
|
|
80
|
+
border: 0;
|
|
81
|
+
border-radius: margin(3);
|
|
82
|
+
aspect-ratio: 1 / 1;
|
|
83
|
+
background: transparent;
|
|
84
|
+
color: var(--playpilot-detail-text-color-alt, var(--playpilot-text-color-alt));
|
|
85
|
+
|
|
86
|
+
&:hover {
|
|
87
|
+
color: var(--playpilot-detail-text-color, var(--playpilot-text-color));
|
|
88
|
+
box-shadow: 0 0 0 2px currentColor;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.context-menu {
|
|
93
|
+
--border-radius: var(--playpilot-context-menu-border-radius, #{margin(0.5)});
|
|
94
|
+
z-index: 10;
|
|
95
|
+
position: absolute;
|
|
96
|
+
bottom: calc(100% + margin(0.5));
|
|
97
|
+
right: 0;
|
|
98
|
+
max-width: margin(15);
|
|
99
|
+
border-radius: var(--border-radius);
|
|
100
|
+
background: var(--playpilot-context-menu-background, var(--playpilot-detail-background-light, var(--playpilot-lighter)));
|
|
101
|
+
box-shadow: var(--playpilot-shadow);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.item {
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
appearance: none;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
gap: margin(0.5);
|
|
110
|
+
border: 0;
|
|
111
|
+
padding: margin(0.75) margin(1);
|
|
112
|
+
width: 100%;
|
|
113
|
+
border-bottom: 1px solid var(--playpilot-context-menu-border, rgba(255, 255, 255, 0.1));
|
|
114
|
+
background: transparent;
|
|
115
|
+
white-space: nowrap;
|
|
116
|
+
color: var(--playpilot-detail-text-color, var(--playpilot-text-color));
|
|
117
|
+
font-family: inherit;
|
|
118
|
+
font-weight: normal;
|
|
119
|
+
text-align: left;
|
|
120
|
+
|
|
121
|
+
&:hover {
|
|
122
|
+
box-shadow: inset 0 0 0 2px currentColor;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&:first-child {
|
|
126
|
+
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&:last-child {
|
|
130
|
+
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
|
131
|
+
border-bottom: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
&:not(:hover) :global(svg) {
|
|
135
|
+
opacity: 0.75;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
</style>
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
import ParticipantsRail from './Rails/ParticipantsRail.svelte'
|
|
7
7
|
import SimilarRail from './Rails/SimilarRail.svelte'
|
|
8
8
|
import TitlePoster from './TitlePoster.svelte'
|
|
9
|
+
import Share from './Share.svelte'
|
|
9
10
|
import { t } from '$lib/localization'
|
|
10
11
|
import type { TitleData } from '$lib/types/title'
|
|
11
12
|
import { heading } from '$lib/actions/heading'
|
|
12
13
|
import { removeImageUrlPrefix } from '$lib/image'
|
|
14
|
+
import { titleUrl } from '$lib/routes'
|
|
13
15
|
|
|
14
16
|
interface Props {
|
|
15
17
|
title: TitleData
|
|
@@ -31,20 +33,26 @@
|
|
|
31
33
|
|
|
32
34
|
<div class="heading" use:heading={2} class:truncate={small} id="title">{title.title}</div>
|
|
33
35
|
|
|
34
|
-
<div class="
|
|
35
|
-
<div class="
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
<div class="row">
|
|
37
|
+
<div class="info">
|
|
38
|
+
<div class="imdb">
|
|
39
|
+
<IconIMDb />
|
|
40
|
+
{title.imdb_score}
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<Genres genres={title.genres} />
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
<div>{title.year}</div>
|
|
46
|
+
<div class="capitalize">{t(`Type: ${title.type}`)}</div>
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
{#if !small && title.length}
|
|
49
|
+
<div>{title.length} {t('Minutes')}</div>
|
|
50
|
+
{/if}
|
|
51
|
+
</div>
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
53
|
+
<div class="action">
|
|
54
|
+
<Share title={title.title} url={titleUrl(title)} />
|
|
55
|
+
</div>
|
|
48
56
|
</div>
|
|
49
57
|
</div>
|
|
50
58
|
|
|
@@ -138,11 +146,20 @@
|
|
|
138
146
|
}
|
|
139
147
|
}
|
|
140
148
|
|
|
149
|
+
.row {
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: flex-start;
|
|
152
|
+
}
|
|
153
|
+
|
|
141
154
|
.info {
|
|
142
155
|
display: flex;
|
|
143
156
|
flex-wrap: wrap;
|
|
144
157
|
align-items: center;
|
|
145
|
-
gap: margin(0.5) margin(
|
|
158
|
+
gap: margin(0.5) margin(0.75);
|
|
159
|
+
|
|
160
|
+
@include desktop() {
|
|
161
|
+
gap: margin(0.5) margin(1);
|
|
162
|
+
}
|
|
146
163
|
|
|
147
164
|
.small & {
|
|
148
165
|
gap: margin(0.5) margin(0.75);
|
|
@@ -165,6 +182,10 @@
|
|
|
165
182
|
}
|
|
166
183
|
}
|
|
167
184
|
|
|
185
|
+
.action {
|
|
186
|
+
margin: margin(-0.125) 0 0 auto;
|
|
187
|
+
}
|
|
188
|
+
|
|
168
189
|
.background {
|
|
169
190
|
position: absolute;
|
|
170
191
|
top: 0;
|
|
@@ -312,50 +312,6 @@ describe('linkInjection.js', () => {
|
|
|
312
312
|
expect(document.querySelectorAll('[data-playpilot-injection-key]')).toHaveLength(2)
|
|
313
313
|
})
|
|
314
314
|
|
|
315
|
-
it('Should disregard missing trailing periods', () => {
|
|
316
|
-
document.body.innerHTML = '<p>This is a sentence with a phrase in it</p>'
|
|
317
|
-
|
|
318
|
-
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
319
|
-
const injection = generateInjection('This is a sentence with a phrase in it.', 'a phrase')
|
|
320
|
-
|
|
321
|
-
injectLinksInDocument(elements, { aiInjections: [injection], manualInjections: [] })
|
|
322
|
-
|
|
323
|
-
expect(document.querySelectorAll('[data-playpilot-injection-key]')).toHaveLength(1)
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
it('Should disregard additional quotes', () => {
|
|
327
|
-
document.body.innerHTML = '<p>This is part of a paragraph. This is a sentence with a phrase in it."</p>'
|
|
328
|
-
|
|
329
|
-
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
330
|
-
const injection = generateInjection('"This is a sentence with a phrase in it."', 'a phrase')
|
|
331
|
-
|
|
332
|
-
injectLinksInDocument(elements, { aiInjections: [injection], manualInjections: [] })
|
|
333
|
-
|
|
334
|
-
expect(document.querySelectorAll('[data-playpilot-injection-key]')).toHaveLength(1)
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
it('Should disregard differences in accents', () => {
|
|
338
|
-
document.body.innerHTML = '<p>This is à sentence with a phràse in it."</p>'
|
|
339
|
-
|
|
340
|
-
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
341
|
-
const injection = generateInjection('This is a sentence with a phrase in it.', 'a phràse')
|
|
342
|
-
|
|
343
|
-
injectLinksInDocument(elements, { aiInjections: [injection], manualInjections: [] })
|
|
344
|
-
|
|
345
|
-
expect(document.querySelectorAll('[data-playpilot-injection-key]')).toHaveLength(1)
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
it('Should treat different types of quotes as the same', () => {
|
|
349
|
-
document.body.innerHTML = '<p>This is a “sentence” with a "phrase" in it.</p>'
|
|
350
|
-
|
|
351
|
-
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
352
|
-
const injection = generateInjection('This is a "sentence" with a \'phrase’ in it.', 'phrase')
|
|
353
|
-
|
|
354
|
-
injectLinksInDocument(elements, { aiInjections: [injection], manualInjections: [] })
|
|
355
|
-
|
|
356
|
-
expect(document.querySelectorAll('[data-playpilot-injection-key]')).toHaveLength(1)
|
|
357
|
-
})
|
|
358
|
-
|
|
359
315
|
it('Should leave the text intact if no injections were found', () => {
|
|
360
316
|
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
361
317
|
|
|
@@ -112,32 +112,6 @@ describe('text.js', () => {
|
|
|
112
112
|
it('Should return given phrase without trailing period', () => {
|
|
113
113
|
expect(cleanPhrase('Some phrase.')).toBe('somephrase')
|
|
114
114
|
})
|
|
115
|
-
|
|
116
|
-
it('Should return given phrase without leading or trailing quotes', () => {
|
|
117
|
-
expect(cleanPhrase('"Some phrase"')).toBe('somephrase')
|
|
118
|
-
expect(cleanPhrase('Some phrase"')).toBe('somephrase')
|
|
119
|
-
expect(cleanPhrase('"Some phrase')).toBe('somephrase')
|
|
120
|
-
|
|
121
|
-
expect(cleanPhrase('\'Some phrase\'')).toBe('somephrase')
|
|
122
|
-
expect(cleanPhrase('Some phrase\'')).toBe('somephrase')
|
|
123
|
-
expect(cleanPhrase('\'Some phrase')).toBe('somephrase')
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
it('Should return given phrase without leading or trailing quotes and trailing period', () => {
|
|
127
|
-
expect(cleanPhrase('"Some phrase."')).toBe('somephrase')
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('Should disregard accents and diacritics', () => {
|
|
131
|
-
expect(cleanPhrase('María')).toBe(cleanPhrase('Maria'))
|
|
132
|
-
expect(cleanPhrase('Göteborg')).toBe(cleanPhrase('Goteborg'))
|
|
133
|
-
expect(cleanPhrase('über')).toBe(cleanPhrase('uber'))
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('Should disregard differences in quotes', () => {
|
|
137
|
-
expect(cleanPhrase('Some "phrase"')).toBe(cleanPhrase('Some \'phrase\''))
|
|
138
|
-
expect(cleanPhrase('Some ”phrase“')).toBe(cleanPhrase('Some "phrase"'))
|
|
139
|
-
expect(cleanPhrase('Some ’phrase\'')).toBe(cleanPhrase('Some "phrase"'))
|
|
140
|
-
})
|
|
141
115
|
})
|
|
142
116
|
|
|
143
117
|
describe('truncateAroundPhrase', () => {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { render, fireEvent } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import Share from '../../../routes/components/Share.svelte'
|
|
5
|
+
import { copyToClipboard } from '$lib/clipboard'
|
|
6
|
+
import { track } from '$lib/tracking'
|
|
7
|
+
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
8
|
+
|
|
9
|
+
vi.mock('$lib/clipboard', () => ({
|
|
10
|
+
copyToClipboard: vi.fn(),
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
vi.mock('$lib/tracking', () => ({
|
|
14
|
+
track: vi.fn(),
|
|
15
|
+
}))
|
|
16
|
+
|
|
17
|
+
describe('Share.svelte', () => {
|
|
18
|
+
it('Should open context menu on click', async () => {
|
|
19
|
+
const { getByLabelText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
|
|
20
|
+
|
|
21
|
+
expect(queryByText('Copy URL')).not.toBeTruthy()
|
|
22
|
+
expect(queryByText('Email')).not.toBeTruthy()
|
|
23
|
+
|
|
24
|
+
await fireEvent.click(getByLabelText('Share'))
|
|
25
|
+
|
|
26
|
+
expect(queryByText('Copy URL')).toBeTruthy()
|
|
27
|
+
expect(queryByText('Email')).toBeTruthy()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('Should close context menu on click of items', async () => {
|
|
31
|
+
const { getByLabelText, getByText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
|
|
32
|
+
|
|
33
|
+
await fireEvent.click(getByLabelText('Share'))
|
|
34
|
+
await fireEvent.click(getByText('Copy URL'))
|
|
35
|
+
|
|
36
|
+
expect(queryByText('Copy URL')).not.toBeTruthy()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('Should close context menu on click of body', async () => {
|
|
40
|
+
const { getByLabelText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
|
|
41
|
+
|
|
42
|
+
await fireEvent.click(getByLabelText('Share'))
|
|
43
|
+
await fireEvent.click(document.body)
|
|
44
|
+
|
|
45
|
+
expect(queryByText('Copy URL')).not.toBeTruthy()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('Should fire copyToClipboard on click of button', async () => {
|
|
49
|
+
const { getByLabelText, getByText } = render(Share, { title: 'Some title', url: 'some-url' })
|
|
50
|
+
|
|
51
|
+
await fireEvent.click(getByLabelText('Share'))
|
|
52
|
+
await fireEvent.click(getByText('Copy URL'))
|
|
53
|
+
|
|
54
|
+
expect(copyToClipboard).toHaveBeenCalledWith('some-url')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('Should fire track function on click of copy URL button', async () => {
|
|
58
|
+
const { getByLabelText, getByText } = render(Share, { title: 'Some title', url: 'some-url' })
|
|
59
|
+
|
|
60
|
+
await fireEvent.click(getByLabelText('Share'))
|
|
61
|
+
await fireEvent.click(getByText('Copy URL'))
|
|
62
|
+
|
|
63
|
+
expect(track).toHaveBeenCalledWith(TrackingEvent.ShareTitle, null, { title: 'Some title', url: 'some-url', method: 'copy' })
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('Should fire track function on click of email button', async () => {
|
|
67
|
+
const { getByLabelText, getByText } = render(Share, { title: 'Some title', url: 'some-url' })
|
|
68
|
+
|
|
69
|
+
await fireEvent.click(getByLabelText('Share'))
|
|
70
|
+
await fireEvent.click(getByText('Email'))
|
|
71
|
+
|
|
72
|
+
expect(track).toHaveBeenCalledWith(TrackingEvent.ShareTitle, null, { title: 'Some title', url: 'some-url', method: 'email' })
|
|
73
|
+
})
|
|
74
|
+
})
|