@playpilot/tpi 5.32.0-beta.3 → 5.32.0-beta.youtube.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.
Files changed (32) hide show
  1. package/dist/link-injections.js +11 -11
  2. package/package.json +1 -1
  3. package/src/lib/data/translations.ts +5 -5
  4. package/src/lib/enums/SplitTest.ts +0 -5
  5. package/src/lib/fakeData.ts +1 -0
  6. package/src/lib/injection.ts +4 -35
  7. package/src/lib/scss/global.scss +0 -39
  8. package/src/lib/text.ts +2 -1
  9. package/src/lib/trailer.ts +22 -0
  10. package/src/lib/types/title.d.ts +1 -0
  11. package/src/routes/+page.svelte +2 -17
  12. package/src/routes/components/Button.svelte +61 -0
  13. package/src/routes/components/Debugger.svelte +0 -8
  14. package/src/routes/components/Icons/IconClose.svelte +9 -1
  15. package/src/routes/components/Icons/IconPlay.svelte +3 -0
  16. package/src/routes/components/Playlinks/PlaylinkIcon.svelte +4 -1
  17. package/src/routes/components/RoundButton.svelte +4 -5
  18. package/src/routes/components/Share.svelte +5 -23
  19. package/src/routes/components/Title.svelte +22 -22
  20. package/src/routes/components/Trailer.svelte +18 -0
  21. package/src/routes/components/YouTubeEmbedOverlay.svelte +96 -0
  22. package/src/tests/lib/injections.test.js +11 -0
  23. package/src/tests/lib/text.test.js +10 -0
  24. package/src/tests/lib/trailer.test.js +56 -0
  25. package/src/tests/routes/+page.test.js +0 -2
  26. package/src/tests/routes/components/Button.test.js +28 -0
  27. package/src/tests/routes/components/Share.test.js +12 -12
  28. package/src/tests/routes/components/Title.test.js +13 -0
  29. package/src/tests/routes/components/Trailer.test.js +20 -0
  30. package/src/tests/routes/components/YouTubeEmbedOverlay.test.js +31 -0
  31. package/src/routes/components/HighlightedInjection.svelte +0 -230
  32. package/src/tests/routes/components/HighlightedInjection.test.js +0 -98
@@ -714,6 +714,17 @@ describe('linkInjection.js', () => {
714
714
  expect(document.querySelectorAll('a')).toHaveLength(1)
715
715
  })
716
716
 
717
+ it('Should not inject injections into already existing links when multiple injections are present for the same phrase in the same sentence that only contain the phrase once and the phrase is broken up by HTML', () => {
718
+ document.body.innerHTML = '<p>This is a <strong>phr</strong>ase.</p>'
719
+
720
+ const elements = Array.from(document.querySelectorAll('p'))
721
+ const injection = generateInjection('This is a phrase.', 'phrase')
722
+
723
+ injectLinksInDocument(elements, { aiInjections: [injection, injection], manualInjections: [] })
724
+
725
+ expect(document.querySelectorAll('a')).toHaveLength(1)
726
+ })
727
+
717
728
  it('Should not mount popover if user uses touch', async () => {
718
729
  mockMatchMedia(true)
719
730
 
@@ -364,6 +364,16 @@ describe('text.js', () => {
364
364
  after: 'matched',
365
365
  })
366
366
  })
367
+
368
+ it('Should return after phrase properly if element contains odd spacing in elements', () => {
369
+ document.body.innerHTML = '<p>Text with a <em>phrase </em><em>that</em> can be matched</p>'
370
+ const node = /** @type {Element} */ (document.querySelector('body')).childNodes[0]
371
+
372
+ expect(findSurroundingPhrases(node, 12, 23)).toEqual({
373
+ before: 'with a',
374
+ after: 'can be',
375
+ })
376
+ })
367
377
  })
368
378
 
369
379
  describe('reverseString', () => {
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
+ import { closeCurrentModal, destroyAllModals, destroyCurrentModal, getAllModals, getPreviousModal, goBackToPreviousModal, openModal } from '$lib/modal'
3
+ import { linkInjections, title } from '$lib/fakeData'
4
+ import { mount, unmount } from 'svelte'
5
+ import ParticipantModal from '../../routes/components/ParticipantModal.svelte'
6
+ import TitleModal from '../../routes/components/TitleModal.svelte'
7
+ import { closeTrailerOverlay, openTrailerOverlay } from '$lib/trailer'
8
+
9
+ vi.mock('svelte', () => ({
10
+ mount: vi.fn(),
11
+ unmount: vi.fn(),
12
+ }))
13
+
14
+ const titleWithTrailer = { ...title, embeddable_url: 'abc' }
15
+
16
+ describe('modal.js', () => {
17
+ beforeEach(() => {
18
+ vi.resetAllMocks()
19
+ })
20
+
21
+ describe('openTrailerOverlay', () => {
22
+ it('Should call mount with given title embeddable_url', () => {
23
+ openTrailerOverlay(titleWithTrailer)
24
+
25
+ expect(mount).toHaveBeenCalledWith(
26
+ expect.any(Function),
27
+ expect.objectContaining({
28
+ target: expect.anything(),
29
+ props: expect.objectContaining({
30
+ onclose: expect.any(Function),
31
+ embeddable_url: titleWithTrailer.embeddable_url,
32
+ }),
33
+ }),
34
+ )
35
+ })
36
+ })
37
+
38
+ describe('closeTrailerOverlay', () => {
39
+ it('Should call unmount if component was previously mounted', () => {
40
+ vi.mocked(mount).mockReturnValueOnce({})
41
+
42
+ openTrailerOverlay(titleWithTrailer)
43
+ closeTrailerOverlay()
44
+
45
+ expect(unmount).toHaveBeenCalled()
46
+ })
47
+
48
+ it('Should not call unmount if component was not previously mounted', () => {
49
+ vi.mocked(mount).mockReturnValueOnce({})
50
+
51
+ closeTrailerOverlay()
52
+
53
+ expect(unmount).not.toHaveBeenCalled()
54
+ })
55
+ })
56
+ })
@@ -13,7 +13,6 @@ import { getFullUrlPath } from '$lib/url'
13
13
  import { fetchAds } from '$lib/api/ads'
14
14
  import { fetchConfig } from '$lib/api/config'
15
15
  import { hasConsentedTo } from '$lib/consent'
16
- import { getSplitTestVariantName } from '$lib/splitTest'
17
16
 
18
17
  vi.mock('$lib/api/externalPages', () => ({
19
18
  fetchLinkInjections: vi.fn(() => {}),
@@ -67,7 +66,6 @@ vi.mock('$lib/url', () => ({
67
66
 
68
67
  vi.mock('$lib/splitTest', () => ({
69
68
  trackSplitTestView: vi.fn(),
70
- getSplitTestVariantName: vi.fn(),
71
69
  }))
72
70
 
73
71
  vi.mock('$lib/api/ads', () => ({
@@ -0,0 +1,28 @@
1
+ import { render, fireEvent } from '@testing-library/svelte'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+
4
+ import Button from '../../../routes/components/Button.svelte'
5
+
6
+ describe('Button.svelte', () => {
7
+ it('Should use filled class by default', () => {
8
+ const { getByRole } = render(Button)
9
+
10
+ expect(getByRole('button').classList).toContain('filled')
11
+ })
12
+
13
+ it('Should use border class when border variant is given', () => {
14
+ const { getByRole } = render(Button, { variant: 'border' })
15
+
16
+ expect(getByRole('button').classList).not.toContain('filled')
17
+ expect(getByRole('button').classList).toContain('border')
18
+ })
19
+
20
+ it('Should fire given onclick function on click', async () => {
21
+ const onclick = vi.fn()
22
+ const { getByRole } = render(Button, { onclick })
23
+
24
+ await fireEvent.click(getByRole('button'))
25
+
26
+ expect(onclick).toHaveBeenCalled()
27
+ })
28
+ })
@@ -16,57 +16,57 @@ vi.mock('$lib/tracking', () => ({
16
16
 
17
17
  describe('Share.svelte', () => {
18
18
  it('Should open context menu on click', async () => {
19
- const { getByLabelText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
19
+ const { getByText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
20
20
 
21
21
  expect(queryByText('Copy URL')).not.toBeTruthy()
22
22
  expect(queryByText('Email')).not.toBeTruthy()
23
23
 
24
- await fireEvent.click(getByLabelText('Share'))
24
+ await fireEvent.click(getByText('Share'))
25
25
 
26
26
  expect(queryByText('Copy URL')).toBeTruthy()
27
27
  expect(queryByText('Email')).toBeTruthy()
28
28
  })
29
29
 
30
30
  it('Should close context menu on click of items', async () => {
31
- const { getByLabelText, getByText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
31
+ const { getByText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
32
32
 
33
- await fireEvent.click(getByLabelText('Share'))
33
+ await fireEvent.click(getByText('Share'))
34
34
  await fireEvent.click(getByText('Copy URL'))
35
35
 
36
36
  expect(queryByText('Copy URL')).not.toBeTruthy()
37
37
  })
38
38
 
39
39
  it('Should close context menu on click of body', async () => {
40
- const { getByLabelText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
40
+ const { getByText, queryByText } = render(Share, { title: 'Some title', url: 'some-url' })
41
41
 
42
- await fireEvent.click(getByLabelText('Share'))
42
+ await fireEvent.click(getByText('Share'))
43
43
  await fireEvent.click(document.body)
44
44
 
45
45
  expect(queryByText('Copy URL')).not.toBeTruthy()
46
46
  })
47
47
 
48
48
  it('Should fire copyToClipboard on click of button', async () => {
49
- const { getByLabelText, getByText } = render(Share, { title: 'Some title', url: 'some-url' })
49
+ const { getByText } = render(Share, { title: 'Some title', url: 'some-url' })
50
50
 
51
- await fireEvent.click(getByLabelText('Share'))
51
+ await fireEvent.click(getByText('Share'))
52
52
  await fireEvent.click(getByText('Copy URL'))
53
53
 
54
54
  expect(copyToClipboard).toHaveBeenCalledWith('some-url?utm_source=tpi')
55
55
  })
56
56
 
57
57
  it('Should fire track function on click of copy URL button', async () => {
58
- const { getByLabelText, getByText } = render(Share, { title: 'Some title', url: 'some-url' })
58
+ const { getByText } = render(Share, { title: 'Some title', url: 'some-url' })
59
59
 
60
- await fireEvent.click(getByLabelText('Share'))
60
+ await fireEvent.click(getByText('Share'))
61
61
  await fireEvent.click(getByText('Copy URL'))
62
62
 
63
63
  expect(track).toHaveBeenCalledWith(TrackingEvent.ShareTitle, null, { title: 'Some title', url: 'http://localhost:3000/', method: 'copy' })
64
64
  })
65
65
 
66
66
  it('Should fire track function on click of email button', async () => {
67
- const { getByLabelText, getByText } = render(Share, { title: 'Some title', url: 'some-url' })
67
+ const { getByText } = render(Share, { title: 'Some title', url: 'some-url' })
68
68
 
69
- await fireEvent.click(getByLabelText('Share'))
69
+ await fireEvent.click(getByText('Share'))
70
70
  await fireEvent.click(getByText('Email'))
71
71
 
72
72
  expect(track).toHaveBeenCalledWith(TrackingEvent.ShareTitle, null, { title: 'Some title', url: 'http://localhost:3000/', method: 'email' })
@@ -91,4 +91,17 @@ describe('Title.svelte', () => {
91
91
  expect(fetchParticipantsForTitle).toHaveBeenCalled()
92
92
  expect(fetchSimilarTitles).toHaveBeenCalled()
93
93
  })
94
+
95
+ it('Should show trailer button when embeddable_url is given', () => {
96
+ const { getByText } = render(Title, { title: { ...title, embeddable_url: 'some-url' } })
97
+
98
+ expect(getByText('Watch trailer')).toBeTruthy()
99
+ })
100
+
101
+ // Temporarily disabled while button is always visible
102
+ // it('Should not show trailer button when embeddable_url is not given', () => {
103
+ // const { queryByText } = render(Title, { title })
104
+
105
+ // expect(queryByText('Watch Trailer')).not.toBeTruthy()
106
+ // })
94
107
  })
@@ -0,0 +1,20 @@
1
+ import { render, fireEvent } from '@testing-library/svelte'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+
4
+ import Trailer from '../../../routes/components/Trailer.svelte'
5
+ import { title } from '$lib/fakeData'
6
+ import { openTrailerOverlay } from '$lib/trailer'
7
+
8
+ vi.mock('$lib/trailer', () => ({
9
+ openTrailerOverlay: vi.fn(),
10
+ }))
11
+
12
+ describe('Trailer.svelte', () => {
13
+ it('Should fire given onclick function with given title on click', async () => {
14
+ const { getByRole } = render(Trailer, { title })
15
+
16
+ await fireEvent.click(getByRole('button'))
17
+
18
+ expect(openTrailerOverlay).toHaveBeenCalledWith(title)
19
+ })
20
+ })
@@ -0,0 +1,31 @@
1
+ import { fireEvent, render } from '@testing-library/svelte'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+
4
+ import YouTubeEmbedOverlay from '../../../routes/components/YouTubeEmbedOverlay.svelte'
5
+
6
+ describe('YouTubeEmbedOverlay.svelte', () => {
7
+ it('Should render embed iframe with given video url', () => {
8
+ const { container } = render(YouTubeEmbedOverlay, { embeddable_url: 'youtube.com/watch?v=abc', onclose: () => null })
9
+
10
+ // @ts-ignore
11
+ expect(container.querySelector('iframe').src).toBe('https://www.youtube.com/embed/abc?autoplay=true')
12
+ })
13
+
14
+ it('Should render error message if embeddable_url is invalid', () => {
15
+ const { container, getByText } = render(YouTubeEmbedOverlay, { embeddable_url: '-', onclose: () => null })
16
+
17
+ expect(container.querySelector('iframe')).not.toBeTruthy()
18
+ expect(getByText('Something went wrong')).toBeTruthy()
19
+ })
20
+
21
+ it('Should fire given onclose function on click of close button and backdrop', async () => {
22
+ const onclose = vi.fn()
23
+ const { getByLabelText, getByTestId } = render(YouTubeEmbedOverlay, { embeddable_url: '-', onclose })
24
+
25
+ await fireEvent.click(getByLabelText('Close'))
26
+ expect(onclose).toHaveBeenCalledTimes(1)
27
+
28
+ await fireEvent.click(getByTestId('backdrop'))
29
+ expect(onclose).toHaveBeenCalledTimes(2)
30
+ })
31
+ })
@@ -1,230 +0,0 @@
1
- <script lang="ts">
2
- import { filterRemovedAndInactiveInjections, sortInjections } from '$lib/injection'
3
- import { t } from '$lib/localization'
4
- import { titleUrl } from '$lib/routes'
5
- import { cleanPhrase } from '$lib/text'
6
- import type { LinkInjection } from '$lib/types/injection'
7
- import { openModal } from '$lib/modal'
8
- import { trackSplitTestAction } from '$lib/splitTest'
9
- import { SplitTest } from '$lib/enums/SplitTest'
10
- import { scale } from 'svelte/transition'
11
- import IconIMDb from './Icons/IconIMDb.svelte'
12
- import TitlePoster from './TitlePoster.svelte'
13
- import RoundButton from './RoundButton.svelte'
14
- import IconClose from './Icons/IconClose.svelte'
15
-
16
- interface Props {
17
- linkInjections: LinkInjection[]
18
- }
19
-
20
- const { linkInjections }: Props = $props()
21
-
22
- const scrollThreshold = Math.min(window.innerHeight, 800)
23
-
24
- const filteredInjections = $derived(filterRemovedAndInactiveInjections(linkInjections))
25
- const sortedInjections: LinkInjection[] = $derived(sortInjections(filteredInjections))
26
-
27
- // Get title injection that might relate the most the overall article.
28
- // If any is found it is used as the primary display, highlighting that injection over others.
29
- // If none are found we simply use the first active injection and hope for the best.
30
- const primaryInjection = $derived.by(() => {
31
- const pageTitle = cleanPhrase(document.querySelector('h1')?.innerText || document.title || '')
32
- return sortedInjections.find(injection => pageTitle.includes(cleanPhrase(injection.title))) || filteredInjections[0]
33
- })
34
-
35
- let shown = $state(false)
36
- let closed = $state(false)
37
- let offsetBottom = $state(0)
38
-
39
- // Only show the element once the user has scroll past a threshold. From here we check the offset from the bottom of
40
- // the page to make sure we don't overlay the element on top of other elements on the page, such as ads or menus
41
- function onscroll(): void {
42
- if (shown || closed) return
43
- if (window.scrollY < scrollThreshold) return
44
-
45
- const bottomFixedElementOffsets = getBottomFixedElementOffsets()
46
- const largestOffset = Math.max(...bottomFixedElementOffsets, 0)
47
-
48
- offsetBottom = largestOffset + parseFloat(getComputedStyle(document.documentElement).fontSize) // 1 rem
49
- shown = true
50
-
51
- trackSplitTestAction(SplitTest.InTextEngagement, 'shown')
52
- }
53
-
54
- // Get all elements that are seemingly fixed to the bottom. This is used to determine the position of the element to try
55
- // and make sure it does not overlap ads or other elements at the bottom of the screen.
56
- // This is determined by them being fixed (duh) and being close to the bottom of the screen. This might turn out to be unreliable.
57
- // It does not account for elements that are added after this function is called, which is often the case for ads.
58
- function getBottomFixedElementOffsets(): number[] {
59
- const fixedElements = Array.from(document.querySelectorAll('*')).filter((element) => {
60
- const style = getComputedStyle(element)
61
- const isFixed = style.position === 'fixed'
62
- const isVisible = style.display !== 'none' && style.visibility !== 'hidden'
63
-
64
- const rect = element.getBoundingClientRect()
65
- const isCloseToBottom = rect.bottom < 50
66
- const hasSize = rect.width > 0 && rect.height > 0
67
-
68
- return isFixed && isVisible && isCloseToBottom && hasSize
69
- })
70
-
71
- return fixedElements.map((element) => element.clientHeight + (window.innerHeight - element.getBoundingClientRect().bottom))
72
- }
73
-
74
- function onclick(event: MouseEvent): void {
75
- event.preventDefault()
76
-
77
- closed = true
78
-
79
- openModal({ event, injection: primaryInjection, data: primaryInjection.title_details })
80
- trackSplitTestAction(SplitTest.InTextEngagement, 'click-highlight')
81
- }
82
-
83
- function close(event: MouseEvent): void {
84
- event.preventDefault()
85
-
86
- closed = true
87
-
88
- trackSplitTestAction(SplitTest.InTextEngagement, 'close')
89
- }
90
- </script>
91
-
92
- <svelte:window {onscroll} />
93
-
94
- {#if primaryInjection && shown && !closed}
95
- {@const title = primaryInjection.title_details!}
96
-
97
- <div class="highlighted-injection" style:bottom="{offsetBottom}px" in:scale={{ start: 0.85, duration: 100 }}>
98
- <a {onclick} class="link" href={titleUrl(title)} target="_blank" aria-label="{title.title} (opens in a new tab)">
99
- <div class="poster">
100
- <TitlePoster {title} width={30} height={43} />
101
- </div>
102
-
103
- <div class="content">
104
- <div class="details">
105
- <div class="heading">{title.title}</div>
106
-
107
- <div class="meta">
108
- <div class="imdb">
109
- <IconIMDb />
110
- {title.imdb_score || '-'}
111
- </div>
112
-
113
- <div>{title.year}</div>
114
-
115
- <div class="capitalize">{t(`Type: ${title.type}`)}</div>
116
-
117
- {#if title.length}
118
- <div>{title.length} {t('Minutes Short')}</div>
119
- {/if}
120
- </div>
121
- </div>
122
-
123
- <div class="action">
124
- {t('Watch')}
125
- </div>
126
- </div>
127
- </a>
128
-
129
- <div class="close">
130
- <RoundButton onclick={close} aria-label="Close">
131
- <IconClose />
132
- </RoundButton>
133
- </div>
134
- </div>
135
- {/if}
136
-
137
- <style lang="scss">
138
- .highlighted-injection {
139
- position: fixed;
140
- bottom: margin(1);
141
- left: margin(1);
142
- width: calc(100% - margin(2));
143
-
144
- @include desktop {
145
- max-width: 400px;
146
- }
147
- }
148
-
149
- .link {
150
- display: flex;
151
- gap: margin(1);
152
- align-items: flex-start;
153
- border-radius: theme(border-radius);
154
- padding: margin(0.5) margin(1) margin(0.5) margin(0.5);
155
- background: theme(dark);
156
- box-shadow: theme(shadow-large);
157
- text-decoration: none;
158
-
159
- &:hover,
160
- &:active {
161
- transform: scale(1.025);
162
- transition: transform 50ms;
163
- filter: brightness(theme(hover-filter-brightness));
164
- }
165
-
166
- &:active {
167
- transform: scale(0.975);
168
- }
169
- }
170
-
171
- .poster {
172
- flex: 0 0 margin(2);
173
- width: margin(2);
174
- box-shadow: 0 0 2px 1px theme(content);
175
- border-radius: theme(border-radius-small);
176
- background: theme(content);
177
- overflow: hidden;
178
- }
179
-
180
- .content {
181
- display: flex;
182
- align-items: center;
183
- gap: margin(0.5);
184
- width: 100%;
185
- margin: auto 0;
186
- color: theme(text-color-alt);
187
- font-family: theme(font-family);
188
- font-size: 12px;
189
- line-height: 1.5;
190
- font-style: normal;
191
- }
192
-
193
- .heading {
194
- margin-bottom: margin(0.25);
195
- font-size: theme(font-size-base);
196
- font-weight: theme(font-bold);
197
- color: theme(text-color);
198
- }
199
-
200
- .meta {
201
- display: flex;
202
- flex-wrap: wrap;
203
- gap: 0 margin(0.5);
204
- white-space: nowrap;
205
- color: theme(text-color-alt);
206
- }
207
-
208
- .imdb {
209
- display: flex;
210
- align-items: center;
211
- gap: margin(0.25);
212
- }
213
-
214
- .action {
215
- grid-area: action;
216
- margin-left: auto;
217
- padding: margin(0.5);
218
- border: theme(playlinks-action-border, 1px solid currentColor);
219
- border-radius: theme(playlinks-action-border-radius, margin(2));
220
- font-weight: theme(playlinks-action-font-weight, 500);
221
- color: theme(playlinks-action-text-color, text-color);
222
- line-height: 1;
223
- }
224
-
225
- .close {
226
- position: absolute;
227
- top: margin(-0.75);
228
- right: margin(-0.75);
229
- }
230
- </style>
@@ -1,98 +0,0 @@
1
- import { render, fireEvent, waitFor } from '@testing-library/svelte'
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
3
-
4
- import HighlightedInjection from '../../../routes/components/HighlightedInjection.svelte'
5
- import { generateInjection } from '../../helpers'
6
- import { title } from '$lib/fakeData'
7
- import { openModal } from '$lib/modal'
8
-
9
- vi.mock('$lib/modal', () => ({
10
- openModal: vi.fn(),
11
- }))
12
-
13
- /**
14
- * @param {number} value
15
- */
16
- async function mockScroll(value) {
17
- Object.defineProperty(window, 'scrollY', { value, configurable: true })
18
- window.dispatchEvent(new Event('scroll'))
19
-
20
- return new Promise(res => setTimeout(res))
21
- }
22
-
23
- describe('HighlightedInjection.svelte', () => {
24
- beforeEach(() => {
25
- vi.resetAllMocks()
26
- })
27
-
28
- const linkInjections = [
29
- generateInjection('Some sentence', 'Some title'),
30
- { ...generateInjection('Some sentence', 'Some title'), title_details: { ...title, title: 'Some second injection' } },
31
- ]
32
-
33
- it('Should show after scrolling the required distance', async () => {
34
- const { queryByRole } = render(HighlightedInjection, { linkInjections })
35
-
36
- await mockScroll(0)
37
- expect(queryByRole('link')).not.toBeTruthy()
38
-
39
- await mockScroll(200)
40
- expect(queryByRole('link')).not.toBeTruthy()
41
-
42
- await mockScroll(1000)
43
- expect(queryByRole('link')).toBeTruthy()
44
- })
45
-
46
- it('Should not hide after being shown by scrolling', async () => {
47
- const { queryByRole } = render(HighlightedInjection, { linkInjections })
48
-
49
- await mockScroll(1000)
50
- expect(queryByRole('link')).toBeTruthy()
51
-
52
- await mockScroll(0)
53
- expect(queryByRole('link')).toBeTruthy()
54
- })
55
-
56
- it('Should render the first injection in the list', async () => {
57
- const { getByText } = render(HighlightedInjection, { linkInjections })
58
-
59
- await mockScroll(1000)
60
-
61
- // @ts-ignore
62
- expect(getByText(linkInjections[0].title_details.title)).toBeTruthy()
63
- })
64
-
65
- it('Should render the injection most relevant to the document title', async () => {
66
- // @ts-ignore
67
- document.body.innerHTML = `<h1>${linkInjections[1].title_details.title}</h1>`
68
-
69
- const { getByText } = render(HighlightedInjection, { linkInjections })
70
-
71
- await mockScroll(1000)
72
-
73
- // @ts-ignore
74
- expect(getByText(linkInjections[1].title_details.title)).toBeTruthy()
75
- })
76
-
77
- it('Should open modal on click', async () => {
78
- const { getByRole } = render(HighlightedInjection, { linkInjections })
79
-
80
- await mockScroll(1000)
81
- await fireEvent.click(getByRole('link'))
82
-
83
- expect(openModal).toHaveBeenCalled()
84
- })
85
-
86
- it('Should close elements when close button is closed', async () => {
87
- const { getByLabelText, queryByRole } = render(HighlightedInjection, { linkInjections })
88
-
89
- await mockScroll(1000)
90
- await fireEvent.click(getByLabelText('Close'))
91
-
92
- await waitFor(() => {
93
- expect(queryByRole('link')).not.toBeTruthy()
94
- })
95
-
96
- expect(openModal).not.toHaveBeenCalled()
97
- })
98
- })