@playpilot/tpi 3.1.0-beta.4 → 3.1.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 +7 -7
- package/package.json +1 -1
- package/src/lib/auth.ts +6 -2
- package/src/lib/enums/TrackingEvent.ts +1 -0
- package/src/lib/linkInjection.ts +43 -11
- package/src/lib/tracking.ts +1 -1
- package/src/lib/types/global.d.ts +2 -0
- package/src/main.js +5 -1
- package/src/routes/+layout.svelte +8 -0
- package/src/routes/+page.svelte +22 -4
- package/src/routes/components/AfterArticlePlaylinks.svelte +4 -0
- package/src/routes/components/Editorial/Editor.svelte +5 -4
- package/src/routes/components/Editorial/EditorTrigger.svelte +72 -0
- package/src/routes/components/Editorial/ManualInjection.svelte +13 -10
- package/src/routes/components/Editorial/ResizeHandle.svelte +4 -4
- package/src/routes/components/Editorial/Search/TitleSearch.svelte +0 -2
- package/src/routes/components/Icons/IconPlayPilot.svelte +8 -0
- package/src/routes/components/Title.svelte +5 -5
- package/src/tests/lib/auth.test.js +11 -1
- package/src/tests/lib/linkInjection.test.js +134 -7
- package/src/tests/routes/+page.test.js +79 -2
- package/src/tests/routes/components/Editorial/Editor.test.js +4 -4
- package/src/tests/routes/components/Editorial/EditorTrigger.test.js +24 -0
- package/src/tests/routes/components/Editorial/ManualInjection.test.js +65 -21
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fireEvent
|
|
2
|
-
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
|
1
|
+
import { fireEvent } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
|
3
3
|
|
|
4
4
|
import { injectLinksInDocument, clearLinkInjections, clearLinkInjection, getLinkInjectionElements, insertAfterArticlePlaylinks, getLinkInjectionsParentElement, sortLinkInjectionsByRange, isAvailableAsManualInjection, filterRemovedAndInactiveInjections, isEquivalentInjection, filterInvalidInTextInjections, filterInvalidAfterArticleInjections, isValidInjection } from '$lib/linkInjection'
|
|
5
5
|
import { mount, unmount } from 'svelte'
|
|
@@ -29,6 +29,10 @@ describe('linkInjection.js', () => {
|
|
|
29
29
|
window.PlayPilotLinkInjections = {}
|
|
30
30
|
})
|
|
31
31
|
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
vi.useRealTimers()
|
|
34
|
+
})
|
|
35
|
+
|
|
32
36
|
describe('injectLinksInDocument', () => {
|
|
33
37
|
it('Should replace given words with injections', () => {
|
|
34
38
|
const injection = generateInjection('This is a sentence with an injection.', 'an injection')
|
|
@@ -335,7 +339,10 @@ describe('linkInjection.js', () => {
|
|
|
335
339
|
|
|
336
340
|
const link = /** @type {HTMLAnchorElement} */ (document.querySelector('[data-playpilot-injection-key]'))
|
|
337
341
|
|
|
342
|
+
vi.useFakeTimers()
|
|
343
|
+
|
|
338
344
|
await fireEvent.mouseEnter(link)
|
|
345
|
+
vi.advanceTimersByTime(200)
|
|
339
346
|
expect(mount).toHaveBeenCalled()
|
|
340
347
|
|
|
341
348
|
await fireEvent.mouseLeave(link)
|
|
@@ -353,13 +360,38 @@ describe('linkInjection.js', () => {
|
|
|
353
360
|
|
|
354
361
|
const link = /** @type {HTMLAnchorElement} */ (document.querySelector('[data-playpilot-injection-key]'))
|
|
355
362
|
|
|
363
|
+
vi.useFakeTimers()
|
|
364
|
+
|
|
356
365
|
await fireEvent.mouseEnter(link)
|
|
366
|
+
vi.advanceTimersByTime(200)
|
|
357
367
|
await fireEvent.mouseLeave(link)
|
|
358
368
|
await fireEvent.mouseEnter(link)
|
|
369
|
+
vi.advanceTimersByTime(200)
|
|
359
370
|
|
|
360
371
|
expect(mount).toHaveBeenCalledTimes(2)
|
|
361
372
|
})
|
|
362
373
|
|
|
374
|
+
it('Should not mount popover on mouseover if not hovered for long enough duration', async () => {
|
|
375
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
376
|
+
|
|
377
|
+
const elements = Array.from(document.body.querySelectorAll('p'))
|
|
378
|
+
const injection = generateInjection('This is a sentence with an injection.', 'an injection')
|
|
379
|
+
|
|
380
|
+
const mock = vi.fn()
|
|
381
|
+
injectLinksInDocument(elements, mock, { aiInjections: [injection], manualInjections: [] })
|
|
382
|
+
|
|
383
|
+
const link = /** @type {HTMLAnchorElement} */ (document.querySelector('[data-playpilot-injection-key]'))
|
|
384
|
+
|
|
385
|
+
vi.useFakeTimers()
|
|
386
|
+
|
|
387
|
+
await fireEvent.mouseEnter(link)
|
|
388
|
+
vi.advanceTimersByTime(50)
|
|
389
|
+
await fireEvent.mouseLeave(link)
|
|
390
|
+
vi.advanceTimersByTime(500)
|
|
391
|
+
|
|
392
|
+
expect(mount).not.toHaveBeenCalled()
|
|
393
|
+
})
|
|
394
|
+
|
|
363
395
|
it('Should mount popover component only once when the same popover is already open', async () => {
|
|
364
396
|
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
365
397
|
|
|
@@ -371,10 +403,15 @@ describe('linkInjection.js', () => {
|
|
|
371
403
|
|
|
372
404
|
const link = /** @type {HTMLAnchorElement} */ (document.querySelector('[data-playpilot-injection-key]'))
|
|
373
405
|
|
|
406
|
+
vi.useFakeTimers()
|
|
407
|
+
|
|
374
408
|
await fireEvent.mouseEnter(link)
|
|
409
|
+
vi.advanceTimersByTime(200)
|
|
375
410
|
expect(mount).toHaveBeenCalled()
|
|
376
411
|
|
|
377
412
|
await fireEvent.mouseEnter(link)
|
|
413
|
+
vi.advanceTimersByTime(200)
|
|
414
|
+
|
|
378
415
|
expect(mount).toHaveBeenCalledTimes(1)
|
|
379
416
|
})
|
|
380
417
|
|
|
@@ -487,6 +524,30 @@ describe('linkInjection.js', () => {
|
|
|
487
524
|
|
|
488
525
|
expect(document.querySelectorAll('a')).toHaveLength(3)
|
|
489
526
|
})
|
|
527
|
+
|
|
528
|
+
it('Should properly inject into lists', () => {
|
|
529
|
+
document.body.innerHTML = `<section>
|
|
530
|
+
<ul>
|
|
531
|
+
<li>First</li>
|
|
532
|
+
<li>Title</li>
|
|
533
|
+
<li>Some other title</li>
|
|
534
|
+
<li>Fourth</li>
|
|
535
|
+
</ul>
|
|
536
|
+
</section>
|
|
537
|
+
`
|
|
538
|
+
|
|
539
|
+
const elements = getLinkInjectionElements(/** @type {HTMLElement} */ (document.querySelector('section')))
|
|
540
|
+
const injections = [
|
|
541
|
+
generateInjection('First Title Some other title', 'First'),
|
|
542
|
+
generateInjection('First Title Some other title', 'Title'),
|
|
543
|
+
generateInjection('First Title Some other title', 'Some other title'),
|
|
544
|
+
generateInjection('First Title Some other title', 'Fourth'),
|
|
545
|
+
]
|
|
546
|
+
|
|
547
|
+
injectLinksInDocument(elements, () => null, { aiInjections: [], manualInjections: injections })
|
|
548
|
+
|
|
549
|
+
expect(document.querySelectorAll('a')).toHaveLength(4)
|
|
550
|
+
})
|
|
490
551
|
})
|
|
491
552
|
|
|
492
553
|
describe('clearLinkInjections', () => {
|
|
@@ -557,7 +618,7 @@ describe('linkInjection.js', () => {
|
|
|
557
618
|
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
558
619
|
})
|
|
559
620
|
|
|
560
|
-
it('Should return
|
|
621
|
+
it('Should return a list as a single element', () => {
|
|
561
622
|
document.body.innerHTML = `<section>
|
|
562
623
|
<ul>
|
|
563
624
|
<li>Hey!</li>
|
|
@@ -568,10 +629,22 @@ describe('linkInjection.js', () => {
|
|
|
568
629
|
<li>a</li>
|
|
569
630
|
<li>list.</li>
|
|
570
631
|
</ul>
|
|
632
|
+
|
|
633
|
+
<ol>
|
|
634
|
+
<li>Hey!</li>
|
|
635
|
+
<li>I</li>
|
|
636
|
+
<li>am</li>
|
|
637
|
+
<li>part</li>
|
|
638
|
+
<li>of</li>
|
|
639
|
+
<li>a</li>
|
|
640
|
+
<li>list.</li>
|
|
641
|
+
</ol>
|
|
571
642
|
</section>`
|
|
572
643
|
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
573
644
|
|
|
574
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(
|
|
645
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
646
|
+
expect(getLinkInjectionElements(parent)[0].tagName).toBe('UL')
|
|
647
|
+
expect(getLinkInjectionElements(parent)[1].tagName).toBe('OL')
|
|
575
648
|
})
|
|
576
649
|
|
|
577
650
|
it('Should ignore links, buttons, script tags, style tags, iframes, and headers', () => {
|
|
@@ -686,8 +759,8 @@ describe('linkInjection.js', () => {
|
|
|
686
759
|
describe('insertAfterArticlePlaylinks', () => {
|
|
687
760
|
it('Should insert component after given elements', () => {
|
|
688
761
|
document.body.innerHTML = `<section>
|
|
689
|
-
<
|
|
690
|
-
<
|
|
762
|
+
<p>Some text</p>
|
|
763
|
+
<p>Some other text</p>
|
|
691
764
|
</section>
|
|
692
765
|
`
|
|
693
766
|
|
|
@@ -696,8 +769,10 @@ describe('linkInjection.js', () => {
|
|
|
696
769
|
const elements = getLinkInjectionElements(document.body)
|
|
697
770
|
insertAfterArticlePlaylinks(elements, [injection], () => null)
|
|
698
771
|
|
|
772
|
+
console.log(document.body.innerHTML)
|
|
773
|
+
|
|
699
774
|
expect(mount).toHaveBeenCalled()
|
|
700
|
-
expect(document.querySelector('[data-playpilot-after-article-playlinks]')).toBeTruthy()
|
|
775
|
+
expect(document.querySelector('p:last-of-type + [data-playpilot-after-article-playlinks]')).toBeTruthy()
|
|
701
776
|
})
|
|
702
777
|
|
|
703
778
|
it('Should not insert component if no linkInjections were given', () => {
|
|
@@ -713,6 +788,58 @@ describe('linkInjection.js', () => {
|
|
|
713
788
|
expect(mount).not.toHaveBeenCalled()
|
|
714
789
|
expect(document.querySelector('[data-playpilot-after-article-playlinks]')).not.toBeTruthy()
|
|
715
790
|
})
|
|
791
|
+
|
|
792
|
+
it('Should insert after the given element if selector is given in config object', () => {
|
|
793
|
+
window.PlayPilotLinkInjections.after_article_selector = 'hr'
|
|
794
|
+
|
|
795
|
+
document.body.innerHTML = `<section>
|
|
796
|
+
<div>Some text</div>
|
|
797
|
+
<hr>
|
|
798
|
+
<div>Some other text</div>
|
|
799
|
+
</section>
|
|
800
|
+
`
|
|
801
|
+
|
|
802
|
+
const injection = generateInjection('This is a sentence with an injection.', 'an injection')
|
|
803
|
+
const elements = getLinkInjectionElements(document.body)
|
|
804
|
+
insertAfterArticlePlaylinks(elements, [injection], () => null)
|
|
805
|
+
|
|
806
|
+
expect(document.querySelector('hr + [data-playpilot-after-article-playlinks]')).toBeTruthy()
|
|
807
|
+
})
|
|
808
|
+
|
|
809
|
+
it('Should insert in given position if position is given in config object', () => {
|
|
810
|
+
window.PlayPilotLinkInjections.after_article_insert_position = 'beforebegin'
|
|
811
|
+
|
|
812
|
+
document.body.innerHTML = `<section>
|
|
813
|
+
<div>Some text</div>
|
|
814
|
+
<hr>
|
|
815
|
+
<div>Some other text</div>
|
|
816
|
+
</section>
|
|
817
|
+
`
|
|
818
|
+
|
|
819
|
+
const injection = generateInjection('This is a sentence with an injection.', 'an injection')
|
|
820
|
+
const elements = getLinkInjectionElements(document.body)
|
|
821
|
+
insertAfterArticlePlaylinks(elements, [injection], () => null)
|
|
822
|
+
|
|
823
|
+
expect(document.querySelector('hr + [data-playpilot-after-article-playlinks]')).toBeTruthy()
|
|
824
|
+
})
|
|
825
|
+
|
|
826
|
+
it('Should insert in given position for the given element if selector and position are given in config object', () => {
|
|
827
|
+
window.PlayPilotLinkInjections.after_article_selector = 'hr'
|
|
828
|
+
window.PlayPilotLinkInjections.after_article_insert_position = 'beforebegin'
|
|
829
|
+
|
|
830
|
+
document.body.innerHTML = `<section>
|
|
831
|
+
<div>Some text</div>
|
|
832
|
+
<hr>
|
|
833
|
+
<div>Some other text</div>
|
|
834
|
+
</section>
|
|
835
|
+
`
|
|
836
|
+
|
|
837
|
+
const injection = generateInjection('This is a sentence with an injection.', 'an injection')
|
|
838
|
+
const elements = getLinkInjectionElements(document.body)
|
|
839
|
+
insertAfterArticlePlaylinks(elements, [injection], () => null)
|
|
840
|
+
|
|
841
|
+
expect(document.querySelector('[data-playpilot-after-article-playlinks] + hr')).toBeTruthy()
|
|
842
|
+
})
|
|
716
843
|
})
|
|
717
844
|
|
|
718
845
|
describe('sortLinkInjectionsByRange', () => {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { render, waitFor } from '@testing-library/svelte'
|
|
1
|
+
import { fireEvent, render, waitFor } from '@testing-library/svelte'
|
|
2
2
|
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
|
3
3
|
|
|
4
4
|
import page from '../../routes/+page.svelte'
|
|
5
5
|
import { fetchConfig, pollLinkInjections } from '$lib/api'
|
|
6
6
|
import { track } from '$lib/tracking'
|
|
7
7
|
import { TrackingEvent } from '$lib/enums/TrackingEvent'
|
|
8
|
-
import { authorize } from '$lib/auth'
|
|
8
|
+
import { authorize, getAuthToken, removeAuthCookie } from '$lib/auth'
|
|
9
|
+
import { generateInjection } from '../helpers'
|
|
10
|
+
import { injectLinksInDocument } from '$lib/linkInjection'
|
|
9
11
|
import { isCrawler } from '$lib/crawler'
|
|
10
12
|
|
|
11
13
|
vi.mock('$lib/api', () => ({
|
|
@@ -13,6 +15,14 @@ vi.mock('$lib/api', () => ({
|
|
|
13
15
|
fetchConfig: vi.fn(),
|
|
14
16
|
}))
|
|
15
17
|
|
|
18
|
+
vi.mock(import('$lib/linkInjection'), async (importOriginal) => {
|
|
19
|
+
const actual = await importOriginal()
|
|
20
|
+
return {
|
|
21
|
+
...actual,
|
|
22
|
+
injectLinksInDocument: vi.fn(() => []),
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
16
26
|
vi.mock('$lib/tracking', () => ({
|
|
17
27
|
track: vi.fn(),
|
|
18
28
|
}))
|
|
@@ -22,6 +32,8 @@ vi.mock(import('$lib/auth'), async (importOriginal) => {
|
|
|
22
32
|
return {
|
|
23
33
|
...actual,
|
|
24
34
|
authorize: vi.fn(),
|
|
35
|
+
getAuthToken: vi.fn(() => ''),
|
|
36
|
+
removeAuthCookie: vi.fn(),
|
|
25
37
|
}
|
|
26
38
|
})
|
|
27
39
|
|
|
@@ -36,6 +48,7 @@ vi.mock('$lib/url', () => ({
|
|
|
36
48
|
describe('$routes/+page.svelte', () => {
|
|
37
49
|
beforeEach(() => {
|
|
38
50
|
vi.resetAllMocks()
|
|
51
|
+
vi.mocked(injectLinksInDocument).mockReturnValue([])
|
|
39
52
|
})
|
|
40
53
|
|
|
41
54
|
afterEach(() => {
|
|
@@ -131,12 +144,76 @@ describe('$routes/+page.svelte', () => {
|
|
|
131
144
|
expect(container.querySelector('.editor')).not.toBeTruthy()
|
|
132
145
|
})
|
|
133
146
|
|
|
147
|
+
it('Should not render editor trigger if getAuthToken does not return token', () => {
|
|
148
|
+
vi.mocked(getAuthToken).mockReturnValueOnce('')
|
|
149
|
+
|
|
150
|
+
const { queryByTitle } = render(page)
|
|
151
|
+
|
|
152
|
+
expect(queryByTitle('Show PlayPilot TPI editor')).not.toBeTruthy()
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('Should render editor trigger if getAuthToken returns token', () => {
|
|
156
|
+
vi.mocked(getAuthToken).mockReturnValueOnce('token')
|
|
157
|
+
|
|
158
|
+
const { getByTitle } = render(page)
|
|
159
|
+
|
|
160
|
+
expect(getByTitle('Show PlayPilot TPI editor')).toBeTruthy()
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('Should call removeAuthCookie when editor trigger close button is clicked', async () => {
|
|
164
|
+
vi.mocked(getAuthToken).mockReturnValueOnce('token')
|
|
165
|
+
|
|
166
|
+
const { getByTitle } = render(page)
|
|
167
|
+
|
|
168
|
+
await fireEvent.click(getByTitle('Close PlayPilot TPI'))
|
|
169
|
+
|
|
170
|
+
expect(removeAuthCookie).toHaveBeenCalled()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('Should open editor after clicking editor token', async () => {
|
|
174
|
+
vi.mocked(getAuthToken).mockReturnValueOnce('token')
|
|
175
|
+
vi.mocked(authorize).mockResolvedValueOnce(true)
|
|
176
|
+
|
|
177
|
+
const { getByTitle, container } = render(page)
|
|
178
|
+
|
|
179
|
+
await fireEvent.click(getByTitle('Show PlayPilot TPI editor'))
|
|
180
|
+
|
|
181
|
+
expect(container.querySelector('.editor')).toBeTruthy()
|
|
182
|
+
})
|
|
183
|
+
|
|
134
184
|
it('Should track event when the page is loaded', () => {
|
|
135
185
|
render(page)
|
|
136
186
|
|
|
137
187
|
expect(track).toHaveBeenCalledWith(TrackingEvent.ArticlePageView)
|
|
138
188
|
})
|
|
139
189
|
|
|
190
|
+
it('Should track injection event when injections are injected', async () => {
|
|
191
|
+
// @ts-ignore
|
|
192
|
+
vi.mocked(pollLinkInjections).mockResolvedValueOnce({ ai_injections: [generateInjection('a', 'b')], link_injections: [{ ...generateInjection('a', 'b'), manual: true }] })
|
|
193
|
+
vi.mocked(injectLinksInDocument).mockReturnValueOnce([generateInjection('a', 'b'), { ...generateInjection('a', 'b'), manual: true }])
|
|
194
|
+
|
|
195
|
+
render(page)
|
|
196
|
+
|
|
197
|
+
await waitFor(() => {
|
|
198
|
+
expect(track).toHaveBeenCalledWith(TrackingEvent.ArticleInjected, null, {
|
|
199
|
+
ai: 1,
|
|
200
|
+
manual: 1,
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('Should not track injection event when no injections were successful', async () => {
|
|
206
|
+
// @ts-ignore
|
|
207
|
+
vi.mocked(pollLinkInjections).mockResolvedValueOnce({ ai_injections: [generateInjection('a', 'b')], link_injections: [{ ...generateInjection('a', 'b'), manual: true }] })
|
|
208
|
+
vi.mocked(injectLinksInDocument).mockReturnValueOnce([{ ...generateInjection('a', 'b'), manual: true, failed: true }])
|
|
209
|
+
|
|
210
|
+
render(page)
|
|
211
|
+
|
|
212
|
+
await waitFor(() => expect(injectLinksInDocument).toHaveBeenCalled())
|
|
213
|
+
|
|
214
|
+
expect(track).toHaveBeenCalledOnce()
|
|
215
|
+
})
|
|
216
|
+
|
|
140
217
|
it('Should not initialize if isCrawler is true', async () => {
|
|
141
218
|
vi.mocked(isCrawler).mockReturnValueOnce(true)
|
|
142
219
|
|
|
@@ -66,10 +66,10 @@ describe('Editor.svelte', () => {
|
|
|
66
66
|
|
|
67
67
|
expect(getAllByText(title.title)).toHaveLength(2)
|
|
68
68
|
expect(track).toHaveBeenCalledWith(TrackingEvent.TotalInjectionsCount, null, {
|
|
69
|
-
total:
|
|
70
|
-
failed_automatic:
|
|
71
|
-
failed_manual:
|
|
72
|
-
final_injected:
|
|
69
|
+
total: 7,
|
|
70
|
+
failed_automatic: 2,
|
|
71
|
+
failed_manual: 1,
|
|
72
|
+
final_injected: 2,
|
|
73
73
|
})
|
|
74
74
|
})
|
|
75
75
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import EditorTrigger from '../../../../routes/components/Editorial/EditorTrigger.svelte'
|
|
5
|
+
|
|
6
|
+
describe('EditorTrigger.svelte', () => {
|
|
7
|
+
it('Should fire the given onclick function', async () => {
|
|
8
|
+
const onclick = vi.fn()
|
|
9
|
+
const { getByTitle } = render(EditorTrigger, { onclick, onclose: () => null })
|
|
10
|
+
|
|
11
|
+
await fireEvent.click(getByTitle('Show PlayPilot TPI editor'))
|
|
12
|
+
|
|
13
|
+
expect(onclick).toHaveBeenCalled()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('Should fire the given onclick function', async () => {
|
|
17
|
+
const onclose = vi.fn()
|
|
18
|
+
const { getByTitle } = render(EditorTrigger, { onclick: () => null, onclose })
|
|
19
|
+
|
|
20
|
+
await fireEvent.click(getByTitle('Close PlayPilot TPI'))
|
|
21
|
+
|
|
22
|
+
expect(onclose).toHaveBeenCalled()
|
|
23
|
+
})
|
|
24
|
+
})
|
|
@@ -5,6 +5,24 @@ import ManualInjection from '../../../../routes/components/Editorial/ManualInjec
|
|
|
5
5
|
import { searchTitles } from '$lib/search'
|
|
6
6
|
import { title } from '$lib/fakeData'
|
|
7
7
|
|
|
8
|
+
function createSelectionMock() {
|
|
9
|
+
const container = document.querySelector('div')
|
|
10
|
+
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
window.getSelection = vi.fn(() => ({
|
|
13
|
+
toString: () => 'text',
|
|
14
|
+
getRangeAt: () => ({
|
|
15
|
+
commonAncestorContainer: container,
|
|
16
|
+
startContainer: container,
|
|
17
|
+
startOffset: 0,
|
|
18
|
+
endOffset: 0,
|
|
19
|
+
cloneContents: () => ({ childNodes: [] }),
|
|
20
|
+
}),
|
|
21
|
+
anchorNode: container,
|
|
22
|
+
focusNode: container,
|
|
23
|
+
}))
|
|
24
|
+
}
|
|
25
|
+
|
|
8
26
|
vi.mock('$lib/search', () => ({
|
|
9
27
|
searchTitles: vi.fn(),
|
|
10
28
|
}))
|
|
@@ -14,27 +32,14 @@ describe('ManualInjection.svelte', () => {
|
|
|
14
32
|
vi.resetAllMocks()
|
|
15
33
|
|
|
16
34
|
document.body.innerHTML = '<div>Some text in a sentence</div>'
|
|
17
|
-
|
|
18
|
-
const container = document.querySelector('div')
|
|
19
|
-
|
|
20
|
-
// @ts-ignore
|
|
21
|
-
window.getSelection = vi.fn(() => ({
|
|
22
|
-
toString: () => 'text',
|
|
23
|
-
getRangeAt: () => ({
|
|
24
|
-
commonAncestorContainer: container,
|
|
25
|
-
startContainer: container,
|
|
26
|
-
startOffset: 0,
|
|
27
|
-
endOffset: 0,
|
|
28
|
-
}),
|
|
29
|
-
anchorNode: container,
|
|
30
|
-
focusNode: container,
|
|
31
|
-
}))
|
|
32
35
|
})
|
|
33
36
|
|
|
34
37
|
it('Should input selected text in selected text input and search input', async () => {
|
|
38
|
+
createSelectionMock()
|
|
39
|
+
|
|
35
40
|
const { getByLabelText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave: () => null })
|
|
36
41
|
|
|
37
|
-
await fireEvent.
|
|
42
|
+
await fireEvent.mouseUp(window)
|
|
38
43
|
|
|
39
44
|
expect(window.getSelection).toHaveBeenCalled()
|
|
40
45
|
expect(/** @type {HTMLFormElement} */ (getByLabelText('Select text on the page')).value).toBe('text')
|
|
@@ -42,17 +47,21 @@ describe('ManualInjection.svelte', () => {
|
|
|
42
47
|
})
|
|
43
48
|
|
|
44
49
|
it('Should disable save button by default', async () => {
|
|
50
|
+
createSelectionMock()
|
|
51
|
+
|
|
45
52
|
const { getByText } = render(ManualInjection, { htmlString: '', onsave: () => null })
|
|
46
53
|
|
|
47
54
|
expect(getByText('Add playlink').hasAttribute('disabled')).toBeTruthy()
|
|
48
55
|
})
|
|
49
56
|
|
|
50
57
|
it('Should enable save button after selecting text and selecting title from ManualInjection', async () => {
|
|
58
|
+
createSelectionMock()
|
|
59
|
+
|
|
51
60
|
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
52
61
|
|
|
53
62
|
const { getByText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave: () => null })
|
|
54
63
|
|
|
55
|
-
await fireEvent.
|
|
64
|
+
await fireEvent.mouseUp(window)
|
|
56
65
|
await waitFor(() => getByText(title.title))
|
|
57
66
|
await fireEvent.click(getByText(title.title))
|
|
58
67
|
|
|
@@ -60,12 +69,14 @@ describe('ManualInjection.svelte', () => {
|
|
|
60
69
|
})
|
|
61
70
|
|
|
62
71
|
it('Should call given onsave function when clicked', async () => {
|
|
72
|
+
createSelectionMock()
|
|
73
|
+
|
|
63
74
|
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
64
75
|
|
|
65
76
|
const onsave = vi.fn()
|
|
66
77
|
const { getByText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave })
|
|
67
78
|
|
|
68
|
-
await fireEvent.
|
|
79
|
+
await fireEvent.mouseUp(window)
|
|
69
80
|
await fireEvent.click(getByText(title.title))
|
|
70
81
|
await fireEvent.click(getByText('Add playlink'))
|
|
71
82
|
|
|
@@ -95,6 +106,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
95
106
|
startContainer: container,
|
|
96
107
|
startOffset: 0,
|
|
97
108
|
endOffset: 0,
|
|
109
|
+
cloneContents: () => ({ childNodes: [] }),
|
|
98
110
|
}),
|
|
99
111
|
anchorNode: container,
|
|
100
112
|
focusNode: container,
|
|
@@ -103,7 +115,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
103
115
|
const onsave = vi.fn()
|
|
104
116
|
const { getByText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave })
|
|
105
117
|
|
|
106
|
-
await fireEvent.
|
|
118
|
+
await fireEvent.mouseUp(window)
|
|
107
119
|
await fireEvent.click(getByText(title.title))
|
|
108
120
|
await fireEvent.click(getByText('Add playlink'))
|
|
109
121
|
|
|
@@ -136,12 +148,13 @@ describe('ManualInjection.svelte', () => {
|
|
|
136
148
|
startContainer: container,
|
|
137
149
|
startOffset: 0,
|
|
138
150
|
endOffset: 0,
|
|
151
|
+
cloneContents: () => ({ childNodes: [] }),
|
|
139
152
|
}),
|
|
140
153
|
anchorNode: container,
|
|
141
154
|
focusNode: container,
|
|
142
155
|
}))
|
|
143
156
|
|
|
144
|
-
await fireEvent.
|
|
157
|
+
await fireEvent.mouseUp(window)
|
|
145
158
|
|
|
146
159
|
expect(searchTitles).not.toHaveBeenCalled()
|
|
147
160
|
|
|
@@ -157,6 +170,7 @@ describe('ManualInjection.svelte', () => {
|
|
|
157
170
|
startContainer: container,
|
|
158
171
|
startOffset: 0,
|
|
159
172
|
endOffset: 0,
|
|
173
|
+
cloneContents: () => ({ childNodes: [] }),
|
|
160
174
|
}),
|
|
161
175
|
anchorNode: container,
|
|
162
176
|
focusNode: container,
|
|
@@ -164,8 +178,38 @@ describe('ManualInjection.svelte', () => {
|
|
|
164
178
|
|
|
165
179
|
;(render(ManualInjection, { htmlString: document.body.innerHTML, onsave }))
|
|
166
180
|
|
|
167
|
-
await fireEvent.
|
|
181
|
+
await fireEvent.mouseUp(window)
|
|
168
182
|
|
|
169
183
|
await waitFor(() => expect(searchTitles).toHaveBeenCalled())
|
|
170
184
|
})
|
|
185
|
+
|
|
186
|
+
it('Should not select content if it contains multiple child nodes with content', async () => {
|
|
187
|
+
document.body.innerHTML = '<main><div>Some text in a sentence</div></main>'
|
|
188
|
+
|
|
189
|
+
const container = document.querySelector('div')
|
|
190
|
+
const onsave = vi.fn()
|
|
191
|
+
const { getByText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave })
|
|
192
|
+
|
|
193
|
+
// @ts-ignore
|
|
194
|
+
window.getSelection = vi.fn(() => ({
|
|
195
|
+
toString: () => 'Some text',
|
|
196
|
+
getRangeAt: () => ({
|
|
197
|
+
commonAncestorContainer: container,
|
|
198
|
+
startContainer: container,
|
|
199
|
+
startOffset: 0,
|
|
200
|
+
endOffset: 0,
|
|
201
|
+
cloneContents: () => ({ childNodes: [{ textContent: 'a' }, { textContent: 'b' }] }),
|
|
202
|
+
}),
|
|
203
|
+
anchorNode: container,
|
|
204
|
+
focusNode: container,
|
|
205
|
+
}))
|
|
206
|
+
|
|
207
|
+
await fireEvent.mouseUp(window)
|
|
208
|
+
|
|
209
|
+
expect(searchTitles).not.toHaveBeenCalled()
|
|
210
|
+
|
|
211
|
+
await waitFor(() => {
|
|
212
|
+
expect(getByText('Selection contains multiple items. Selection may not contain a mix of styled and non styled text. Please select the text more directly.')).toBeTruthy()
|
|
213
|
+
})
|
|
214
|
+
})
|
|
171
215
|
})
|