@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playpilot/tpi",
3
- "version": "3.6.1",
3
+ "version": "3.7.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
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 html HTML to be crawled
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(html: string, { url = getFullUrlPath(), hash = stringToHash(html), params = {} }: { url?: string, hash?: string; params?: object } = {}): Promise<LinkInjectionResponse> {
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: html,
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
- html: string,
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(html)
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[], html: string): Promise<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(html, {
116
+ const response = await fetchLinkInjections(pageText, {
118
117
  params: {
119
118
  private_token: getAuthToken(),
120
119
  link_injections: newLinkInjections,
@@ -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|H[1-6])$/.test(element.tagName)) continue
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 &amp; 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>
@@ -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
- const htmlString = $derived(elements.map(p => p.outerHTML).join(''))
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 unless we're in editorial mode.
53
- if (!isEditorialMode && config?.exclude_urls_pattern && url.match(config.exclude_urls_pattern)) return
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(htmlString, { maxTries: 1 })
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(htmlString, { requireCompletedResult: true, onpoll: (update) => response = update })
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
- {htmlString}
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
- htmlString?: string,
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
- htmlString = '',
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, htmlString)
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
- {htmlString}
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
- {htmlString}
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
- htmlString: string
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 { htmlString = '', onsave, onclose = () => null }: Props = $props()
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(htmlString)
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
- htmlString?: string,
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
- htmlString = '',
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(htmlString)
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(htmlString)
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-playlink-font-weight, inherit);
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('some-html', { url: 'https://some-url', hash: 'some-hash' })
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: 'some-html',
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('some-html', { hash: 'some-hash' })).rejects.toThrowError('No token was provided')
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('some-html', { hash: 'some-hash' })).rejects.toThrowError()
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('some-html', { hash: 'some-hash' })
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('some-html', { hash: 'some-hash' })).rejects.toThrowError()
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('some-html', { hash: 'some-hash' })
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('some-html', { hash: 'some-hash' })
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('some-html', { hash: 'some-hash' })
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('some-html', { requireCompletedResult: true, pollInterval: 500 })
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('some-html', { requireCompletedResult: true, pollInterval: 500, onpoll })
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('some-html', { requireCompletedResult: true, pollInterval: 500 })
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('some-html', { requireCompletedResult: true, pollInterval: 200, maxTries: 2 })
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('some-html', { pollInterval: 500 })
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('some-html')
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, headers, and more', () => {
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(1)
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('<p>Here</p>', { maxTries: 1 })
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, { htmlString: document.body.innerHTML, onsave: () => null })
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, { htmlString: '', onsave: () => null })
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, { htmlString: document.body.innerHTML, onsave: () => null })
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, { htmlString: document.body.innerHTML, onsave })
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, { htmlString: document.body.innerHTML, onsave })
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, { htmlString: document.body.innerHTML, onsave })
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, { htmlString: document.body.innerHTML, onsave }))
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, { htmlString: document.body.innerHTML, onsave })
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 })