@playpilot/tpi 1.4.3 → 2.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.
@@ -1,4 +1,4 @@
1
- import { render } from '@testing-library/svelte'
1
+ import { 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'
@@ -43,6 +43,21 @@ describe('$routes/+page.svelte', () => {
43
43
  expect(pollLinkInjections).toHaveBeenCalled()
44
44
  })
45
45
 
46
+ it('Should call pollLinkInjections again if ai_running is true while in editorial mode', async () => {
47
+ vi.mocked(authorize).mockResolvedValueOnce(true)
48
+ // @ts-ignore
49
+ window.PlayPilotLinkInjections = { editorial_token: 'some-token' }
50
+
51
+ vi.mocked(pollLinkInjections).mockResolvedValueOnce({ ai_running: true, injections_ready: true, page_updated: false, automation_enabled: true, injections_enabled: true, ai_injections: [], link_injections: [] })
52
+
53
+ render(page)
54
+
55
+ await waitFor(() => {
56
+ expect(pollLinkInjections).toHaveBeenCalled()
57
+ expect(pollLinkInjections).toHaveBeenCalledTimes(2)
58
+ })
59
+ })
60
+
46
61
  it('Should use the given selector when present', () => {
47
62
  document.body.innerHTML = '<div class="some-element"><p>Here</p></div> <p>Not here</p>'
48
63
 
@@ -0,0 +1,58 @@
1
+ import { fireEvent, render, waitFor } from '@testing-library/svelte'
2
+ import { describe, expect, it, vi } from 'vitest'
3
+
4
+ import AIIndicator from '../../../../routes/components/Editorial/AIIndicator.svelte'
5
+ import { generateInjection } from '../../../helpers'
6
+
7
+ describe('AIIndicator.svelte', () => {
8
+ it('Should should running state by default', () => {
9
+ const { getByText } = render(AIIndicator, { onadd: () => null })
10
+
11
+ expect(getByText('AI links are currently processing.', { exact: false })).toBeTruthy()
12
+ })
13
+
14
+ it('Should show completed state with text depending on length of given array', async () => {
15
+ const { getByText, component } = render(AIIndicator, { onadd: () => null })
16
+
17
+ component.notifyUserOfNewState([generateInjection('Some sentence', 'Some title')])
18
+ await waitFor(() => {
19
+ expect(getByText('AI links are ready.', { exact: false })).toBeTruthy()
20
+ expect(getByText('1 New link was found.')).toBeTruthy()
21
+ })
22
+
23
+ component.notifyUserOfNewState([generateInjection('Some sentence', 'Some title'), generateInjection('Some sentence', 'Some title')])
24
+ await waitFor(() => {
25
+ expect(getByText('AI links are ready.', { exact: false })).toBeTruthy()
26
+ expect(getByText('2 New links were found.')).toBeTruthy()
27
+ })
28
+
29
+ component.notifyUserOfNewState([])
30
+ await waitFor(() => {
31
+ expect(getByText('AI links finished running, but no new links were found.')).toBeTruthy()
32
+ })
33
+ })
34
+
35
+ it('Should close element when dismiss button is clicked', async () => {
36
+ const { getByText, component, container } = render(AIIndicator, { onadd: () => null })
37
+
38
+ component.notifyUserOfNewState([])
39
+ await waitFor(() => getByText('Dismiss'))
40
+
41
+ await fireEvent.click(getByText('Dismiss'))
42
+ expect(container.querySelector('.ai-indicator')).not.toBeTruthy()
43
+ })
44
+
45
+ it('Should close element and call onadd when Add AI links button is clicked', async () => {
46
+ const onadd = vi.fn()
47
+ const { getByText, component, container } = render(AIIndicator, { onadd })
48
+
49
+ const injection = generateInjection('Some sentence', 'Some title')
50
+ component.notifyUserOfNewState([injection])
51
+ await waitFor(() => getByText('Add AI links'))
52
+
53
+ await fireEvent.click(getByText('Add AI links'))
54
+
55
+ expect(onadd).toHaveBeenCalledWith([injection])
56
+ expect(container.querySelector('.ai-indicator')).not.toBeTruthy()
57
+ })
58
+ })
@@ -1,29 +1,19 @@
1
- import { render } from '@testing-library/svelte'
1
+ import { fireEvent, render } from '@testing-library/svelte'
2
2
  import { describe, expect, it, vi } from 'vitest'
3
3
 
4
4
  import Editor from '../../../../routes/components/Editorial/Editor.svelte'
5
5
  import { title } from '$lib/fakeData'
6
+ import { generateInjection } from '../../../helpers'
6
7
 
7
8
  vi.mock('$lib/api', () => ({
8
9
  saveLinkInjections: vi.fn(),
9
10
  }))
10
11
 
11
12
  describe('Editor.svelte', () => {
12
- const linkInjections = [{
13
- sid: '123',
14
- key: '1',
15
- title: 'an injection',
16
- sentence: 'This is a sentence with an injection.',
17
- playpilot_url: 'https://some-link.com/',
18
- title_details: title,
19
- }, {
20
- sid: '456',
21
- key: '2',
22
- title: 'a sentence',
23
- sentence: 'This is a sentence with an injection.',
24
- playpilot_url: 'https://some-link.com/',
25
- title_details: title,
26
- }]
13
+ const linkInjections = [
14
+ generateInjection('This is a sentence with an injection.', 'an injection'),
15
+ generateInjection('This is a sentence with an injection.', 'a sentence'),
16
+ ]
27
17
 
28
18
  it('Should show the number of injections', () => {
29
19
  const { getByText } = render(Editor, { linkInjections })
@@ -56,15 +46,23 @@ describe('Editor.svelte', () => {
56
46
  expect(queryByText('Save links')).not.toBeTruthy()
57
47
  })
58
48
 
59
- it('Should show save button if injections are present', async () => {
49
+ it('Should not show save button if injections are present but no change is made', async () => {
60
50
  const { queryByText } = render(Editor, { linkInjections, loading: false })
61
51
 
62
- expect(queryByText('Save links')).toBeTruthy()
52
+ expect(queryByText('Save links')).not.toBeTruthy()
53
+ })
54
+
55
+ it('Should show save button if injections are present and a change has been made', async () => {
56
+ const { getByText, getAllByText } = render(Editor, { linkInjections, loading: false })
57
+
58
+ await fireEvent.click(getAllByText('Visible')[0])
59
+
60
+ expect(getByText('Save links')).toBeTruthy()
63
61
  })
64
62
 
65
63
  it('Should show empty state if no links are found', async () => {
66
- const { queryByText } = render(Editor, { linkInjections: [], loading: false })
64
+ const { getByText } = render(Editor, { linkInjections: [], loading: false })
67
65
 
68
- expect(queryByText('No links available', { exact: false })).toBeTruthy()
66
+ expect(getByText('No links available', { exact: false })).toBeTruthy()
69
67
  })
70
68
  })
@@ -3,34 +3,29 @@ import { describe, expect, it, vi } from 'vitest'
3
3
 
4
4
  import EditorItem from '../../../../routes/components/Editorial/EditorItem.svelte'
5
5
  import { injectLinksInDocument } from '$lib/linkInjection'
6
- import { title } from '$lib/fakeData'
6
+ import { linkInjections, title } from '$lib/fakeData'
7
7
  import { track } from '$lib/tracking'
8
+ import { generateInjection } from '../../../helpers'
8
9
 
9
10
  vi.mock('$lib/tracking', () => ({
10
11
  track: vi.fn(),
11
12
  }))
12
13
 
13
14
  describe('EditorItem.svelte', () => {
14
- const linkInjection = {
15
- sid: '1',
16
- title: 'an injection',
17
- sentence: 'This is a sentence with an injection.',
18
- playpilot_url: 'https://some-link.com/',
19
- key: 'some-key',
20
- title_details: title,
21
- }
15
+ const linkInjection = generateInjection('This is a sentence with an injection.', 'an injection')
16
+ const linkInjections = { aiInjections: [linkInjection], manualInjections: [] }
22
17
 
23
18
  it('Should render the given title', () => {
24
19
  const { getByText, getByRole } = render(EditorItem, { linkInjection })
25
20
 
26
- expect(getByText(linkInjection.title_details.title)).toBeTruthy()
27
- expect(/** @type {HTMLImageElement} */ (getByRole('presentation')).src).toContain(linkInjection.title_details.standing_poster)
21
+ expect(getByText(linkInjection.title_details?.title || '')).toBeTruthy()
22
+ expect(/** @type {HTMLImageElement} */ (getByRole('presentation')).src).toContain(linkInjection.title_details?.standing_poster)
28
23
  })
29
24
 
30
25
  it('Should highlight and unhighlight the matching link when component is hovered', async () => {
31
26
  document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
32
27
 
33
- injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
28
+ injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
34
29
 
35
30
  const { getAllByText, container } = render(EditorItem, { linkInjection })
36
31
 
@@ -45,7 +40,7 @@ describe('EditorItem.svelte', () => {
45
40
  document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
46
41
 
47
42
  const linkInjectionWithAfterArticle = { ...linkInjection, in_text: true, after_article: true }
48
- injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjectionWithAfterArticle], () => null)
43
+ injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, { aiInjections: [linkInjectionWithAfterArticle], manualInjections: [] })
49
44
 
50
45
  const { container } = render(EditorItem, { linkInjection })
51
46
 
@@ -56,7 +51,7 @@ describe('EditorItem.svelte', () => {
56
51
  it('Should scroll matching link into view when component is clicked', async () => {
57
52
  document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
58
53
 
59
- injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
54
+ injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
60
55
 
61
56
  const { container } = render(EditorItem, { linkInjection })
62
57
 
@@ -67,7 +62,7 @@ describe('EditorItem.svelte', () => {
67
62
  it('Should not scroll matching link into view when component is clicked but there is no matching injection', async () => {
68
63
  document.body.innerHTML = '<p>This has no matching injections.</p>'
69
64
 
70
- injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
65
+ injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
71
66
 
72
67
  const { container } = render(EditorItem, { linkInjection })
73
68
 
@@ -78,7 +73,7 @@ describe('EditorItem.svelte', () => {
78
73
  it('Should not scroll matching link into view when component is clicked on button or input', async () => {
79
74
  document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
80
75
 
81
- injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
76
+ injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
82
77
 
83
78
  const { container } = render(EditorItem, { linkInjection })
84
79
 
@@ -92,7 +87,7 @@ describe('EditorItem.svelte', () => {
92
87
  it('Should highlight element in editor when hovering element on page', async () => {
93
88
  document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
94
89
 
95
- injectLinksInDocument(Array.from(document.querySelectorAll('p')), [linkInjection], () => null)
90
+ injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
96
91
 
97
92
  await new Promise(res => setTimeout(res))
98
93
 
@@ -1,7 +1,7 @@
1
1
  import { fireEvent, render, waitFor } from '@testing-library/svelte'
2
2
  import { beforeEach, describe, expect, it, vi } from 'vitest'
3
3
 
4
- import TitleSearch from '../../../../routes/components/Editorial/ManualInjection.svelte'
4
+ import ManualInjection from '../../../../routes/components/Editorial/ManualInjection.svelte'
5
5
  import { searchTitles } from '$lib/search'
6
6
  import { title } from '$lib/fakeData'
7
7
 
@@ -9,7 +9,7 @@ vi.mock('$lib/search', () => ({
9
9
  searchTitles: vi.fn(),
10
10
  }))
11
11
 
12
- describe('TitleSearch.svelte', () => {
12
+ describe('ManualInjection.svelte', () => {
13
13
  beforeEach(() => {
14
14
  vi.resetAllMocks()
15
15
 
@@ -31,7 +31,7 @@ describe('TitleSearch.svelte', () => {
31
31
  })
32
32
 
33
33
  it('Should input selected text in selected text input and search input', async () => {
34
- const { getByLabelText } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave: () => null })
34
+ const { getByLabelText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave: () => null })
35
35
 
36
36
  await fireEvent.click(window)
37
37
 
@@ -41,15 +41,15 @@ describe('TitleSearch.svelte', () => {
41
41
  })
42
42
 
43
43
  it('Should disable save button by default', async () => {
44
- const { getByText } = render(TitleSearch, { htmlString: '', onsave: () => null })
44
+ const { getByText } = render(ManualInjection, { htmlString: '', onsave: () => null })
45
45
 
46
46
  expect(getByText('Add playlink').hasAttribute('disabled')).toBeTruthy()
47
47
  })
48
48
 
49
- it('Should enable save button after selecting text and selecting title from TitleSearch', async () => {
49
+ it('Should enable save button after selecting text and selecting title from ManualInjection', async () => {
50
50
  vi.mocked(searchTitles).mockResolvedValueOnce([title])
51
51
 
52
- const { getByText } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave: () => null })
52
+ const { getByText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave: () => null })
53
53
 
54
54
  await fireEvent.click(window)
55
55
  await waitFor(() => getByText(title.title))
@@ -62,7 +62,7 @@ describe('TitleSearch.svelte', () => {
62
62
  vi.mocked(searchTitles).mockResolvedValueOnce([title])
63
63
 
64
64
  const onsave = vi.fn()
65
- const { getByText } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave })
65
+ const { getByText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave })
66
66
 
67
67
  await fireEvent.click(window)
68
68
  await fireEvent.click(getByText(title.title))
@@ -75,6 +75,7 @@ describe('TitleSearch.svelte', () => {
75
75
  playpilot_url: `https://www.playpilot.com/show/${title.slug}`,
76
76
  key: expect.any(String),
77
77
  title_details: title,
78
+ manual: true,
78
79
  })
79
80
  })
80
81
 
@@ -98,7 +99,7 @@ describe('TitleSearch.svelte', () => {
98
99
  }))
99
100
 
100
101
  const onsave = vi.fn()
101
- const { getByText } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave })
102
+ const { getByText } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave })
102
103
 
103
104
  await fireEvent.click(window)
104
105
  await fireEvent.click(getByText(title.title))
@@ -111,6 +112,7 @@ describe('TitleSearch.svelte', () => {
111
112
  playpilot_url: `https://www.playpilot.com/show/${title.slug}`,
112
113
  key: expect.any(String),
113
114
  title_details: title,
115
+ manual: true,
114
116
  })
115
117
  })
116
118
 
@@ -122,7 +124,7 @@ describe('TitleSearch.svelte', () => {
122
124
  let container = document.querySelector('div')
123
125
 
124
126
  const onsave = vi.fn()
125
- const { unmount } = render(TitleSearch, { htmlString: document.body.innerHTML, onsave })
127
+ const { unmount } = render(ManualInjection, { htmlString: document.body.innerHTML, onsave })
126
128
 
127
129
  // @ts-ignore
128
130
  window.getSelection = vi.fn(() => ({
@@ -156,7 +158,7 @@ describe('TitleSearch.svelte', () => {
156
158
  anchorNode: container,
157
159
  }))
158
160
 
159
- ;(render(TitleSearch, { htmlString: document.body.innerHTML, onsave }))
161
+ ;(render(ManualInjection, { htmlString: document.body.innerHTML, onsave }))
160
162
 
161
163
  await fireEvent.click(window)
162
164
 
package/src/typedefs.js CHANGED
@@ -43,17 +43,20 @@
43
43
 
44
44
  /**
45
45
  * @typedef {{
46
- * sid: string,
47
- * title: string,
48
- * sentence: string,
49
- * playpilot_url: string,
50
- * key: string,
51
- * title_details?: TitleData,
52
- * inactive?: boolean,
53
- * failed?: boolean,
54
- * in_text?: boolean,
55
- * after_article?: boolean
56
- * after_article_style?: 'modal_button' | 'playlinks' | null
46
+ * sid: string,
47
+ * title: string,
48
+ * sentence: string,
49
+ * playpilot_url: string,
50
+ * key: string,
51
+ * title_details?: TitleData,
52
+ * inactive?: boolean,
53
+ * failed?: boolean,
54
+ * in_text?: boolean,
55
+ * after_article?: boolean,
56
+ * after_article_style?: 'modal_button' | 'playlinks' | null,
57
+ * manual?: boolean,
58
+ * removed?: boolean,
59
+ * duplicate?: boolean,
57
60
  * }} LinkInjection
58
61
  */
59
62
 
@@ -64,7 +67,19 @@
64
67
  */
65
68
 
66
69
  /**
67
- * @typedef {{ injections_ready: boolean, injections_enabled: boolean, link_injections: LinkInjection[] | null, automation_enabled: boolean }} LinkInjectionResponse
70
+ * @typedef {{
71
+ * injections_ready: boolean,
72
+ * page_updated: boolean,
73
+ * automation_enabled: boolean,
74
+ * injections_enabled: boolean,
75
+ * ai_running: boolean,
76
+ * ai_injections: LinkInjection[] | null,
77
+ * link_injections: LinkInjection[] | null,
78
+ * }} LinkInjectionResponse
79
+ */
80
+
81
+ /**
82
+ * @typedef {{ aiInjections: LinkInjection[], manualInjections: LinkInjection[] }} LinkInjectionTypes
68
83
  */
69
84
 
70
85
  /**