@playpilot/tpi 3.8.2 → 3.10.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 -9
- package/eslint.config.js +15 -0
- package/package.json +1 -1
- package/src/lib/enums/TrackingEvent.ts +2 -0
- package/src/lib/linkInjection.ts +2 -1
- package/src/lib/tracking.ts +1 -1
- package/src/lib/types/playlink.d.ts +2 -0
- package/src/routes/components/ContextMenu.svelte +1 -2
- package/src/routes/components/Editorial/AIIndicator.svelte +6 -6
- package/src/routes/components/Editorial/Editor.svelte +3 -2
- package/src/routes/components/Editorial/EditorItem.svelte +23 -2
- package/src/routes/components/Editorial/ManualInjection.svelte +1 -1
- package/src/routes/components/Editorial/ReportIssueModal.svelte +156 -0
- package/src/routes/components/Modal.svelte +5 -2
- package/src/routes/components/Playlinks.svelte +22 -4
- package/src/routes/components/Popover.svelte +2 -1
- package/src/routes/components/Title.svelte +1 -1
- package/src/routes/components/TitlePopover.svelte +1 -1
- package/src/tests/lib/auth.test.js +1 -1
- package/src/tests/lib/linkInjection.test.js +1 -1
- package/src/tests/routes/components/Editorial/AiIndicator.test.js +7 -0
- package/src/tests/routes/components/Editorial/EditorItem.test.js +13 -4
- package/src/tests/routes/components/Editorial/ReportIssueModal.test.js +63 -0
- package/src/tests/routes/components/Playlinks.test.js +23 -3
package/eslint.config.js
CHANGED
|
@@ -48,6 +48,21 @@ export default [
|
|
|
48
48
|
'comma-dangle': ['error', 'always-multiline'],
|
|
49
49
|
'no-trailing-spaces': ['error'],
|
|
50
50
|
'indent': ['warn', 2],
|
|
51
|
+
'no-unused-vars': [
|
|
52
|
+
'error',
|
|
53
|
+
{
|
|
54
|
+
vars: 'all',
|
|
55
|
+
args: 'after-used',
|
|
56
|
+
ignoreRestSiblings: true,
|
|
57
|
+
argsIgnorePattern: '^_',
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
'prefer-const': [
|
|
61
|
+
'error',
|
|
62
|
+
{
|
|
63
|
+
destructuring: 'all',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
51
66
|
},
|
|
52
67
|
},
|
|
53
68
|
]
|
package/package.json
CHANGED
package/src/lib/linkInjection.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { cleanPhrase, findTextNodeContaining, isNodeInLink, replaceStartingFrom
|
|
|
6
6
|
import type { LinkInjection, LinkInjectionTypes } from './types/injection'
|
|
7
7
|
import { isHoldingSpecialKey } from './event'
|
|
8
8
|
import { playFallbackViewTransition } from './viewTransition'
|
|
9
|
+
import { prefersReducedMotion } from 'svelte/motion'
|
|
9
10
|
|
|
10
11
|
const keyDataAttribute = 'data-playpilot-injection-key'
|
|
11
12
|
const keySelector = `[${keyDataAttribute}]`
|
|
@@ -243,7 +244,7 @@ function addLinkInjectionEventListeners(injections: LinkInjection[]): void {
|
|
|
243
244
|
playFallbackViewTransition(() => {
|
|
244
245
|
destroyLinkPopover(false)
|
|
245
246
|
openLinkModal(event, injection)
|
|
246
|
-
}, window.innerWidth >= 600 && !window.matchMedia("(pointer: coarse)").matches)
|
|
247
|
+
}, !prefersReducedMotion.current && window.innerWidth >= 600 && !window.matchMedia("(pointer: coarse)").matches)
|
|
247
248
|
})
|
|
248
249
|
|
|
249
250
|
window.addEventListener('mousemove', (event) => {
|
package/src/lib/tracking.ts
CHANGED
|
@@ -10,7 +10,7 @@ const baseUrl = 'https://insights.playpilot.net'
|
|
|
10
10
|
* @param [title] Title related to the event
|
|
11
11
|
* @param [payload] Any data that will be included with the event
|
|
12
12
|
*/
|
|
13
|
-
export async function track(event: string, title: TitleData | null = null, payload: Record<string, string | number | null> = {}): Promise<void> {
|
|
13
|
+
export async function track(event: string, title: TitleData | null = null, payload: Record<string, string | number | null | undefined> = {}): Promise<void> {
|
|
14
14
|
const headers = new Headers({ 'Content-Type': 'application/json' })
|
|
15
15
|
|
|
16
16
|
if (title) {
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
5
|
aiRunning?: boolean
|
|
6
|
-
automationEnabled?: boolean
|
|
7
|
-
message?: string
|
|
8
|
-
percentage?: number
|
|
9
|
-
aiInjectionsCount?: number
|
|
6
|
+
automationEnabled?: boolean
|
|
7
|
+
message?: string
|
|
8
|
+
percentage?: number
|
|
9
|
+
aiInjectionsCount?: number
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const { aiRunning = false, automationEnabled = false, message = '', percentage = 0, aiInjectionsCount = 0 }: Props = $props()
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
{message}
|
|
34
34
|
|
|
35
35
|
<span class="ellipses">
|
|
36
|
-
{#each { length: 3 }}
|
|
36
|
+
{#each { length: 3 } as _}
|
|
37
37
|
<span>.</span>
|
|
38
38
|
{/each}
|
|
39
39
|
</span>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
<div class="loading-bar-fill" data-testid="loading-bar" style:width="{Math.max(percentage, 3)}%"></div>
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
|
-
<div class="loading-bar-label">{percentage}%</div>
|
|
47
|
+
<div class="loading-bar-label">{Math.floor(percentage)}%</div>
|
|
48
48
|
</div>
|
|
49
49
|
{:else}
|
|
50
50
|
<p>
|
|
@@ -43,9 +43,10 @@
|
|
|
43
43
|
const editorPositionKey = 'editor-position'
|
|
44
44
|
const editorHeightKey = 'editor-height'
|
|
45
45
|
|
|
46
|
+
const position: Position = JSON.parse(localStorage.getItem(editorPositionKey) || '{ "x": 0, "y": 0 }')
|
|
47
|
+
const height: number = parseInt(localStorage.getItem(editorHeightKey) || '0')
|
|
48
|
+
|
|
46
49
|
let editorElement: HTMLElement | null = $state(null)
|
|
47
|
-
let position: Position = $state(JSON.parse(localStorage.getItem(editorPositionKey) || '{ "x": 0, "y": 0 }'))
|
|
48
|
-
let height: number = $state(parseInt(localStorage.getItem(editorHeightKey) || '0'))
|
|
49
50
|
let manualInjectionActive = $state(false)
|
|
50
51
|
let saving = $state(false)
|
|
51
52
|
let hasError = $state(false)
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import PlaylinkTypeSelect from './PlaylinkTypeSelect.svelte'
|
|
8
8
|
import Alert from './Alert.svelte'
|
|
9
9
|
import ContextMenu from '../ContextMenu.svelte'
|
|
10
|
-
import { onMount } from 'svelte'
|
|
10
|
+
import { mount, onMount, unmount } from 'svelte'
|
|
11
11
|
import { track } from '$lib/tracking'
|
|
12
12
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
13
13
|
import type { LinkInjection } from '$lib/types/injection'
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { getLinkInjectionElements, getLinkInjectionsParentElement, isValidPlaylinkType } from '$lib/linkInjection'
|
|
17
17
|
import { imagePlaceholderDataUrl } from '$lib/constants'
|
|
18
18
|
import { removeImageUrlPrefix } from '$lib/image'
|
|
19
|
+
import ReportIssueModal from './ReportIssueModal.svelte'
|
|
19
20
|
|
|
20
21
|
interface Props {
|
|
21
22
|
linkInjection: LinkInjection,
|
|
@@ -90,6 +91,13 @@
|
|
|
90
91
|
return cleanPhrase(element.innerText).includes(cleanPhrase(linkInjection.sentence))
|
|
91
92
|
}) || []
|
|
92
93
|
}
|
|
94
|
+
|
|
95
|
+
function showReportIssueModal(): void {
|
|
96
|
+
const component: any = mount(ReportIssueModal, {
|
|
97
|
+
target: document.body,
|
|
98
|
+
props: { linkInjection, onclose: () => unmount(component) },
|
|
99
|
+
})
|
|
100
|
+
}
|
|
93
101
|
</script>
|
|
94
102
|
|
|
95
103
|
<svelte:window on:mouseover={setInEditorHighlight} />
|
|
@@ -126,6 +134,7 @@
|
|
|
126
134
|
<div class="context-menu">
|
|
127
135
|
<ContextMenu ariaLabel="More options">
|
|
128
136
|
<button class="context-menu-action" onclick={onremove}>Remove</button>
|
|
137
|
+
<button class="context-menu-action" onclick={showReportIssueModal}>Report issue</button>
|
|
129
138
|
</ContextMenu>
|
|
130
139
|
</div>
|
|
131
140
|
</div>
|
|
@@ -257,13 +266,25 @@
|
|
|
257
266
|
appearance: none;
|
|
258
267
|
background: transparent;
|
|
259
268
|
border: 0;
|
|
260
|
-
padding: margin(1);
|
|
269
|
+
padding: margin(0.5) margin(1);
|
|
270
|
+
width: 100%;
|
|
271
|
+
text-align: left;
|
|
261
272
|
font-family: inherit;
|
|
273
|
+
white-space: nowrap;
|
|
262
274
|
color: var(--playpilot-text-color-alt);
|
|
263
275
|
cursor: pointer;
|
|
264
276
|
|
|
265
277
|
&:hover {
|
|
266
278
|
color: var(--playpilot-text-color);
|
|
279
|
+
background-color: var(--playpilot-content-light);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
&:first-child {
|
|
283
|
+
margin-top: margin(0.5);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
&:last-child {
|
|
287
|
+
margin-bottom: margin(0.5);
|
|
267
288
|
}
|
|
268
289
|
}
|
|
269
290
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { heading } from '$lib/actions/heading'
|
|
3
|
+
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
4
|
+
import { track } from '$lib/tracking'
|
|
5
|
+
import { truncateAroundPhrase } from '$lib/text'
|
|
6
|
+
import type { LinkInjection } from '$lib/types/injection'
|
|
7
|
+
import Modal from '../Modal.svelte'
|
|
8
|
+
import { fade } from 'svelte/transition'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
onclose: () => void,
|
|
12
|
+
linkInjection: LinkInjection
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { onclose, linkInjection }: Props = $props()
|
|
16
|
+
|
|
17
|
+
const options = [{
|
|
18
|
+
value: 'failed_injection',
|
|
19
|
+
label: 'The link failed to appear on the page.',
|
|
20
|
+
}, {
|
|
21
|
+
value: 'layout_issue',
|
|
22
|
+
label: 'The link is breaking the layout or formatting of my page.',
|
|
23
|
+
}, {
|
|
24
|
+
value: 'wrong_content',
|
|
25
|
+
label: 'The link points to the wrong movie or show.',
|
|
26
|
+
}, {
|
|
27
|
+
value: 'inappropriate_title',
|
|
28
|
+
label: 'The link points to an inappropriate movie or show.',
|
|
29
|
+
}, {
|
|
30
|
+
value: 'broken_link',
|
|
31
|
+
label: 'The link is broken or leads to an error page.',
|
|
32
|
+
}, {
|
|
33
|
+
value: 'other',
|
|
34
|
+
label: 'My issue is not listed, but please have a look regardless.',
|
|
35
|
+
}]
|
|
36
|
+
|
|
37
|
+
let reason = $state('')
|
|
38
|
+
let reportSent = $state(false)
|
|
39
|
+
|
|
40
|
+
function sendReport() {
|
|
41
|
+
if (!reason) return
|
|
42
|
+
|
|
43
|
+
track(TrackingEvent.ManualReport, linkInjection.title_details, {
|
|
44
|
+
report_reason: reason,
|
|
45
|
+
sid: linkInjection.sid,
|
|
46
|
+
title: linkInjection.title,
|
|
47
|
+
sentence: linkInjection.sentence,
|
|
48
|
+
failed: linkInjection.failed?.toString(),
|
|
49
|
+
failed_message: linkInjection.failed_message,
|
|
50
|
+
manual: linkInjection.manual?.toString(),
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
reportSent = true
|
|
54
|
+
setTimeout(onclose, 3000)
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<Modal {onclose}>
|
|
59
|
+
<div class="form">
|
|
60
|
+
<div class="heading" use:heading={2}>Report issue</div>
|
|
61
|
+
|
|
62
|
+
<div class="info">
|
|
63
|
+
<div><strong>Title</strong>: {linkInjection.title}</div>
|
|
64
|
+
<div>
|
|
65
|
+
<strong>Sentence</strong>:
|
|
66
|
+
{truncateAroundPhrase(linkInjection.sentence, linkInjection.title, 100)}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<label for="reason">What would you like to report?</label>
|
|
71
|
+
<select id="reason" name="reason" bind:value={reason}>
|
|
72
|
+
<option value=''>Select an issue...</option>
|
|
73
|
+
|
|
74
|
+
{#each options as { value, label }}
|
|
75
|
+
<!-- This onclick exists purely for Vitest, the bind:value above would just not work :( -->
|
|
76
|
+
<option {value} onclick={() => reason = value}>{label}</option>
|
|
77
|
+
{/each}
|
|
78
|
+
</select>
|
|
79
|
+
|
|
80
|
+
<div>
|
|
81
|
+
{#if reportSent}
|
|
82
|
+
<div class="sent" in:fade={{ duration: 200 }}>Report has been sent, thank you! Closing window...</div>
|
|
83
|
+
{:else}
|
|
84
|
+
<button class="submit" onclick={sendReport} disabled={!reason}>Send report</button>
|
|
85
|
+
{/if}
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</Modal>
|
|
89
|
+
|
|
90
|
+
<style lang="scss">
|
|
91
|
+
select {
|
|
92
|
+
background: var(--playpilot-content);
|
|
93
|
+
padding: margin(0.5) margin(1);
|
|
94
|
+
width: 100%;
|
|
95
|
+
border: 0;
|
|
96
|
+
border-radius: margin(0.5);
|
|
97
|
+
color: var(--playpilot-text-color);
|
|
98
|
+
font-family: inherit;
|
|
99
|
+
font-size: margin(0.85);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
option:hover,
|
|
103
|
+
option:active,
|
|
104
|
+
option:checked {
|
|
105
|
+
background-color: var(--playpilot-content-light);
|
|
106
|
+
font-weight: bold;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.form {
|
|
110
|
+
padding: margin(2);
|
|
111
|
+
font-family: var(--playpilot-font-family);
|
|
112
|
+
color: var(--playpilot-text-color);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.heading {
|
|
116
|
+
font-size: margin(1.25);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.info {
|
|
120
|
+
margin: margin(1) 0;
|
|
121
|
+
font-size: margin(0.85);
|
|
122
|
+
color: var(--playpilot-text-color-alt);
|
|
123
|
+
|
|
124
|
+
strong {
|
|
125
|
+
color: var(--playpilot-text-color);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.submit {
|
|
130
|
+
margin-top: margin(1);
|
|
131
|
+
padding: margin(0.5) margin(1);
|
|
132
|
+
border: 0;
|
|
133
|
+
border-radius: margin(2);
|
|
134
|
+
background: var(--playpilot-green);
|
|
135
|
+
box-shadow: var(--playpilot-shadow);
|
|
136
|
+
font-family: inherit;
|
|
137
|
+
color: var(--playpilot-text-color);
|
|
138
|
+
font-size: margin(0.85);
|
|
139
|
+
cursor: pointer;
|
|
140
|
+
|
|
141
|
+
&[disabled] {
|
|
142
|
+
background: var(--playpilot-content);
|
|
143
|
+
color: var(--playpilot-text-color-alt);
|
|
144
|
+
box-shadow: none;
|
|
145
|
+
opacity: 0.5;
|
|
146
|
+
cursor: default;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.sent {
|
|
151
|
+
margin-top: margin(1);
|
|
152
|
+
padding: margin(0.5) 0;
|
|
153
|
+
color: var(--playpilot-green);
|
|
154
|
+
font-size: margin(0.85);
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import IconClose from './Icons/IconClose.svelte'
|
|
4
4
|
import RoundButton from './RoundButton.svelte'
|
|
5
5
|
import { onMount, setContext, type Snippet } from 'svelte'
|
|
6
|
+
import { prefersReducedMotion } from 'svelte/motion'
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
children: Snippet
|
|
@@ -22,6 +23,8 @@
|
|
|
22
23
|
})
|
|
23
24
|
|
|
24
25
|
function scaleOrFly(node: Element): TransitionConfig {
|
|
26
|
+
if (prefersReducedMotion.current) return fade(node, { duration: 0 })
|
|
27
|
+
|
|
25
28
|
const shouldFly = window.innerWidth < 600
|
|
26
29
|
|
|
27
30
|
if (shouldFly) return fly(node, { duration: 250, y: window.innerHeight })
|
|
@@ -31,8 +34,8 @@
|
|
|
31
34
|
|
|
32
35
|
<svelte:window on:keydown={({ key }) => { if (key === 'Escape') onclose() }} />
|
|
33
36
|
|
|
34
|
-
<div class="modal" transition:fade={{ duration: 150 }}>
|
|
35
|
-
<div class="dialog" {onscroll} role="dialog" transition:scaleOrFly|global data-view-transition-new>
|
|
37
|
+
<div class="modal" transition:fade|global={{ duration: 150 }}>
|
|
38
|
+
<div class="dialog" {onscroll} role="dialog" aria-labelledby="title" transition:scaleOrFly|global data-view-transition-new>
|
|
36
39
|
<div class="close">
|
|
37
40
|
<RoundButton onclick={() => onclose()}>
|
|
38
41
|
<IconClose />
|
|
@@ -36,16 +36,16 @@
|
|
|
36
36
|
}
|
|
37
37
|
</script>
|
|
38
38
|
|
|
39
|
-
<div class="heading" use:heading={
|
|
39
|
+
<div class="heading" use:heading={3}>{t('Where To Stream Online')}</div>
|
|
40
40
|
|
|
41
41
|
<div class="playlinks" class:list>
|
|
42
|
-
{#each mergedPlaylink as { name, url, logo_url, extra_info: { category } }}
|
|
43
|
-
<a href={url} target="_blank" class="playlink" onclick={() => onclick(name)} data-playlink={name} rel="sponsored">
|
|
42
|
+
{#each mergedPlaylink as { name, url, logo_url, highlighted, cta_text, extra_info: { category } }}
|
|
43
|
+
<a href={url} target="_blank" class="playlink" class:highlighted={highlighted && cta_text} onclick={() => onclick(name)} data-playlink={name} rel="sponsored">
|
|
44
44
|
<img src={removeImageUrlPrefix(logo_url)} alt="" height="32" width="32" />
|
|
45
45
|
|
|
46
46
|
<div>
|
|
47
47
|
<span class="name">{name}</span>
|
|
48
|
-
<div class="category">{categoryStrings[category] || t('Stream')}</div>
|
|
48
|
+
<div class="category">{cta_text || categoryStrings[category] || t('Stream')}</div>
|
|
49
49
|
</div>
|
|
50
50
|
|
|
51
51
|
{#if list}
|
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
.playlink {
|
|
115
|
+
position: relative;
|
|
115
116
|
display: flex;
|
|
116
117
|
align-items: center;
|
|
117
118
|
gap: margin(0.75);
|
|
@@ -134,6 +135,23 @@
|
|
|
134
135
|
text-decoration: none !important;
|
|
135
136
|
}
|
|
136
137
|
|
|
138
|
+
&.highlighted {
|
|
139
|
+
&::before {
|
|
140
|
+
content: "";
|
|
141
|
+
z-index: 1;
|
|
142
|
+
display: block;
|
|
143
|
+
position: absolute;
|
|
144
|
+
top: 0;
|
|
145
|
+
right: 0;
|
|
146
|
+
bottom: 0;
|
|
147
|
+
left: 0;
|
|
148
|
+
border-radius: inherit;
|
|
149
|
+
box-shadow: inset 0 0 0 2px currentColor;
|
|
150
|
+
opacity: 0.35;
|
|
151
|
+
pointer-events: none;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
137
155
|
img {
|
|
138
156
|
margin: 0;
|
|
139
157
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount, setContext, tick, type Snippet } from 'svelte'
|
|
3
|
+
import { prefersReducedMotion } from 'svelte/motion'
|
|
3
4
|
import { fly } from 'svelte/transition'
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
</script>
|
|
60
61
|
|
|
61
62
|
<div class="popover" class:flip bind:this={element} style:--max-height={maxHeight ? maxHeight + 'px' : null} tabindex="-1" aria-hidden="true">
|
|
62
|
-
<div class="dialog" transition:fly|global={{ duration: 100, y: 10 }} data-view-transition-old>
|
|
63
|
+
<div class="dialog" transition:fly|global={{ duration: prefersReducedMotion.current ? 0 : 100, y: 10 }} data-view-transition-old>
|
|
63
64
|
{@render children()}
|
|
64
65
|
</div>
|
|
65
66
|
</div>
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
}
|
|
43
43
|
</script>
|
|
44
44
|
|
|
45
|
-
<div class="title-popover" bind:this={element} data-playpilot-title-popover>
|
|
45
|
+
<div class="title-popover" bind:this={element} data-playpilot-title-popover role="region" aria-labelledby="title">
|
|
46
46
|
<Popover bind:maxHeight>
|
|
47
47
|
<Title {title} small compact={!!maxHeight && maxHeight < 250} />
|
|
48
48
|
</Popover>
|
|
@@ -70,7 +70,7 @@ describe('$lib/auth', () => {
|
|
|
70
70
|
|
|
71
71
|
it('Should not authorize is fetch response was negative', async () => {
|
|
72
72
|
fakeFetch({ response: '', ok: false, status: 403 })
|
|
73
|
-
|
|
73
|
+
const authorized = await authorize('https://example.com/some-path?articleReplacementEditToken=some-token')
|
|
74
74
|
|
|
75
75
|
expect(authorized).not.toBeTruthy()
|
|
76
76
|
expect(track).toHaveBeenCalled()
|
|
@@ -804,7 +804,7 @@ describe('linkInjection.js', () => {
|
|
|
804
804
|
const result = getLinkInjectionElements(parent, '[data-exclude]')
|
|
805
805
|
expect(result).toHaveLength(1)
|
|
806
806
|
expect(result[0].innerText).toBe('I am a regular element')
|
|
807
|
-
})
|
|
807
|
+
})
|
|
808
808
|
|
|
809
809
|
it('Should return paragraphs fully even if they contain no direct text nodes, skipping empty paragraphs', () => {
|
|
810
810
|
document.body.innerHTML = `<section>
|
|
@@ -38,6 +38,13 @@ describe('AIIndicator.svelte', () => {
|
|
|
38
38
|
expect(getByText('25%')).toBeTruthy()
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
+
it('Should floor the percentage label if a decimal is given', () => {
|
|
42
|
+
const { getByText, getByTestId } = render(AIIndicator, { aiRunning: true, automationEnabled: true, percentage: 25.55 })
|
|
43
|
+
|
|
44
|
+
expect(getByTestId('loading-bar').style.width).toBe('25.55%')
|
|
45
|
+
expect(getByText('25%')).toBeTruthy()
|
|
46
|
+
})
|
|
47
|
+
|
|
41
48
|
it('Should limit progress to minimum value in loading bar style only', () => {
|
|
42
49
|
const { getByText, getByTestId } = render(AIIndicator, { aiRunning: true, automationEnabled: true, percentage: 0 })
|
|
43
50
|
|
|
@@ -165,14 +165,14 @@ describe('EditorItem.svelte', () => {
|
|
|
165
165
|
expect(onremove).toHaveBeenCalled()
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
-
it('Should display an inactive injection as being so with text and classname',
|
|
168
|
+
it('Should display an inactive injection as being so with text and classname', () => {
|
|
169
169
|
const { getByText, container } = render(EditorItem, { linkInjection: { ...linkInjection, inactive: true } })
|
|
170
170
|
|
|
171
171
|
expect(getByText('Inactive')).toBeTruthy()
|
|
172
172
|
expect(container.querySelector('.inactive')).toBeTruthy()
|
|
173
173
|
})
|
|
174
174
|
|
|
175
|
-
it('Should not display an active injection as being inactive',
|
|
175
|
+
it('Should not display an active injection as being inactive', () => {
|
|
176
176
|
const { queryByText, container } = render(EditorItem, { linkInjection })
|
|
177
177
|
|
|
178
178
|
expect(queryByText('Inactive')).not.toBeTruthy()
|
|
@@ -180,15 +180,24 @@ describe('EditorItem.svelte', () => {
|
|
|
180
180
|
expect(container.querySelector('.inactive')).not.toBeTruthy()
|
|
181
181
|
})
|
|
182
182
|
|
|
183
|
-
it('Should display an icon when playlink types are invalid',
|
|
183
|
+
it('Should display an icon when playlink types are invalid', () => {
|
|
184
184
|
const { getByLabelText } = render(EditorItem, { linkInjection: { ...linkInjection, in_text: false } })
|
|
185
185
|
|
|
186
186
|
expect(getByLabelText('Invalid playlink settings')).toBeTruthy()
|
|
187
187
|
})
|
|
188
188
|
|
|
189
|
-
it('Should not display an icon when playlink types are valid',
|
|
189
|
+
it('Should not display an icon when playlink types are valid', () => {
|
|
190
190
|
const { queryByLabelText } = render(EditorItem, { linkInjection: { ...linkInjection, in_text: true } })
|
|
191
191
|
|
|
192
192
|
expect(queryByLabelText('Invalid playlink settings')).not.toBeTruthy()
|
|
193
193
|
})
|
|
194
|
+
|
|
195
|
+
it('Should mount component when clicking report issue button', async () => {
|
|
196
|
+
const { getByLabelText, getByText } = render(EditorItem, { linkInjection })
|
|
197
|
+
|
|
198
|
+
await fireEvent.click(getByLabelText('More options'))
|
|
199
|
+
await fireEvent.click(getByText('Report issue'))
|
|
200
|
+
|
|
201
|
+
expect(getByText('What would you like to report?')).toBeTruthy()
|
|
202
|
+
})
|
|
194
203
|
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import ReportIssueModal from '../../../../routes/components/Editorial/ReportIssueModal.svelte'
|
|
5
|
+
import { track } from '$lib/tracking'
|
|
6
|
+
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
7
|
+
import { generateInjection } from '../../../helpers'
|
|
8
|
+
|
|
9
|
+
vi.mock('$lib/tracking', () => ({
|
|
10
|
+
track: vi.fn(),
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
describe('ReportIssueModal.svelte', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.resetAllMocks()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const onclose = vi.fn()
|
|
19
|
+
const linkInjection = generateInjection('a word', 'Some sentence with a word')
|
|
20
|
+
|
|
21
|
+
it('Should enable button after selecting issue', async () => {
|
|
22
|
+
const { getByRole, getAllByRole, getByText } = render(ReportIssueModal, { onclose, linkInjection })
|
|
23
|
+
|
|
24
|
+
expect(/** @type {HTMLButtonElement } */ (getByText('Send report')).disabled).toBeTruthy()
|
|
25
|
+
|
|
26
|
+
await fireEvent.click(getByRole('combobox'))
|
|
27
|
+
await fireEvent.click(getAllByRole('option')[1])
|
|
28
|
+
|
|
29
|
+
expect(/** @type {HTMLButtonElement } */ (getByText('Send report')).disabled).not.toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('Should submit form when clicked', async () => {
|
|
33
|
+
const { getByRole, getAllByRole, getByText } = render(ReportIssueModal, { onclose, linkInjection })
|
|
34
|
+
|
|
35
|
+
await fireEvent.click(getByRole('combobox'))
|
|
36
|
+
await fireEvent.click(getAllByRole('option')[1])
|
|
37
|
+
await fireEvent.click(getByText('Send report'))
|
|
38
|
+
|
|
39
|
+
expect(track).toHaveBeenCalledWith(
|
|
40
|
+
TrackingEvent.ManualReport,
|
|
41
|
+
linkInjection.title_details,
|
|
42
|
+
{
|
|
43
|
+
report_reason: /** @type {HTMLOptionElement} */ (getAllByRole('option')[1]).value,
|
|
44
|
+
sid: linkInjection.sid,
|
|
45
|
+
title: linkInjection.title,
|
|
46
|
+
sentence: linkInjection.sentence,
|
|
47
|
+
failed: linkInjection.failed?.toString(),
|
|
48
|
+
failed_message: linkInjection.failed_message,
|
|
49
|
+
manual: linkInjection.manual?.toString(),
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
expect(getByText('Report has been sent', { exact: false }))
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('Should not submit form when no reason is selected', async () => {
|
|
57
|
+
const { getByText } = render(ReportIssueModal, { onclose, linkInjection })
|
|
58
|
+
|
|
59
|
+
await fireEvent.click(getByText('Send report'))
|
|
60
|
+
|
|
61
|
+
expect(track).not.toHaveBeenCalled()
|
|
62
|
+
})
|
|
63
|
+
})
|