@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.
@@ -1,5 +1,5 @@
1
- import { fireEvent } from '@testing-library/svelte'
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 each item in a list separately', () => {
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(7)
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
- <div>Some text</div>
690
- <div>Some other text</div>
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: '7',
70
- failed_automatic: '2',
71
- failed_manual: '1',
72
- final_injected: '2',
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.click(window)
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.click(window)
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.click(window)
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.click(window)
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.click(window)
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.click(window)
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
  })