@playpilot/tpi 1.0.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/.github/workflows/tests.yml +22 -0
- package/.prettierignore +4 -0
- package/.prettierrc +16 -0
- package/README.md +38 -0
- package/dist/link-injections.js +7 -0
- package/eslint.config.js +33 -0
- package/index.html +11 -0
- package/jsconfig.json +19 -0
- package/package.json +35 -0
- package/src/app.d.ts +13 -0
- package/src/app.html +12 -0
- package/src/demo.spec.js +7 -0
- package/src/lib/api.js +160 -0
- package/src/lib/array.js +15 -0
- package/src/lib/auth.js +84 -0
- package/src/lib/constants.js +2 -0
- package/src/lib/enums/TrackingEvent.js +15 -0
- package/src/lib/fakeData.js +140 -0
- package/src/lib/genres.json +420 -0
- package/src/lib/global.css +37 -0
- package/src/lib/hash.js +15 -0
- package/src/lib/html.js +21 -0
- package/src/lib/index.js +1 -0
- package/src/lib/linkInjection.js +275 -0
- package/src/lib/search.js +24 -0
- package/src/lib/text.js +61 -0
- package/src/lib/tracking.js +32 -0
- package/src/lib/variables.css +16 -0
- package/src/main.js +45 -0
- package/src/routes/+layout.svelte +54 -0
- package/src/routes/+page.svelte +96 -0
- package/src/routes/components/AfterArticlePlaylinks.svelte +90 -0
- package/src/routes/components/ContextMenu.svelte +67 -0
- package/src/routes/components/Description.svelte +47 -0
- package/src/routes/components/Editorial/Alert.svelte +18 -0
- package/src/routes/components/Editorial/DragHandle.svelte +134 -0
- package/src/routes/components/Editorial/Editor.svelte +277 -0
- package/src/routes/components/Editorial/EditorItem.svelte +260 -0
- package/src/routes/components/Editorial/ManualInjection.svelte +192 -0
- package/src/routes/components/Editorial/PlaylinkTypeSelect.svelte +132 -0
- package/src/routes/components/Editorial/Search/TitleSearch.svelte +176 -0
- package/src/routes/components/Editorial/Switch.svelte +76 -0
- package/src/routes/components/Editorial/TextInput.svelte +29 -0
- package/src/routes/components/Genres.svelte +41 -0
- package/src/routes/components/Icons/IconAlign.svelte +12 -0
- package/src/routes/components/Icons/IconBack.svelte +3 -0
- package/src/routes/components/Icons/IconBookmark.svelte +3 -0
- package/src/routes/components/Icons/IconChevron.svelte +18 -0
- package/src/routes/components/Icons/IconClose.svelte +3 -0
- package/src/routes/components/Icons/IconContinue.svelte +3 -0
- package/src/routes/components/Icons/IconDots.svelte +5 -0
- package/src/routes/components/Icons/IconEnlarge.svelte +12 -0
- package/src/routes/components/Icons/IconIMDb.svelte +3 -0
- package/src/routes/components/Icons/IconNewTab.svelte +3 -0
- package/src/routes/components/Modal.svelte +106 -0
- package/src/routes/components/Participants.svelte +44 -0
- package/src/routes/components/Playlinks.svelte +155 -0
- package/src/routes/components/Popover.svelte +95 -0
- package/src/routes/components/RoundButton.svelte +38 -0
- package/src/routes/components/SkeletonText.svelte +33 -0
- package/src/routes/components/Title.svelte +180 -0
- package/src/routes/components/TitleModal.svelte +24 -0
- package/src/routes/components/TitlePopover.svelte +17 -0
- package/src/tests/helpers.js +18 -0
- package/src/tests/lib/api.test.js +162 -0
- package/src/tests/lib/array.test.js +14 -0
- package/src/tests/lib/auth.test.js +115 -0
- package/src/tests/lib/hash.test.js +28 -0
- package/src/tests/lib/html.test.js +16 -0
- package/src/tests/lib/linkInjection.test.js +754 -0
- package/src/tests/lib/search.test.js +42 -0
- package/src/tests/lib/text.test.js +94 -0
- package/src/tests/lib/tracking.test.js +71 -0
- package/src/tests/routes/+page.test.js +109 -0
- package/src/tests/routes/components/AfterArticlePlaylinks.test.js +115 -0
- package/src/tests/routes/components/ContextMenu.test.js +37 -0
- package/src/tests/routes/components/Description.test.js +58 -0
- package/src/tests/routes/components/Editorial/Alert.test.js +17 -0
- package/src/tests/routes/components/Editorial/DragHandle.test.js +55 -0
- package/src/tests/routes/components/Editorial/Editor.test.js +64 -0
- package/src/tests/routes/components/Editorial/EditorItem.test.js +142 -0
- package/src/tests/routes/components/Editorial/ManualInjection.test.js +114 -0
- package/src/tests/routes/components/Editorial/PlaylinkTypeSelect.test.js +63 -0
- package/src/tests/routes/components/Editorial/Search/TitleSearch.test.js +58 -0
- package/src/tests/routes/components/Editorial/Switch.test.js +60 -0
- package/src/tests/routes/components/Editorial/TextInput.test.js +30 -0
- package/src/tests/routes/components/Genres.test.js +37 -0
- package/src/tests/routes/components/Modal.test.js +84 -0
- package/src/tests/routes/components/Participants.test.js +33 -0
- package/src/tests/routes/components/Playlinks.test.js +101 -0
- package/src/tests/routes/components/Popover.test.js +66 -0
- package/src/tests/routes/components/RoundButton.test.js +35 -0
- package/src/tests/routes/components/SkeletonText.test.js +12 -0
- package/src/tests/routes/components/Title.test.js +82 -0
- package/src/tests/routes/components/TitleModal.test.js +33 -0
- package/src/tests/routes/components/TitlePopover.test.js +23 -0
- package/src/tests/setup.js +53 -0
- package/src/typedefs.js +72 -0
- package/static/favicon.png +0 -0
- package/svelte.config.js +13 -0
- package/vite.config.js +61 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import EditorItem from '../../../../routes/components/Editorial/EditorItem.svelte'
|
|
5
|
+
import { injectLinksInDocument } from '$lib/linkInjection'
|
|
6
|
+
import { title } from '$lib/fakeData'
|
|
7
|
+
|
|
8
|
+
describe('EditorItem.svelte', () => {
|
|
9
|
+
const linkInjection = {
|
|
10
|
+
sid: '1',
|
|
11
|
+
title: 'an injection',
|
|
12
|
+
sentence: 'This is a sentence with an injection.',
|
|
13
|
+
playpilot_url: 'https://some-link.com/',
|
|
14
|
+
key: 'some-key',
|
|
15
|
+
title_details: title,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
it('Should render the given title', () => {
|
|
19
|
+
const { getByText, getByRole } = render(EditorItem, { linkInjection })
|
|
20
|
+
|
|
21
|
+
expect(getByText(linkInjection.title_details.title)).toBeTruthy()
|
|
22
|
+
expect(/** @type {HTMLImageElement} */ (getByRole('presentation')).src).toContain(linkInjection.title_details.standing_poster)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('Should highlight and unhighlight the matching link when component is hovered', async () => {
|
|
26
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
27
|
+
|
|
28
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
|
|
29
|
+
|
|
30
|
+
const { getAllByText, container } = render(EditorItem, { linkInjection })
|
|
31
|
+
|
|
32
|
+
await fireEvent.mouseEnter(/** @type {HTMLElement} */ (container.querySelector('.item')))
|
|
33
|
+
expect(getAllByText(linkInjection.title)[0].classList).toContain('injection-highlight')
|
|
34
|
+
|
|
35
|
+
await fireEvent.mouseLeave(/** @type {HTMLElement} */ (container.querySelector('.item')))
|
|
36
|
+
expect(getAllByText(linkInjection.title)[0].classList).not.toContain('injection-highlight')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('Should highlight multiple elements if multiple are present', async () => {
|
|
40
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
41
|
+
|
|
42
|
+
const linkInjectionWithAfterArticle = { ...linkInjection, in_text: true, after_article: true }
|
|
43
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjectionWithAfterArticle], () => null)
|
|
44
|
+
|
|
45
|
+
const { container } = render(EditorItem, { linkInjection })
|
|
46
|
+
|
|
47
|
+
await fireEvent.mouseEnter(/** @type {HTMLElement} */ (container.querySelector('.item')))
|
|
48
|
+
expect(document.querySelectorAll('.injection-highlight')).toHaveLength(2)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('Should scroll matching link into view when component is clicked', async () => {
|
|
52
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
53
|
+
|
|
54
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
|
|
55
|
+
|
|
56
|
+
const { container } = render(EditorItem, { linkInjection })
|
|
57
|
+
|
|
58
|
+
await fireEvent.click(/** @type {HTMLElement} */ (container.querySelector('.item')))
|
|
59
|
+
expect(window.HTMLElement.prototype.scrollIntoView).toHaveBeenCalled()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('Should not scroll matching link into view when component is clicked but there is no matching injection', async () => {
|
|
63
|
+
document.body.innerHTML = '<p>This has no matching injections.</p>'
|
|
64
|
+
|
|
65
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
|
|
66
|
+
|
|
67
|
+
const { container } = render(EditorItem, { linkInjection })
|
|
68
|
+
|
|
69
|
+
await fireEvent.click(/** @type {HTMLElement} */ (container.querySelector('.item')))
|
|
70
|
+
expect(window.HTMLElement.prototype.scrollIntoView).not.toHaveBeenCalled()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('Should not scroll matching link into view when component is clicked on button or input', async () => {
|
|
74
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
75
|
+
|
|
76
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
|
|
77
|
+
|
|
78
|
+
const { container } = render(EditorItem, { linkInjection })
|
|
79
|
+
|
|
80
|
+
await fireEvent.click(/** @type {HTMLElement} */ (container.querySelector('.expand')))
|
|
81
|
+
expect(window.HTMLElement.prototype.scrollIntoView).not.toHaveBeenCalled()
|
|
82
|
+
|
|
83
|
+
await fireEvent.click(/** @type {HTMLElement} */ (container.querySelector('input')))
|
|
84
|
+
expect(window.HTMLElement.prototype.scrollIntoView).not.toHaveBeenCalled()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('Should highlight element in editor when hovering element on page', async () => {
|
|
88
|
+
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
89
|
+
|
|
90
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
|
|
91
|
+
|
|
92
|
+
await new Promise(res => setTimeout(res))
|
|
93
|
+
|
|
94
|
+
const onhighlight = vi.fn()
|
|
95
|
+
const { container } = render(EditorItem, { linkInjection, onhighlight })
|
|
96
|
+
|
|
97
|
+
await fireEvent.mouseOver(/** @type {HTMLElement} */ (document.querySelector(`[data-playpilot-injection-key=${ linkInjection.key }]`)))
|
|
98
|
+
expect(container.querySelector('.highlighted')).toBeTruthy()
|
|
99
|
+
expect(onhighlight).toHaveBeenCalled()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('Should show expanded content after clicking expand', async () => {
|
|
103
|
+
const { container, getByLabelText } = render(EditorItem, { linkInjection })
|
|
104
|
+
|
|
105
|
+
expect(container.querySelector('.expanded')).not.toBeTruthy()
|
|
106
|
+
|
|
107
|
+
await fireEvent.click(getByLabelText('Expand'))
|
|
108
|
+
expect(container.querySelector('.expanded')).toBeTruthy()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('Should show different state when injection failed', () => {
|
|
112
|
+
const { getByText, queryByLabelText } = render(EditorItem, { linkInjection: { ...linkInjection, failed: true } })
|
|
113
|
+
|
|
114
|
+
expect(getByText('the link could not be injected', { exact: false })).toBeTruthy()
|
|
115
|
+
expect(queryByLabelText('Expand')).not.toBeTruthy()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('Should show length of title if length is given', () => {
|
|
119
|
+
const title_details = { ...linkInjection.title_details, length: 20 }
|
|
120
|
+
const { getByTestId, getByText } = render(EditorItem, { linkInjection: { ...linkInjection, title_details } })
|
|
121
|
+
|
|
122
|
+
expect(getByTestId('length')).toBeTruthy()
|
|
123
|
+
expect(getByText('20m')).toBeTruthy()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('Should not show length of title if no length is given', () => {
|
|
127
|
+
const title_details = { ...linkInjection.title_details, length: null }
|
|
128
|
+
const { queryByTestId } = render(EditorItem, { linkInjection: { ...linkInjection, title_details } })
|
|
129
|
+
|
|
130
|
+
expect(queryByTestId('length')).not.toBeTruthy()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('Should call given onremove function when remove button is clicked', async () => {
|
|
134
|
+
const onremove = vi.fn()
|
|
135
|
+
const { getByLabelText, getByText } = render(EditorItem, { linkInjection, onremove })
|
|
136
|
+
|
|
137
|
+
await fireEvent.click(getByLabelText('More options'))
|
|
138
|
+
await fireEvent.click(getByText('Remove'))
|
|
139
|
+
|
|
140
|
+
expect(onremove).toHaveBeenCalled()
|
|
141
|
+
})
|
|
142
|
+
})
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { fireEvent, render, waitFor } from '@testing-library/svelte'
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import TitleSearch from '../../../../routes/components/Editorial/ManualInjection.svelte'
|
|
5
|
+
import { searchTitles } from '$lib/search'
|
|
6
|
+
import { title } from '$lib/fakeData'
|
|
7
|
+
|
|
8
|
+
vi.mock('$lib/search', () => ({
|
|
9
|
+
searchTitles: vi.fn(),
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
describe('TitleSearch.svelte', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.resetAllMocks()
|
|
15
|
+
|
|
16
|
+
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
|
+
}))
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('Should input selected text in selected text input and search input', async () => {
|
|
33
|
+
const { getByLabelText } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave: () => null })
|
|
34
|
+
|
|
35
|
+
await fireEvent.click(window)
|
|
36
|
+
|
|
37
|
+
expect(window.getSelection).toHaveBeenCalled()
|
|
38
|
+
expect(/** @type {HTMLFormElement} */ (getByLabelText('Select text on the page')).value).toBe('text')
|
|
39
|
+
expect(/** @type {HTMLFormElement} */ (getByLabelText('Search...')).value).toBe('text')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('Should disable save button by default', async () => {
|
|
43
|
+
const { getByText } = render(TitleSearch, { htmlString: '', onsave: () => null })
|
|
44
|
+
|
|
45
|
+
expect(getByText('Add playlink').hasAttribute('disabled')).toBeTruthy()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('Should enable save button after selecting text and selecting title from TitleSearch', async () => {
|
|
49
|
+
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
50
|
+
|
|
51
|
+
const { getByText } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave: () => null })
|
|
52
|
+
|
|
53
|
+
await fireEvent.click(window)
|
|
54
|
+
await waitFor(() => getByText(title.title))
|
|
55
|
+
await fireEvent.click(getByText(title.title))
|
|
56
|
+
|
|
57
|
+
expect(getByText('Add playlink').hasAttribute('disabled')).not.toBeTruthy()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('Should call given onsave function when clicked', async () => {
|
|
61
|
+
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
62
|
+
|
|
63
|
+
const onsave = vi.fn()
|
|
64
|
+
const { getByText } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave })
|
|
65
|
+
|
|
66
|
+
await fireEvent.click(window)
|
|
67
|
+
await fireEvent.click(getByText(title.title))
|
|
68
|
+
await fireEvent.click(getByText('Add playlink'))
|
|
69
|
+
|
|
70
|
+
expect(onsave).toHaveBeenCalledWith({
|
|
71
|
+
sid: title.sid,
|
|
72
|
+
title: 'text',
|
|
73
|
+
sentence: 'Some text in a sentence',
|
|
74
|
+
playpilot_url: `https://www.playpilot.com/show/${title.slug}`,
|
|
75
|
+
key: expect.any(String),
|
|
76
|
+
title_details: title,
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('Should select content outside of element if content is too short', async () => {
|
|
81
|
+
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
82
|
+
|
|
83
|
+
document.body.innerHTML = '<div>Some <strong>text</strong> in a sentence</div>'
|
|
84
|
+
|
|
85
|
+
const container = document.querySelector('strong')
|
|
86
|
+
|
|
87
|
+
// @ts-ignore
|
|
88
|
+
window.getSelection = vi.fn(() => ({
|
|
89
|
+
toString: () => 'text',
|
|
90
|
+
getRangeAt: () => ({
|
|
91
|
+
commonAncestorContainer: container,
|
|
92
|
+
startContainer: container,
|
|
93
|
+
startOffset: 0,
|
|
94
|
+
endOffset: 0,
|
|
95
|
+
}),
|
|
96
|
+
}))
|
|
97
|
+
|
|
98
|
+
const onsave = vi.fn()
|
|
99
|
+
const { getByText } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave })
|
|
100
|
+
|
|
101
|
+
await fireEvent.click(window)
|
|
102
|
+
await fireEvent.click(getByText(title.title))
|
|
103
|
+
await fireEvent.click(getByText('Add playlink'))
|
|
104
|
+
|
|
105
|
+
expect(onsave).toHaveBeenCalledWith({
|
|
106
|
+
sid: title.sid,
|
|
107
|
+
title: 'text',
|
|
108
|
+
sentence: 'Some text in a sentence',
|
|
109
|
+
playpilot_url: `https://www.playpilot.com/show/${title.slug}`,
|
|
110
|
+
key: expect.any(String),
|
|
111
|
+
title_details: title,
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import PlaylinkTypeSelect from '../../../../routes/components/Editorial/PlaylinkTypeSelect.svelte'
|
|
5
|
+
|
|
6
|
+
describe('PlaylinkTypeSelect.svelte', () => {
|
|
7
|
+
/** @type {LinkInjection} */
|
|
8
|
+
const linkInjection = {
|
|
9
|
+
sid: '1',
|
|
10
|
+
title: 'test',
|
|
11
|
+
sentence: 'Some sentence',
|
|
12
|
+
playpilot_url: 'https://playpilot.com/movie/example-3/',
|
|
13
|
+
key: 'some-key',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
delete linkInjection.after_article
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('Should render options with default given value', () => {
|
|
21
|
+
const { getByText } = render(PlaylinkTypeSelect, { linkInjection })
|
|
22
|
+
|
|
23
|
+
expect(getByText('In-text Playlink')).toBeTruthy()
|
|
24
|
+
expect(getByText('Bottom Playlink')).toBeTruthy()
|
|
25
|
+
|
|
26
|
+
expect(/** @type {HTMLButtonElement} */ (getByText('In-text Playlink').closest('button')).classList).toContain('active')
|
|
27
|
+
expect(/** @type {HTMLButtonElement} */ (getByText('Bottom Playlink').closest('button')).classList).not.toContain('active')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('Should change value and active state on click of each switch', async () => {
|
|
31
|
+
const { getByText } = render(PlaylinkTypeSelect, { linkInjection })
|
|
32
|
+
|
|
33
|
+
await fireEvent.click(getByText('In-text Playlink'))
|
|
34
|
+
expect(/** @type {HTMLButtonElement} */ (getByText('In-text Playlink').closest('button')).classList).not.toContain('active') // Starts as active
|
|
35
|
+
|
|
36
|
+
await fireEvent.click(getByText('Bottom Playlink'))
|
|
37
|
+
expect(/** @type {HTMLButtonElement} */ (getByText('Bottom Playlink').closest('button')).classList).toContain('active') // Starts as inactive
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('Should show after article style options after selecting bottom playlinks', async () => {
|
|
41
|
+
const { queryByText, getByText } = render(PlaylinkTypeSelect, { linkInjection })
|
|
42
|
+
|
|
43
|
+
expect(queryByText('Bottom playlinks style')).not.toBeTruthy()
|
|
44
|
+
|
|
45
|
+
await fireEvent.click(getByText('Bottom Playlink'))
|
|
46
|
+
expect(getByText('Bottom playlinks style')).toBeTruthy()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('Should toggle after article style options on click', async () => {
|
|
50
|
+
const { getByText } = render(PlaylinkTypeSelect, { linkInjection })
|
|
51
|
+
|
|
52
|
+
await fireEvent.click(getByText('Bottom Playlink'))
|
|
53
|
+
|
|
54
|
+
expect(getByText('Playlinks sentence').classList).toContain('active')
|
|
55
|
+
expect(getByText('Modal button').classList).not.toContain('active')
|
|
56
|
+
expect(/** @type {HTMLElement} */(document.querySelector('.active-marker')).classList).not.toContain('right')
|
|
57
|
+
|
|
58
|
+
await fireEvent.click(getByText('Modal button'))
|
|
59
|
+
expect(getByText('Playlinks sentence').classList).not.toContain('active')
|
|
60
|
+
expect(getByText('Modal button').classList).toContain('active')
|
|
61
|
+
expect(/** @type {HTMLElement} */(document.querySelector('.active-marker')).classList).toContain('right')
|
|
62
|
+
})
|
|
63
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import TitleSearch from '../../../../../routes/components/Editorial/Search/TitleSearch.svelte'
|
|
5
|
+
import { searchTitles } from '$lib/search'
|
|
6
|
+
import { title } from '$lib/fakeData'
|
|
7
|
+
|
|
8
|
+
vi.mock('$lib/search', () => ({
|
|
9
|
+
searchTitles: vi.fn(),
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
describe('TitleSearch.svelte', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.resetAllMocks()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('Should search for titles when inputting text', async () => {
|
|
18
|
+
const { getByRole } = render(TitleSearch)
|
|
19
|
+
|
|
20
|
+
await fireEvent.input(getByRole('textbox'), { target: { value: 'test' } })
|
|
21
|
+
|
|
22
|
+
expect(searchTitles).toHaveBeenCalled()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('Should show empty state when no results are returned', async () => {
|
|
26
|
+
vi.mocked(searchTitles).mockResolvedValueOnce([])
|
|
27
|
+
|
|
28
|
+
const { getByRole, getByText } = render(TitleSearch)
|
|
29
|
+
|
|
30
|
+
await fireEvent.input(getByRole('textbox'), { target: { value: 'test' } })
|
|
31
|
+
|
|
32
|
+
expect(getByText('No results found')).toBeTruthy()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('Should show results when given', async () => {
|
|
36
|
+
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
37
|
+
|
|
38
|
+
const { getByRole, getByText } = render(TitleSearch)
|
|
39
|
+
|
|
40
|
+
await fireEvent.input(getByRole('textbox'), { target: { value: 'test' } })
|
|
41
|
+
|
|
42
|
+
expect(getByText(title.title)).toBeTruthy()
|
|
43
|
+
expect(getByText(title.type)).toBeTruthy()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('Should fire the given onselect function and close the results when a result is clicked', async () => {
|
|
47
|
+
vi.mocked(searchTitles).mockResolvedValueOnce([title])
|
|
48
|
+
|
|
49
|
+
const onselect = vi.fn()
|
|
50
|
+
const { getByRole, queryByRole } = render(TitleSearch, { onselect })
|
|
51
|
+
|
|
52
|
+
await fireEvent.input(getByRole('textbox'), { target: { value: 'test' } })
|
|
53
|
+
await fireEvent.click(getByRole('button'))
|
|
54
|
+
|
|
55
|
+
expect(onselect).toHaveBeenCalledWith(title)
|
|
56
|
+
expect(queryByRole('button')).not.toBeTruthy()
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import Switch from '../../../../routes/components/Editorial/Switch.svelte'
|
|
5
|
+
import { createRawSnippet } from 'svelte'
|
|
6
|
+
|
|
7
|
+
describe('Switch.svelte', () => {
|
|
8
|
+
const children = createRawSnippet(() => ({ render: () => '<div>Some snippet</div>' }))
|
|
9
|
+
|
|
10
|
+
it('Should render the given snippet', () => {
|
|
11
|
+
const { getByText } = render(Switch, { label: 'Some label', children })
|
|
12
|
+
|
|
13
|
+
expect(getByText('Some snippet')).toBeTruthy()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('Should be inactive by default', () => {
|
|
17
|
+
const { getByRole } = render(Switch, { children })
|
|
18
|
+
|
|
19
|
+
expect(getByRole('button').classList).not.toContain('active')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('Should be active when prop is given', () => {
|
|
23
|
+
const { getByRole } = render(Switch, { active: true, children })
|
|
24
|
+
|
|
25
|
+
expect(getByRole('button').classList).toContain('active')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('Should not have fullwidth class by default', () => {
|
|
29
|
+
const { getByRole } = render(Switch, { children })
|
|
30
|
+
|
|
31
|
+
expect(getByRole('button').classList).not.toContain('fullwidth')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('Should have fullwidth class when prop is given', () => {
|
|
35
|
+
const { getByRole } = render(Switch, { fullwidth: true, children })
|
|
36
|
+
|
|
37
|
+
expect(getByRole('button').classList).toContain('fullwidth')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('Should switch the active state when clicked', async () => {
|
|
41
|
+
const { getByRole } = render(Switch, { children })
|
|
42
|
+
|
|
43
|
+
await fireEvent.click(getByRole('button'))
|
|
44
|
+
expect(getByRole('button').classList).toContain('active')
|
|
45
|
+
|
|
46
|
+
await fireEvent.click(getByRole('button'))
|
|
47
|
+
expect(getByRole('button').classList).not.toContain('active')
|
|
48
|
+
|
|
49
|
+
await fireEvent.click(getByRole('button'))
|
|
50
|
+
expect(getByRole('button').classList).toContain('active')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('Should fire given onclick function when clicked', async () => {
|
|
54
|
+
const onclick = vi.fn()
|
|
55
|
+
const { getByRole } = render(Switch, { onclick, children })
|
|
56
|
+
|
|
57
|
+
await fireEvent.click(getByRole('button'))
|
|
58
|
+
expect(onclick).toHaveBeenCalled()
|
|
59
|
+
})
|
|
60
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { render } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import LinkInput from '../../../../routes/components/Editorial/TextInput.svelte'
|
|
5
|
+
|
|
6
|
+
describe('TextInput.svelte', () => {
|
|
7
|
+
it('Should render the given label', () => {
|
|
8
|
+
const { getByLabelText } = render(LinkInput, { label: 'Some label' })
|
|
9
|
+
|
|
10
|
+
expect(getByLabelText('Some label')).toBeTruthy()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('Should use the given value', () => {
|
|
14
|
+
const { getByRole } = render(LinkInput, { value: 'Some value' })
|
|
15
|
+
|
|
16
|
+
expect(/** @type {HTMLFormElement} */ (getByRole('textbox')).value).toBe('Some value')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('Should not be labeled as readonly by default', () => {
|
|
20
|
+
const { getByRole } = render(LinkInput, { value: 'Some value' })
|
|
21
|
+
|
|
22
|
+
expect(/** @type {HTMLFormElement} */ (getByRole('textbox')).hasAttribute('readonly')).not.toBeTruthy()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('Should be labeled as readonly when given', () => {
|
|
26
|
+
const { getByRole } = render(LinkInput, { value: 'Some value', readonly: true })
|
|
27
|
+
|
|
28
|
+
expect(/** @type {HTMLFormElement} */ (getByRole('textbox')).hasAttribute('readonly')).toBeTruthy()
|
|
29
|
+
})
|
|
30
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { render, fireEvent } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import Genres from '../../../routes/components/Genres.svelte'
|
|
5
|
+
import genreData from '$lib/genres.json'
|
|
6
|
+
|
|
7
|
+
describe('Genres.svelte', () => {
|
|
8
|
+
it('Should match genre slug to genres in genres.json', () => {
|
|
9
|
+
const { getByText } = render(Genres, { genres: ['101'] })
|
|
10
|
+
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
expect(getByText(genreData.find(g => g.slug === '101').name)).toBeTruthy()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('Should render only the first given genre by default', () => {
|
|
16
|
+
const { getByText, queryByText } = render(Genres, { genres: ['101', '102'] })
|
|
17
|
+
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
expect(getByText(genreData.find(g => g.slug === '101').name)).toBeTruthy()
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
expect(queryByText(genreData.find(g => g.slug === '102').name)).not.toBeTruthy()
|
|
22
|
+
expect(getByText('+1')).toBeTruthy()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('Should expand and render all', async () => {
|
|
26
|
+
const { getByText, queryByText } = render(Genres, { genres: ['101', '102'] })
|
|
27
|
+
|
|
28
|
+
await fireEvent.click(getByText('+1'))
|
|
29
|
+
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
expect(getByText(genreData.find(g => g.slug === '101').name)).toBeTruthy()
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
expect(getByText(genreData.find(g => g.slug === '102').name)).toBeTruthy()
|
|
34
|
+
|
|
35
|
+
expect(queryByText('+1')).not.toBeTruthy()
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { render, fireEvent } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import Modal from '../../../routes/components/Modal.svelte'
|
|
5
|
+
|
|
6
|
+
describe('Modal.svelte', () => {
|
|
7
|
+
it('Should fire given onclose function when backdrop is clicked', async () => {
|
|
8
|
+
const onclose = vi.fn()
|
|
9
|
+
const { container } = render(Modal, { children: () => null, onclose })
|
|
10
|
+
|
|
11
|
+
await fireEvent.click(/** @type {Node} */ (container.querySelector('.backdrop')))
|
|
12
|
+
|
|
13
|
+
expect(onclose).toHaveBeenCalled()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('Should fire given onclose function when close button is clicked', async () => {
|
|
17
|
+
const onclose = vi.fn()
|
|
18
|
+
const { container } = render(Modal, { children: () => null, onclose })
|
|
19
|
+
|
|
20
|
+
await fireEvent.click(/** @type {Node} */ (container.querySelector('button')))
|
|
21
|
+
|
|
22
|
+
expect(onclose).toHaveBeenCalled()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('Should not fire given onclose function when dialog is clicked', async () => {
|
|
26
|
+
const onclose = vi.fn()
|
|
27
|
+
const { container } = render(Modal, { children: () => null, onclose })
|
|
28
|
+
|
|
29
|
+
await fireEvent.click(/** @type {Node} */ (container.querySelector('.dialog')))
|
|
30
|
+
|
|
31
|
+
expect(onclose).not.toHaveBeenCalled()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('Should fire given onclose function when escape key is pressed', async () => {
|
|
35
|
+
const onclose = vi.fn()
|
|
36
|
+
render(Modal, { children: () => null, onclose })
|
|
37
|
+
|
|
38
|
+
fireEvent.keyDown(window, { key: 'Escape' })
|
|
39
|
+
|
|
40
|
+
expect(onclose).toHaveBeenCalled()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('Should not fire given onclose function when key other than escape key is pressed', async () => {
|
|
44
|
+
const onclose = vi.fn()
|
|
45
|
+
render(Modal, { children: () => null, onclose })
|
|
46
|
+
|
|
47
|
+
fireEvent.keyDown(window, { key: 'a' })
|
|
48
|
+
fireEvent.keyDown(window, { key: 'Enter' })
|
|
49
|
+
fireEvent.keyDown(window, { key: 'Space' })
|
|
50
|
+
|
|
51
|
+
expect(onclose).not.toHaveBeenCalled()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('Should fire given onscroll function when dialog is scrolled', async () => {
|
|
55
|
+
const onscroll = vi.fn()
|
|
56
|
+
const { getByRole } = render(Modal, { children: () => null, onscroll })
|
|
57
|
+
|
|
58
|
+
await fireEvent.scroll(getByRole('dialog'))
|
|
59
|
+
|
|
60
|
+
expect(onscroll).toHaveBeenCalled()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('Should set body overflow on mount', () => {
|
|
64
|
+
render(Modal, { children: () => null })
|
|
65
|
+
|
|
66
|
+
expect(document.body.style.overflowY).toBe('hidden')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('Should remove body overflow when component is unmounted', () => {
|
|
70
|
+
const { unmount } = render(Modal, { children: () => null })
|
|
71
|
+
unmount()
|
|
72
|
+
|
|
73
|
+
expect(document.body.style.overflowY).toBe('')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('Should use body overflow as it was before the component was mounted when component is unmounted', () => {
|
|
77
|
+
document.body.style.overflowY = 'some-value'
|
|
78
|
+
|
|
79
|
+
const { unmount } = render(Modal, { children: () => null })
|
|
80
|
+
unmount()
|
|
81
|
+
|
|
82
|
+
expect(document.body.style.overflowY).toBe('some-value')
|
|
83
|
+
})
|
|
84
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { render } from '@testing-library/svelte'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import Participants from '../../../routes/components/Participants.svelte'
|
|
5
|
+
|
|
6
|
+
describe('Participants.svelte', () => {
|
|
7
|
+
it('Should render each given participant', () => {
|
|
8
|
+
const participants = [
|
|
9
|
+
{ name: 'Some name', character: 'Some character' },
|
|
10
|
+
{ name: 'Some second name', character: 'Second second character' },
|
|
11
|
+
]
|
|
12
|
+
const { getByText } = render(Participants, { participants })
|
|
13
|
+
|
|
14
|
+
expect(getByText('Some name')).toBeTruthy()
|
|
15
|
+
expect(getByText('Some second name')).toBeTruthy()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('Should limit the character name if it is too long', () => {
|
|
19
|
+
const participants = [{ name: 'Some name', character: 'Some longer character name' }]
|
|
20
|
+
const { getByText, queryByText } = render(Participants, { participants })
|
|
21
|
+
|
|
22
|
+
expect(queryByText('Some longer character name')).not.toBeTruthy()
|
|
23
|
+
expect(getByText('Some longer characte...')).toBeTruthy()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('Should handle not being given any participants', () => {
|
|
27
|
+
/** @type {Participant[]} */
|
|
28
|
+
const participants = []
|
|
29
|
+
const { container } = render(Participants, { participants })
|
|
30
|
+
|
|
31
|
+
expect(container.querySelectorAll('.participant').length).toBe(0)
|
|
32
|
+
})
|
|
33
|
+
})
|