@playpilot/tpi 3.6.1 → 3.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/link-injections.js +8 -7
- package/package.json +1 -1
- package/src/lib/api.ts +7 -8
- package/src/lib/linkInjection.ts +1 -1
- package/src/routes/+layout.svelte +2 -0
- package/src/routes/+page.svelte +13 -8
- package/src/routes/components/Editorial/Editor.svelte +5 -5
- package/src/routes/components/Editorial/ManualInjection.svelte +3 -3
- package/src/routes/components/Editorial/Session.svelte +4 -4
- package/src/routes/components/Genres.svelte +1 -1
- package/src/routes/components/Playlinks.svelte +3 -3
- package/src/tests/lib/api.test.js +15 -15
- package/src/tests/lib/linkInjection.test.js +3 -3
- package/src/tests/routes/+page.test.js +41 -4
- package/src/tests/routes/components/Editorial/ManualInjection.test.js +8 -8
- package/src/tests/routes/components/Popover.test.js +1 -1
package/package.json
CHANGED
package/src/lib/api.ts
CHANGED
|
@@ -11,13 +11,13 @@ let pollTimeout: ReturnType<typeof setTimeout> | null = null
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Fetch link injections for a URL.
|
|
14
|
-
* @param
|
|
14
|
+
* @param pageText Text content of the article
|
|
15
15
|
* @param options
|
|
16
16
|
* @param [options.url] URL of the given article
|
|
17
17
|
* @param [options.hash] unique key to identify the HTML
|
|
18
18
|
* @param [options.params] Any rest params to include in the request body
|
|
19
19
|
*/
|
|
20
|
-
export async function fetchLinkInjections(
|
|
20
|
+
export async function fetchLinkInjections(pageText: string, { url = getFullUrlPath(), hash = stringToHash(pageText), params = {} }: { url?: string, hash?: string; params?: object } = {}): Promise<LinkInjectionResponse> {
|
|
21
21
|
const headers = new Headers({ 'Content-Type': 'application/json' })
|
|
22
22
|
const apiToken = getApiToken()
|
|
23
23
|
const isEditorialMode = isEditorialModeEnabled() ? await authorize() : false
|
|
@@ -33,7 +33,7 @@ export async function fetchLinkInjections(html: string, { url = getFullUrlPath()
|
|
|
33
33
|
hash,
|
|
34
34
|
url,
|
|
35
35
|
metadata,
|
|
36
|
-
page_text:
|
|
36
|
+
page_text: pageText,
|
|
37
37
|
...params,
|
|
38
38
|
})),
|
|
39
39
|
})
|
|
@@ -48,10 +48,9 @@ export async function fetchLinkInjections(html: string, { url = getFullUrlPath()
|
|
|
48
48
|
/**
|
|
49
49
|
* Link injections take a while to be ready. During this time we poll the endpoint until it returns the result we want.
|
|
50
50
|
* The results return `injections_ready=false` while the injections are not yet ready.
|
|
51
|
-
* @param html HTML to be crawled
|
|
52
51
|
*/
|
|
53
52
|
export async function pollLinkInjections(
|
|
54
|
-
|
|
53
|
+
pageText: string,
|
|
55
54
|
{ requireCompletedResult = false, pollInterval = 3000, maxTries = 600, onpoll = () => null }:
|
|
56
55
|
{ requireCompletedResult?: boolean, pollInterval?: number, maxTries?: number, onpoll?: (_response: LinkInjectionResponse) => void } = {}
|
|
57
56
|
): Promise<LinkInjectionResponse> {
|
|
@@ -69,7 +68,7 @@ export async function pollLinkInjections(
|
|
|
69
68
|
const poll = async (resolve: Function, reject: Function): Promise<void> => {
|
|
70
69
|
let response
|
|
71
70
|
try {
|
|
72
|
-
response = await fetchLinkInjections(
|
|
71
|
+
response = await fetchLinkInjections(pageText)
|
|
73
72
|
|
|
74
73
|
if (requireCompletedResult && (response.automation_enabled && response.ai_running)) throw new Error
|
|
75
74
|
|
|
@@ -96,7 +95,7 @@ export async function pollLinkInjections(
|
|
|
96
95
|
return new Promise(poll)
|
|
97
96
|
}
|
|
98
97
|
|
|
99
|
-
export async function saveLinkInjections(linkInjections: LinkInjection[],
|
|
98
|
+
export async function saveLinkInjections(linkInjections: LinkInjection[], pageText: string): Promise<LinkInjection[]> {
|
|
100
99
|
const selector = window.PlayPilotLinkInjections?.selector
|
|
101
100
|
|
|
102
101
|
// Only save manual injections, AI injections should be left intact.
|
|
@@ -114,7 +113,7 @@ export async function saveLinkInjections(linkInjections: LinkInjection[], html:
|
|
|
114
113
|
removed: !!linkInjection.removed
|
|
115
114
|
}))
|
|
116
115
|
|
|
117
|
-
const response = await fetchLinkInjections(
|
|
116
|
+
const response = await fetchLinkInjections(pageText, {
|
|
118
117
|
params: {
|
|
119
118
|
private_token: getAuthToken(),
|
|
120
119
|
link_injections: newLinkInjections,
|
package/src/lib/linkInjection.ts
CHANGED
|
@@ -31,7 +31,7 @@ export function getLinkInjectionElements(parentElement: HTMLElement): HTMLElemen
|
|
|
31
31
|
if (validElements.includes(element)) continue
|
|
32
32
|
|
|
33
33
|
// Ignore links, buttons, and headers
|
|
34
|
-
if (/^(A|BUTTON|SCRIPT|NOSCRIPT|STYLE|IFRAME|FIGCAPTION|TIME|
|
|
34
|
+
if (/^(A|BUTTON|SCRIPT|NOSCRIPT|STYLE|IFRAME|FIGCAPTION|TIME|H1)$/.test(element.tagName)) continue
|
|
35
35
|
|
|
36
36
|
// Check if this element has a direct text node
|
|
37
37
|
const hasTextNode = Array.from(element.childNodes).some(
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
<time datetime="14:00">1 hour ago</time>
|
|
29
29
|
<p use:noClass>Following the success of John M. Chu's 2018 romantic-comedy Crazy Rich Asians, Quan was inspired to return to acting. He first scored a supporting role in the Netflix movie Finding 'Ohana, before securing a starring role in the absurdist comedy-drama Everything Everywhere all At Once. A critical and commercial success, the film earned $143 million against a budget of $14-25 million, and saw Quan win the Academy Award for Best Supporting Actor. Following his win, Quan struggled to choose projects he was satisfied with, passing on an action-comedy three times, before finally taking his first leading role in it, following advice from Spielberg.</p>
|
|
30
30
|
<p use:noClass>In an interview with Epire & Magazine, Quan reveals he quested starring in Love Hurts, which sees him Love Hurts in the leading role of a former assassin turned successful realtor, whose past returns when his brother attempts to hunt him down. The movie is in a similar vein to successful films such as The Long Kiss Goodnight and Nobody, and Quan discussed how he was reluctant to take the part due to his conditioned beliefs about how an action hero should look. But he reveals that he changed his mind following a meeting with Spielberg, who convinced him to do it.</p>
|
|
31
|
+
|
|
32
|
+
<h2 use:noClass>A smaller heading with an injection in it</h2>
|
|
31
33
|
<p use:noClass><strong use:noClass>Jason Momoa</strong> (”Aquaman”), <strong use:noClass>Jack Black</strong> (”Nacho Libre”) och <strong use:noClass>Jennifer Coolidge</strong> (”The White Lotus”) medverkar i den <strong use:noClass>Jared Hess</strong>-regisserade (”Napolen Dynamite”) filmen. Filmen följer fyra utbölingar som via en magisk portal sugs in i en värld där allt är kubformat. För att komma hem igen måste de övervinna den färgstarka världen.</p>
|
|
32
34
|
|
|
33
35
|
<p use:noClass>
|
package/src/routes/+page.svelte
CHANGED
|
@@ -13,16 +13,19 @@
|
|
|
13
13
|
|
|
14
14
|
let parentElement: HTMLElement | null = $state(null)
|
|
15
15
|
let elements: HTMLElement[] = $state([])
|
|
16
|
+
|
|
16
17
|
let response: LinkInjectionResponse | null = $state(null)
|
|
17
18
|
let isEditorialMode = $state(isEditorialModeEnabled())
|
|
18
19
|
let hasAuthToken = $state(!!getAuthToken())
|
|
19
20
|
let authorized = $state(false)
|
|
20
21
|
let loading = $state(true)
|
|
22
|
+
let isUrlExcluded = $state(true)
|
|
21
23
|
let linkInjections: LinkInjection[] = $state([])
|
|
22
24
|
|
|
23
25
|
// @ts-ignore It's ok if the response is empty
|
|
24
26
|
const { ai_injections: aiInjections = [], link_injections: manualInjections = [] } = $derived(response || {})
|
|
25
|
-
|
|
27
|
+
|
|
28
|
+
const pageText = $derived(elements.map(element => element.innerText).join('\n'))
|
|
26
29
|
|
|
27
30
|
// Rerender link injections when linkInjections change. This is only relevant for editiorial mode.
|
|
28
31
|
$effect(() => {
|
|
@@ -49,8 +52,10 @@
|
|
|
49
52
|
const config = await fetchConfig()
|
|
50
53
|
const url = getFullUrlPath()
|
|
51
54
|
|
|
52
|
-
// URL was marked as being excluded, we stop injections here
|
|
53
|
-
|
|
55
|
+
// If the URL was marked as being excluded in the config, we stop injections here.
|
|
56
|
+
isUrlExcluded = !!(config?.exclude_urls_pattern && url.match(config.exclude_urls_pattern))
|
|
57
|
+
if (isUrlExcluded) return
|
|
58
|
+
|
|
54
59
|
if (config?.custom_style) insertCustomStyle(config.custom_style || '')
|
|
55
60
|
|
|
56
61
|
setElements(config?.html_selector || '')
|
|
@@ -64,7 +69,7 @@
|
|
|
64
69
|
|
|
65
70
|
// Only trying once when not in editorial mode to prevent late injections (as well as a ton of requests)
|
|
66
71
|
// by users who are not in the editorial view.
|
|
67
|
-
response = await pollLinkInjections(
|
|
72
|
+
response = await pollLinkInjections(pageText, { maxTries: 1 })
|
|
68
73
|
|
|
69
74
|
loading = false
|
|
70
75
|
|
|
@@ -84,7 +89,7 @@
|
|
|
84
89
|
// so as not to suddenly insert new links while a user is reading the article.
|
|
85
90
|
if (!isEditorialMode) return
|
|
86
91
|
|
|
87
|
-
response = await pollLinkInjections(
|
|
92
|
+
response = await pollLinkInjections(pageText, { requireCompletedResult: true, onpoll: (update) => response = update })
|
|
88
93
|
inject({ aiInjections, manualInjections })
|
|
89
94
|
}
|
|
90
95
|
|
|
@@ -147,16 +152,16 @@
|
|
|
147
152
|
</script>
|
|
148
153
|
|
|
149
154
|
<div class="playpilot-link-injections">
|
|
150
|
-
{#if hasAuthToken && !isEditorialMode}
|
|
155
|
+
{#if !isUrlExcluded && hasAuthToken && !isEditorialMode}
|
|
151
156
|
<EditorTrigger
|
|
152
157
|
onclick={openEditorialMode}
|
|
153
158
|
onclose={() => { hasAuthToken = false; removeAuthCookie() }} />
|
|
154
159
|
{/if}
|
|
155
160
|
|
|
156
|
-
{#if isEditorialMode && authorized}
|
|
161
|
+
{#if !isUrlExcluded && isEditorialMode && authorized}
|
|
157
162
|
<Editor
|
|
158
163
|
bind:linkInjections
|
|
159
|
-
{
|
|
164
|
+
{pageText}
|
|
160
165
|
{loading}
|
|
161
166
|
onreinitialize={reinitializeEditor}
|
|
162
167
|
injectionsEnabled={response?.injections_enabled}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
interface Props {
|
|
21
21
|
linkInjections: LinkInjection[],
|
|
22
|
-
|
|
22
|
+
pageText?: string,
|
|
23
23
|
loading?: boolean,
|
|
24
24
|
injectionsEnabled?: boolean,
|
|
25
25
|
aiStatus?: {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
let {
|
|
35
35
|
linkInjections = $bindable(),
|
|
36
|
-
|
|
36
|
+
pageText = '',
|
|
37
37
|
loading = false,
|
|
38
38
|
injectionsEnabled = false,
|
|
39
39
|
aiStatus = {},
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
saving = true
|
|
85
85
|
hasError = false
|
|
86
86
|
|
|
87
|
-
linkInjections = await saveLinkInjections(linkInjections,
|
|
87
|
+
linkInjections = await saveLinkInjections(linkInjections, pageText)
|
|
88
88
|
initialStateString = linkInjectionsString
|
|
89
89
|
} catch {
|
|
90
90
|
hasError = true
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
|
|
177
177
|
{#if !loading}
|
|
178
178
|
<Session
|
|
179
|
-
{
|
|
179
|
+
{pageText}
|
|
180
180
|
onallow={() => allowEditing = true}
|
|
181
181
|
ondisallow={() => allowEditing = false}
|
|
182
182
|
ontakeover={onreinitialize}
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
style:top="{scrollDistance}px"
|
|
231
231
|
transition:fly={{ x: Math.min(window.innerWidth, 320), duration: 200, opacity: 1 }}>
|
|
232
232
|
<ManualInjection
|
|
233
|
-
{
|
|
233
|
+
{pageText}
|
|
234
234
|
onclose={() => manualInjectionActive = false}
|
|
235
235
|
onsave={(linkInjection) => linkInjections.push(linkInjection)} />
|
|
236
236
|
</div>
|
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
import { heading } from '$lib/actions/heading'
|
|
15
15
|
|
|
16
16
|
interface Props {
|
|
17
|
-
|
|
17
|
+
pageText: string
|
|
18
18
|
// eslint-disable-next-line no-unused-vars
|
|
19
19
|
onsave: (linkInjection: LinkInjection) => void
|
|
20
20
|
onclose?: () => void
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
let {
|
|
23
|
+
let { pageText = '', onsave, onclose = () => null }: Props = $props()
|
|
24
24
|
|
|
25
25
|
let currentSelection = $state('')
|
|
26
26
|
let selectionSentence = $state('')
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
selectionSentence = findSentenceForSelection(selection, selectionText)
|
|
59
59
|
|
|
60
60
|
const nodeContent = selection.getRangeAt(0).commonAncestorContainer.textContent
|
|
61
|
-
const documentTextContent = decodeHtmlEntities(
|
|
61
|
+
const documentTextContent = decodeHtmlEntities(pageText)
|
|
62
62
|
if (!nodeContent || !documentTextContent.includes(nodeContent)) { // Selected content is not within the ALI selector
|
|
63
63
|
error = 'Selection was not inside of given content'
|
|
64
64
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import Alert from './Alert.svelte'
|
|
5
5
|
|
|
6
6
|
interface Props {
|
|
7
|
-
|
|
7
|
+
pageText?: string,
|
|
8
8
|
// eslint-disable-next-line no-unused-vars
|
|
9
9
|
onpoll?: ({ injectionsEnabled, automationEnabled }: { injectionsEnabled: boolean, automationEnabled: boolean }) => void,
|
|
10
10
|
onallow?: () => void,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const {
|
|
16
|
-
|
|
16
|
+
pageText = '',
|
|
17
17
|
onpoll = () => null,
|
|
18
18
|
onallow = () => null,
|
|
19
19
|
ondisallow = () => null,
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async function setSession(): Promise<void> {
|
|
40
|
-
const result = await fetchAsSession(
|
|
40
|
+
const result = await fetchAsSession(pageText)
|
|
41
41
|
|
|
42
42
|
if (!result) return
|
|
43
43
|
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function takeOverEditing(): void {
|
|
53
|
-
saveCurrentSession(
|
|
53
|
+
saveCurrentSession(pageText)
|
|
54
54
|
ontakeover()
|
|
55
55
|
}
|
|
56
56
|
</script>
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
border-radius: var(--playpilot-genre-border-radius, margin(1));
|
|
36
36
|
padding: margin(0.25) margin(0.5);
|
|
37
37
|
font-family: var(--playpilot-genre-font-family, inherit);
|
|
38
|
-
font-weight: var(--playpilot-
|
|
38
|
+
font-weight: var(--playpilot-genre-font-weight, inherit);
|
|
39
39
|
text-transform: var(--playpilot-genre-text-transform, none);
|
|
40
40
|
color: var(--playpilot-genre-text-color, var(--playpilot-text-color-alt));
|
|
41
41
|
line-height: 1;
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
font-style: var(--playpilot-playlink-font-style, normal);
|
|
125
125
|
text-decoration: none !important;
|
|
126
126
|
white-space: nowrap;
|
|
127
|
-
font-size: margin(0.75);
|
|
127
|
+
font-size: var(--playpilot-playlinks-font-size, margin(0.75));
|
|
128
128
|
line-height: 1;
|
|
129
129
|
|
|
130
130
|
&:hover,
|
|
@@ -150,8 +150,8 @@
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
.category {
|
|
153
|
-
margin-top: margin(0.5);
|
|
154
|
-
font-size: margin(0.625);
|
|
153
|
+
margin-top: var(--playpilot-playlinks-category-margin, margin(0.5));
|
|
154
|
+
font-size: var(--playpilot-playlinks-category-font-size, margin(0.625));
|
|
155
155
|
color: var(--playpilot-playlink-category-text-color, var(--playpilot-text-color));
|
|
156
156
|
font-weight: var(--playpilot-playlink-category-font-weight, inherit);
|
|
157
157
|
font-family: var(--playpilot-playlink-category-font-family, inherit);
|
|
@@ -31,7 +31,7 @@ describe('$lib/api', () => {
|
|
|
31
31
|
it('Should call fetch with given url and body', async () => {
|
|
32
32
|
fakeFetch({ response: 'Some response' })
|
|
33
33
|
|
|
34
|
-
const response = await fetchLinkInjections('
|
|
34
|
+
const response = await fetchLinkInjections('Some text', { url: 'https://some-url', hash: 'some-hash' })
|
|
35
35
|
|
|
36
36
|
expect(response).toBe('Some response')
|
|
37
37
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
@@ -45,7 +45,7 @@ describe('$lib/api', () => {
|
|
|
45
45
|
modified_time: null,
|
|
46
46
|
published_time: null,
|
|
47
47
|
},
|
|
48
|
-
page_text: '
|
|
48
|
+
page_text: 'Some text',
|
|
49
49
|
}),
|
|
50
50
|
method: 'POST',
|
|
51
51
|
headers: expect.any(Object),
|
|
@@ -57,13 +57,13 @@ describe('$lib/api', () => {
|
|
|
57
57
|
// @ts-ignore
|
|
58
58
|
window.PlayPilotLinkInjections = null
|
|
59
59
|
|
|
60
|
-
await expect(async () => await fetchLinkInjections('
|
|
60
|
+
await expect(async () => await fetchLinkInjections('Some text', { hash: 'some-hash' })).rejects.toThrowError('No token was provided')
|
|
61
61
|
})
|
|
62
62
|
|
|
63
63
|
it('Should throw when response was incorrect', async () => {
|
|
64
64
|
fakeFetch({ ok: false })
|
|
65
65
|
|
|
66
|
-
await expect(async () => await fetchLinkInjections('
|
|
66
|
+
await expect(async () => await fetchLinkInjections('Some text', { hash: 'some-hash' })).rejects.toThrowError()
|
|
67
67
|
})
|
|
68
68
|
|
|
69
69
|
it('Should use given api token', async () => {
|
|
@@ -72,7 +72,7 @@ describe('$lib/api', () => {
|
|
|
72
72
|
// @ts-ignore
|
|
73
73
|
window.PlayPilotLinkInjections = { token: 'token' }
|
|
74
74
|
|
|
75
|
-
await fetchLinkInjections('
|
|
75
|
+
await fetchLinkInjections('Some text', { hash: 'some-hash' })
|
|
76
76
|
|
|
77
77
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
78
78
|
expect.stringContaining('api-token=token'),
|
|
@@ -86,7 +86,7 @@ describe('$lib/api', () => {
|
|
|
86
86
|
// @ts-ignore
|
|
87
87
|
window.PlayPilotLinkInjections = null
|
|
88
88
|
|
|
89
|
-
await expect(async () => await fetchLinkInjections('
|
|
89
|
+
await expect(async () => await fetchLinkInjections('Some text', { hash: 'some-hash' })).rejects.toThrowError()
|
|
90
90
|
})
|
|
91
91
|
|
|
92
92
|
it('Should include editorial_mode_enabled=true in url if editorial mode is true', async () => {
|
|
@@ -95,7 +95,7 @@ describe('$lib/api', () => {
|
|
|
95
95
|
vi.mocked(authorize).mockResolvedValueOnce(true)
|
|
96
96
|
vi.mocked(isEditorialModeEnabled).mockResolvedValueOnce(true)
|
|
97
97
|
|
|
98
|
-
const response = await fetchLinkInjections('
|
|
98
|
+
const response = await fetchLinkInjections('Some text', { hash: 'some-hash' })
|
|
99
99
|
|
|
100
100
|
expect(response).toBe('Some response')
|
|
101
101
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
@@ -107,7 +107,7 @@ describe('$lib/api', () => {
|
|
|
107
107
|
it('Should call fetch with given language', async () => {
|
|
108
108
|
fakeFetch({ response: 'Some response' })
|
|
109
109
|
|
|
110
|
-
await fetchLinkInjections('
|
|
110
|
+
await fetchLinkInjections('Some text', { hash: 'some-hash' })
|
|
111
111
|
|
|
112
112
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
113
113
|
expect.stringContaining(`&language=${Language.English}`),
|
|
@@ -117,7 +117,7 @@ describe('$lib/api', () => {
|
|
|
117
117
|
// @ts-ignore
|
|
118
118
|
window.PlayPilotLinkInjections = { token: 'token', language: Language.Swedish }
|
|
119
119
|
|
|
120
|
-
await fetchLinkInjections('
|
|
120
|
+
await fetchLinkInjections('Some text', { hash: 'some-hash' })
|
|
121
121
|
|
|
122
122
|
expect(global.fetch).toHaveBeenCalledWith(
|
|
123
123
|
expect.stringContaining(`&language=${Language.Swedish}`),
|
|
@@ -130,7 +130,7 @@ describe('$lib/api', () => {
|
|
|
130
130
|
it('Should poll endpoint while results are not yet ready when requireCompletedResults is true', async () => {
|
|
131
131
|
fakeFetch({ response: { automation_enabled: true, ai_running: true } })
|
|
132
132
|
|
|
133
|
-
pollLinkInjections('
|
|
133
|
+
pollLinkInjections('Some text', { requireCompletedResult: true, pollInterval: 500 })
|
|
134
134
|
|
|
135
135
|
expect(global.fetch).toHaveBeenCalled()
|
|
136
136
|
|
|
@@ -145,7 +145,7 @@ describe('$lib/api', () => {
|
|
|
145
145
|
fakeFetch({ response: { automation_enabled: true, ai_running: true } })
|
|
146
146
|
|
|
147
147
|
const onpoll = vi.fn()
|
|
148
|
-
pollLinkInjections('
|
|
148
|
+
pollLinkInjections('Some text', { requireCompletedResult: true, pollInterval: 500, onpoll })
|
|
149
149
|
|
|
150
150
|
await waitFor(() => {
|
|
151
151
|
expect(onpoll).toHaveBeenCalledTimes(1)
|
|
@@ -162,7 +162,7 @@ describe('$lib/api', () => {
|
|
|
162
162
|
const response = { automation_enabled: true, ai_running: false, link_injections: [{ title: 'value' }] }
|
|
163
163
|
fakeFetch({ response })
|
|
164
164
|
|
|
165
|
-
const result = await pollLinkInjections('
|
|
165
|
+
const result = await pollLinkInjections('Some text', { requireCompletedResult: true, pollInterval: 500 })
|
|
166
166
|
|
|
167
167
|
expect(global.fetch).toHaveBeenCalled()
|
|
168
168
|
expect(result).toEqual(response)
|
|
@@ -174,7 +174,7 @@ describe('$lib/api', () => {
|
|
|
174
174
|
it('Should stop polling if max limit was exceeded', async () => {
|
|
175
175
|
fakeFetch({ response: { automation_enabled: true, ai_running: true } })
|
|
176
176
|
|
|
177
|
-
await pollLinkInjections('
|
|
177
|
+
await pollLinkInjections('Some text', { requireCompletedResult: true, pollInterval: 200, maxTries: 2 })
|
|
178
178
|
|
|
179
179
|
await new Promise(res => setTimeout(res, 1000)) // Wait for a potential poll
|
|
180
180
|
expect(global.fetch).toHaveBeenCalledTimes(2)
|
|
@@ -183,7 +183,7 @@ describe('$lib/api', () => {
|
|
|
183
183
|
it('Should continue polling if endpoint throws error', async () => {
|
|
184
184
|
fakeFetch({ ok: false })
|
|
185
185
|
|
|
186
|
-
pollLinkInjections('
|
|
186
|
+
pollLinkInjections('Some text', { pollInterval: 500 })
|
|
187
187
|
|
|
188
188
|
expect(global.fetch).toHaveBeenCalled()
|
|
189
189
|
|
|
@@ -194,7 +194,7 @@ describe('$lib/api', () => {
|
|
|
194
194
|
it('Should return empty array if link_injections are null', async () => {
|
|
195
195
|
fakeFetch({ response: { link_injections: null, ai_injections: null } })
|
|
196
196
|
|
|
197
|
-
const result = await pollLinkInjections('
|
|
197
|
+
const result = await pollLinkInjections('Some text')
|
|
198
198
|
expect(result).toEqual(expect.objectContaining({ link_injections: [], ai_injections: [] }))
|
|
199
199
|
})
|
|
200
200
|
})
|
|
@@ -677,10 +677,9 @@ describe('linkInjection.js', () => {
|
|
|
677
677
|
expect(getLinkInjectionElements(parent)[1].tagName).toBe('OL')
|
|
678
678
|
})
|
|
679
679
|
|
|
680
|
-
it('Should ignore links, buttons, script tags, style tags, iframes,
|
|
680
|
+
it('Should ignore links, buttons, script tags, style tags, iframes, h1, and more', () => {
|
|
681
681
|
document.body.innerHTML = `<section>
|
|
682
682
|
<h1>Some header</h1>
|
|
683
|
-
<h4>Some smaller header</h4>
|
|
684
683
|
|
|
685
684
|
<a>I am a link</a>
|
|
686
685
|
<button>And I am a button</button>
|
|
@@ -694,12 +693,13 @@ describe('linkInjection.js', () => {
|
|
|
694
693
|
|
|
695
694
|
<div>
|
|
696
695
|
<a>I am another link</a>
|
|
696
|
+
<h4>Some smaller header</h4>
|
|
697
697
|
<p>And finally, some text</p>
|
|
698
698
|
</div>
|
|
699
699
|
</section>`
|
|
700
700
|
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
701
701
|
|
|
702
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(
|
|
702
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(2) // The h4 tag and the paragraph tag
|
|
703
703
|
})
|
|
704
704
|
|
|
705
705
|
it('Should ignore links, buttons, and headers even when deeply nested', () => {
|
|
@@ -178,7 +178,7 @@ describe('$routes/+page.svelte', () => {
|
|
|
178
178
|
render(page)
|
|
179
179
|
|
|
180
180
|
await waitFor(() => {
|
|
181
|
-
expect(pollLinkInjections).toHaveBeenCalledWith('
|
|
181
|
+
expect(pollLinkInjections).toHaveBeenCalledWith('Here', { maxTries: 1 })
|
|
182
182
|
})
|
|
183
183
|
})
|
|
184
184
|
|
|
@@ -230,19 +230,25 @@ describe('$routes/+page.svelte', () => {
|
|
|
230
230
|
expect(container.querySelector('.editor')).not.toBeTruthy()
|
|
231
231
|
})
|
|
232
232
|
|
|
233
|
-
it('Should not render editor trigger if getAuthToken does not return token', () => {
|
|
233
|
+
it('Should not render editor trigger if getAuthToken does not return token', async () => {
|
|
234
234
|
vi.mocked(getAuthToken).mockReturnValueOnce('')
|
|
235
235
|
|
|
236
236
|
const { queryByTitle } = render(page)
|
|
237
237
|
|
|
238
|
+
await waitFor(() => expect(fetchConfig).toHaveBeenCalled())
|
|
239
|
+
await new Promise((res) => setTimeout(res))
|
|
240
|
+
|
|
238
241
|
expect(queryByTitle('Show PlayPilot TPI editor')).not.toBeTruthy()
|
|
239
242
|
})
|
|
240
243
|
|
|
241
|
-
it('Should render editor trigger if getAuthToken returns token', () => {
|
|
244
|
+
it('Should render editor trigger if getAuthToken returns token', async () => {
|
|
242
245
|
vi.mocked(getAuthToken).mockReturnValueOnce('token')
|
|
243
246
|
|
|
244
247
|
const { getByTitle } = render(page)
|
|
245
248
|
|
|
249
|
+
await waitFor(() => expect(fetchConfig).toHaveBeenCalled())
|
|
250
|
+
await new Promise((res) => setTimeout(res))
|
|
251
|
+
|
|
246
252
|
expect(getByTitle('Show PlayPilot TPI editor')).toBeTruthy()
|
|
247
253
|
})
|
|
248
254
|
|
|
@@ -251,6 +257,9 @@ describe('$routes/+page.svelte', () => {
|
|
|
251
257
|
|
|
252
258
|
const { getByTitle } = render(page)
|
|
253
259
|
|
|
260
|
+
await waitFor(() => expect(fetchConfig).toHaveBeenCalled())
|
|
261
|
+
await new Promise((res) => setTimeout(res))
|
|
262
|
+
|
|
254
263
|
await fireEvent.click(getByTitle('Close PlayPilot TPI'))
|
|
255
264
|
|
|
256
265
|
expect(removeAuthCookie).toHaveBeenCalled()
|
|
@@ -262,6 +271,9 @@ describe('$routes/+page.svelte', () => {
|
|
|
262
271
|
|
|
263
272
|
const { getByTitle, container } = render(page)
|
|
264
273
|
|
|
274
|
+
await waitFor(() => expect(fetchConfig).toHaveBeenCalled())
|
|
275
|
+
await new Promise((res) => setTimeout(res))
|
|
276
|
+
|
|
265
277
|
await fireEvent.click(getByTitle('Show PlayPilot TPI editor'))
|
|
266
278
|
|
|
267
279
|
expect(container.querySelector('.editor')).toBeTruthy()
|
|
@@ -330,7 +342,6 @@ describe('$routes/+page.svelte', () => {
|
|
|
330
342
|
|
|
331
343
|
describe('Config', () => {
|
|
332
344
|
describe('exclude_urls_pattern', () => {
|
|
333
|
-
|
|
334
345
|
it('Should not inject if config exclude_urls_pattern matches current url', async () => {
|
|
335
346
|
vi.mocked(fetchConfig).mockResolvedValueOnce({ exclude_urls_pattern: '/t' })
|
|
336
347
|
|
|
@@ -367,6 +378,32 @@ describe('$routes/+page.svelte', () => {
|
|
|
367
378
|
await waitFor(() => expect(fetchConfig).toHaveBeenCalled())
|
|
368
379
|
expect(pollLinkInjections).toHaveBeenCalled()
|
|
369
380
|
})
|
|
381
|
+
|
|
382
|
+
it('Should show editor button if url is not excluded', async () => {
|
|
383
|
+
vi.mocked(getAuthToken).mockReturnValueOnce('token')
|
|
384
|
+
vi.mocked(authorize).mockResolvedValueOnce(true)
|
|
385
|
+
vi.mocked(fetchConfig).mockResolvedValueOnce({ })
|
|
386
|
+
|
|
387
|
+
const { getByTitle } = render(page)
|
|
388
|
+
|
|
389
|
+
await waitFor(() => expect(fetchConfig).toHaveBeenCalled())
|
|
390
|
+
await new Promise((res) => setTimeout(res))
|
|
391
|
+
|
|
392
|
+
expect(getByTitle('Show PlayPilot TPI editor')).toBeTruthy()
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('Should not show editor button if url is excluded', async () => {
|
|
396
|
+
vi.mocked(getAuthToken).mockReturnValueOnce('token')
|
|
397
|
+
vi.mocked(authorize).mockResolvedValueOnce(true)
|
|
398
|
+
vi.mocked(fetchConfig).mockResolvedValueOnce({ exclude_urls_pattern: '/t' })
|
|
399
|
+
|
|
400
|
+
const { queryByTitle } = render(page)
|
|
401
|
+
|
|
402
|
+
await waitFor(() => expect(fetchConfig).toHaveBeenCalled())
|
|
403
|
+
await new Promise((res) => setTimeout(res))
|
|
404
|
+
|
|
405
|
+
expect(queryByTitle('Show PlayPilot TPI editor')).not.toBeTruthy()
|
|
406
|
+
})
|
|
370
407
|
})
|
|
371
408
|
|
|
372
409
|
describe('html_selector', () => {
|
|
@@ -37,7 +37,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
37
37
|
it('Should input selected text in selected text input and search input', async () => {
|
|
38
38
|
createSelectionMock()
|
|
39
39
|
|
|
40
|
-
const { getByLabelText } = render(ManualInjection, {
|
|
40
|
+
const { getByLabelText } = render(ManualInjection, { pageText: document.body.innerText, onsave: () => null })
|
|
41
41
|
|
|
42
42
|
await fireEvent.mouseUp(window)
|
|
43
43
|
|
|
@@ -49,7 +49,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
49
49
|
it('Should disable save button by default', async () => {
|
|
50
50
|
createSelectionMock()
|
|
51
51
|
|
|
52
|
-
const { getByText } = render(ManualInjection, {
|
|
52
|
+
const { getByText } = render(ManualInjection, { pageText: '', onsave: () => null })
|
|
53
53
|
|
|
54
54
|
expect(getByText('Add playlink').hasAttribute('disabled')).toBeTruthy()
|
|
55
55
|
})
|
|
@@ -59,7 +59,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
59
59
|
|
|
60
60
|
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
61
61
|
|
|
62
|
-
const { getByText } = render(ManualInjection, {
|
|
62
|
+
const { getByText } = render(ManualInjection, { pageText: document.body.innerText, onsave: () => null })
|
|
63
63
|
|
|
64
64
|
await fireEvent.mouseUp(window)
|
|
65
65
|
await waitFor(() => getByText(title.title))
|
|
@@ -74,7 +74,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
74
74
|
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
75
75
|
|
|
76
76
|
const onsave = vi.fn()
|
|
77
|
-
const { getByText } = render(ManualInjection, {
|
|
77
|
+
const { getByText } = render(ManualInjection, { pageText: document.body.innerText, onsave })
|
|
78
78
|
|
|
79
79
|
await fireEvent.mouseUp(window)
|
|
80
80
|
await fireEvent.click(getByText(title.title))
|
|
@@ -113,7 +113,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
113
113
|
}))
|
|
114
114
|
|
|
115
115
|
const onsave = vi.fn()
|
|
116
|
-
const { getByText } = render(ManualInjection, {
|
|
116
|
+
const { getByText } = render(ManualInjection, { pageText: document.body.innerText, onsave })
|
|
117
117
|
|
|
118
118
|
await fireEvent.mouseUp(window)
|
|
119
119
|
await fireEvent.click(getByText(title.title))
|
|
@@ -138,7 +138,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
138
138
|
let container = document.querySelector('div')
|
|
139
139
|
|
|
140
140
|
const onsave = vi.fn()
|
|
141
|
-
const { unmount } = render(ManualInjection, {
|
|
141
|
+
const { unmount } = render(ManualInjection, { pageText: document.body.innerText, onsave })
|
|
142
142
|
|
|
143
143
|
// @ts-ignore
|
|
144
144
|
window.getSelection = vi.fn(() => ({
|
|
@@ -176,7 +176,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
176
176
|
focusNode: container,
|
|
177
177
|
}))
|
|
178
178
|
|
|
179
|
-
;(render(ManualInjection, {
|
|
179
|
+
;(render(ManualInjection, { pageText: document.body.innerText, onsave }))
|
|
180
180
|
|
|
181
181
|
await fireEvent.mouseUp(window)
|
|
182
182
|
|
|
@@ -188,7 +188,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
188
188
|
|
|
189
189
|
const container = document.querySelector('div')
|
|
190
190
|
const onsave = vi.fn()
|
|
191
|
-
const { getByText } = render(ManualInjection, {
|
|
191
|
+
const { getByText } = render(ManualInjection, { pageText: document.body.innerText, onsave })
|
|
192
192
|
|
|
193
193
|
// @ts-ignore
|
|
194
194
|
window.getSelection = vi.fn(() => ({
|
|
@@ -5,7 +5,7 @@ import Popover from '../../../routes/components/Popover.svelte'
|
|
|
5
5
|
import { createRawSnippet } from 'svelte'
|
|
6
6
|
|
|
7
7
|
describe('Popover.svelte', () => {
|
|
8
|
-
const children = createRawSnippet(() => ({ render: () => 'Some snippet' }))
|
|
8
|
+
const children = createRawSnippet(() => ({ render: () => '<div>Some snippet</div>' }))
|
|
9
9
|
|
|
10
10
|
it('Should render given snippet', () => {
|
|
11
11
|
const { getByText } = render(Popover, { children })
|