@playpilot/tpi 3.3.3 → 3.4.0-beta.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.
- package/dist/link-injections.js +8 -8
- package/package.json +1 -1
- package/src/lib/api.ts +6 -9
- package/src/lib/hash.ts +4 -0
- package/src/lib/linkInjection.ts +15 -40
- package/src/lib/localization.ts +7 -7
- package/src/lib/scss/_mixins.scss +0 -13
- package/src/lib/scss/global.scss +0 -5
- package/src/lib/scss/variables.scss +1 -1
- package/src/lib/session.ts +78 -0
- package/src/lib/tracking.ts +1 -2
- package/src/lib/types/injection.d.ts +2 -0
- package/src/lib/types/session.d.ts +14 -0
- package/src/routes/+page.svelte +37 -7
- package/src/routes/components/AfterArticlePlaylinks.svelte +6 -5
- package/src/routes/components/Editorial/Editor.svelte +36 -26
- package/src/routes/components/Editorial/Session.svelte +97 -0
- package/src/routes/components/Modal.svelte +1 -3
- package/src/routes/components/Playlinks.svelte +4 -5
- package/src/routes/components/Popover.svelte +1 -3
- package/src/routes/components/Title.svelte +0 -5
- package/src/tests/lib/api.test.js +14 -14
- package/src/tests/lib/linkInjection.test.js +56 -51
- package/src/tests/lib/localization.test.js +0 -7
- package/src/tests/lib/session.test.js +95 -0
- package/src/tests/lib/tracking.test.js +0 -16
- package/src/tests/routes/+page.test.js +14 -4
- package/src/tests/routes/components/Editorial/Editor.test.js +17 -1
- package/src/tests/routes/components/Editorial/EditorItem.test.js +7 -7
- package/src/tests/routes/components/Editorial/Session.test.js +80 -0
- package/src/tests/setup.js +23 -5
- package/src/lib/event.ts +0 -6
- package/src/lib/viewTransition.ts +0 -25
- package/src/tests/lib/event.test.js +0 -22
- package/src/tests/lib/viewTransition.test.js +0 -13
|
@@ -26,7 +26,7 @@ describe('EditorItem.svelte', () => {
|
|
|
26
26
|
it('Should highlight and unhighlight the matching link when component is hovered', async () => {
|
|
27
27
|
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
28
28
|
|
|
29
|
-
injectLinksInDocument(Array.from(document.querySelectorAll('p')), linkInjections)
|
|
29
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
|
|
30
30
|
|
|
31
31
|
const { getAllByText, container } = render(EditorItem, { linkInjection })
|
|
32
32
|
|
|
@@ -41,7 +41,7 @@ describe('EditorItem.svelte', () => {
|
|
|
41
41
|
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
42
42
|
|
|
43
43
|
const linkInjectionWithAfterArticle = { ...linkInjection, in_text: true, after_article: true }
|
|
44
|
-
injectLinksInDocument(Array.from(document.querySelectorAll('p')), { aiInjections: [linkInjectionWithAfterArticle], manualInjections: [] })
|
|
44
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, { aiInjections: [linkInjectionWithAfterArticle], manualInjections: [] })
|
|
45
45
|
|
|
46
46
|
const { container } = render(EditorItem, { linkInjection })
|
|
47
47
|
|
|
@@ -54,7 +54,7 @@ describe('EditorItem.svelte', () => {
|
|
|
54
54
|
|
|
55
55
|
const failedInjection = generateInjection('This is a sentence', 'fail')
|
|
56
56
|
|
|
57
|
-
injectLinksInDocument(Array.from(document.querySelectorAll('p')), { aiInjections: [], manualInjections: [failedInjection] })
|
|
57
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, { aiInjections: [], manualInjections: [failedInjection] })
|
|
58
58
|
|
|
59
59
|
const { container } = render(EditorItem, { linkInjection })
|
|
60
60
|
|
|
@@ -65,7 +65,7 @@ describe('EditorItem.svelte', () => {
|
|
|
65
65
|
it('Should scroll matching link into view when component is clicked', async () => {
|
|
66
66
|
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
67
67
|
|
|
68
|
-
injectLinksInDocument(Array.from(document.querySelectorAll('p')), linkInjections)
|
|
68
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
|
|
69
69
|
|
|
70
70
|
const { container } = render(EditorItem, { linkInjection })
|
|
71
71
|
|
|
@@ -76,7 +76,7 @@ describe('EditorItem.svelte', () => {
|
|
|
76
76
|
it('Should not scroll matching link into view when component is clicked but there is no matching injection', async () => {
|
|
77
77
|
document.body.innerHTML = '<main><p>This has no matching injections.</p></main>'
|
|
78
78
|
|
|
79
|
-
injectLinksInDocument(Array.from(document.querySelectorAll('main p')), linkInjections)
|
|
79
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('main p')), () => null, linkInjections)
|
|
80
80
|
|
|
81
81
|
const { container } = render(EditorItem, { linkInjection })
|
|
82
82
|
|
|
@@ -87,7 +87,7 @@ describe('EditorItem.svelte', () => {
|
|
|
87
87
|
it('Should not scroll matching link into view when component is clicked on button or input', async () => {
|
|
88
88
|
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
89
89
|
|
|
90
|
-
injectLinksInDocument(Array.from(document.querySelectorAll('p')), linkInjections)
|
|
90
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
|
|
91
91
|
|
|
92
92
|
const { container } = render(EditorItem, { linkInjection })
|
|
93
93
|
|
|
@@ -101,7 +101,7 @@ describe('EditorItem.svelte', () => {
|
|
|
101
101
|
it('Should highlight element in editor when hovering element on page', async () => {
|
|
102
102
|
document.body.innerHTML = '<p>This is a sentence with an injection.</p>'
|
|
103
103
|
|
|
104
|
-
injectLinksInDocument(Array.from(document.querySelectorAll('p')), linkInjections)
|
|
104
|
+
injectLinksInDocument(Array.from(document.querySelectorAll('p')), () => null, linkInjections)
|
|
105
105
|
|
|
106
106
|
await new Promise(res => setTimeout(res))
|
|
107
107
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { fireEvent, render, waitFor } from '@testing-library/svelte'
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import Session from '../../../../routes/components/Editorial/Session.svelte'
|
|
5
|
+
import { fetchAsSession, isAllowedToEdit, saveCurrentSession } from '$lib/session'
|
|
6
|
+
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
vi.mock(import('$lib/session'), async (importOriginal) => {
|
|
9
|
+
const actual = await importOriginal()
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
fetchAsSession: vi.fn(() => {}),
|
|
13
|
+
saveCurrentSession: vi.fn(() => {}),
|
|
14
|
+
sessionPollPeriodMilliseconds: 500,
|
|
15
|
+
isAllowedToEdit: vi.fn(() => true),
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('Session.svelte', () => {
|
|
20
|
+
const onallow = vi.fn()
|
|
21
|
+
const ondisallow = vi.fn()
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.resetAllMocks()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('Should call fetchAsSession on mount', () => {
|
|
28
|
+
render(Session)
|
|
29
|
+
expect(fetchAsSession).toHaveBeenCalled()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('Should continously poll fetchAsSession', async () => {
|
|
33
|
+
render(Session)
|
|
34
|
+
|
|
35
|
+
await waitFor(() => expect(fetchAsSession).toHaveBeenCalledTimes(2))
|
|
36
|
+
await waitFor(() => expect(fetchAsSession).toHaveBeenCalledTimes(3))
|
|
37
|
+
await waitFor(() => expect(fetchAsSession).toHaveBeenCalledTimes(4))
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('Should not show alert and call given onallow function when user is allowed to edit', async () => {
|
|
41
|
+
vi.mocked(fetchAsSession).mockResolvedValueOnce({ automation_enabled: true, injections_enabled: true })
|
|
42
|
+
vi.mocked(isAllowedToEdit).mockReturnValueOnce(true)
|
|
43
|
+
|
|
44
|
+
const { queryByText } = render(Session, { onallow, ondisallow })
|
|
45
|
+
|
|
46
|
+
expect(queryByText('Someone else is currently editing this document.')).not.toBeTruthy()
|
|
47
|
+
|
|
48
|
+
await waitFor(() => {
|
|
49
|
+
expect(onallow).toHaveBeenCalled()
|
|
50
|
+
expect(ondisallow).not.toHaveBeenCalled()
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('Should show alert and call given ondisallow function if user is not allowed to edit', async () => {
|
|
55
|
+
vi.mocked(fetchAsSession).mockResolvedValueOnce({ automation_enabled: true, injections_enabled: true })
|
|
56
|
+
vi.mocked(isAllowedToEdit).mockReturnValueOnce(false)
|
|
57
|
+
|
|
58
|
+
const { getByText } = render(Session, { onallow, ondisallow })
|
|
59
|
+
|
|
60
|
+
await waitFor(() => {
|
|
61
|
+
expect(getByText('Someone else is currently editing this document.')).toBeTruthy()
|
|
62
|
+
expect(onallow).not.toHaveBeenCalled()
|
|
63
|
+
expect(ondisallow).toHaveBeenCalled()
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('Should call given ontakeover function when button is alert is clicked', async () => {
|
|
68
|
+
vi.mocked(fetchAsSession).mockResolvedValueOnce({ automation_enabled: true, injections_enabled: true })
|
|
69
|
+
vi.mocked(isAllowedToEdit).mockReturnValueOnce(false)
|
|
70
|
+
|
|
71
|
+
const ontakeover = vi.fn()
|
|
72
|
+
const { getByText } = render(Session, { onallow, ondisallow, ontakeover })
|
|
73
|
+
|
|
74
|
+
await waitFor(() => getByText('Take over editing'))
|
|
75
|
+
await fireEvent.click(getByText('Take over editing'))
|
|
76
|
+
|
|
77
|
+
expect(ontakeover).toHaveBeenCalled()
|
|
78
|
+
expect(saveCurrentSession).toHaveBeenCalled()
|
|
79
|
+
})
|
|
80
|
+
})
|
package/src/tests/setup.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { cleanup } from '@testing-library/svelte'
|
|
2
2
|
import { afterEach, beforeAll, beforeEach, vi } from 'vitest'
|
|
3
3
|
|
|
4
|
-
vi.mock('svelte/transition')
|
|
5
|
-
|
|
6
4
|
// https://github.com/jsdom/jsdom/issues/3429#issuecomment-1936128876
|
|
7
5
|
const mockAnimations = () => {
|
|
8
6
|
Element.prototype.animate ??= vi.fn().mockReturnValue({
|
|
9
|
-
onfinish: null,
|
|
10
7
|
finished: Promise.resolve(),
|
|
11
8
|
cancel: vi.fn(),
|
|
12
9
|
startTime: null,
|
|
@@ -35,8 +32,31 @@ const mockLocalStorage = () => {
|
|
|
35
32
|
}
|
|
36
33
|
}
|
|
37
34
|
|
|
35
|
+
const mockSessionStorage = () => {
|
|
36
|
+
/** @type {Record<string, string>} */
|
|
37
|
+
let store = {}
|
|
38
|
+
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
;(window).sessionStorage = {
|
|
41
|
+
getItem: function (key) {
|
|
42
|
+
return store[key] || null
|
|
43
|
+
},
|
|
44
|
+
setItem: function (key, value) {
|
|
45
|
+
store[key] = value.toString()
|
|
46
|
+
},
|
|
47
|
+
removeItem: function (key) {
|
|
48
|
+
delete store[key]
|
|
49
|
+
},
|
|
50
|
+
clear: function () {
|
|
51
|
+
store = {}
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
beforeAll(() => {
|
|
57
|
+
mockAnimations()
|
|
39
58
|
mockLocalStorage()
|
|
59
|
+
mockSessionStorage()
|
|
40
60
|
})
|
|
41
61
|
|
|
42
62
|
beforeEach(() => {
|
|
@@ -48,8 +68,6 @@ beforeEach(() => {
|
|
|
48
68
|
|
|
49
69
|
window.HTMLElement.prototype.scrollIntoView = vi.fn()
|
|
50
70
|
localStorage.clear()
|
|
51
|
-
|
|
52
|
-
mockAnimations()
|
|
53
71
|
})
|
|
54
72
|
|
|
55
73
|
afterEach(() => {
|
package/src/lib/event.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Play a view transition if supported. We remove the view transition name from the old element and add it to the new.
|
|
3
|
-
* If not support, simply call the callback.
|
|
4
|
-
* A condition can be passed to optionally only call the callback without the view transition.
|
|
5
|
-
*/
|
|
6
|
-
export function playFallbackViewTransition(callback: Function, condition: boolean): void {
|
|
7
|
-
console.log(condition)
|
|
8
|
-
if (!document.startViewTransition || !condition) {
|
|
9
|
-
callback()
|
|
10
|
-
return
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
document.startViewTransition(() => {
|
|
14
|
-
const viewTransitionOldElement = document.querySelector('[data-view-transition-old]') as HTMLElement
|
|
15
|
-
if (!viewTransitionOldElement) return
|
|
16
|
-
|
|
17
|
-
const viewTransitionName = window.getComputedStyle(viewTransitionOldElement).getPropertyValue('view-transition-name')
|
|
18
|
-
viewTransitionOldElement.style.viewTransitionName = 'none'
|
|
19
|
-
|
|
20
|
-
callback()
|
|
21
|
-
|
|
22
|
-
const viewTransitionNewElement = document.querySelector('[data-view-transition-new]') as HTMLElement
|
|
23
|
-
if (viewTransitionNewElement) viewTransitionNewElement.style.viewTransitionName = viewTransitionName
|
|
24
|
-
})
|
|
25
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { isHoldingSpecialKey } from '$lib/event'
|
|
3
|
-
|
|
4
|
-
describe('event.js', () => {
|
|
5
|
-
describe('isHoldingSpecialKey', () => {
|
|
6
|
-
it('Should return true when holding special key', () => {
|
|
7
|
-
// @ts-ignore
|
|
8
|
-
expect(isHoldingSpecialKey({ ctrlKey: true, button: 0 })).toBe(true)
|
|
9
|
-
// @ts-ignore
|
|
10
|
-
expect(isHoldingSpecialKey({ metaKey: true, button: 0 })).toBe(true)
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
expect(isHoldingSpecialKey({ button: 1 })).toBe(true)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('Should return false when not holding special kew', () => {
|
|
16
|
-
// @ts-ignore
|
|
17
|
-
expect(isHoldingSpecialKey({ ctrlKey: false, metaKey: false, button: 0 })).toBe(false)
|
|
18
|
-
// @ts-ignore
|
|
19
|
-
expect(isHoldingSpecialKey({ button: 0 })).toBe(false)
|
|
20
|
-
})
|
|
21
|
-
})
|
|
22
|
-
})
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
-
import { playFallbackViewTransition } from '$lib/viewTransition'
|
|
3
|
-
|
|
4
|
-
describe('viewTransition.js', () => {
|
|
5
|
-
describe('playFallbackViewTransition', () => {
|
|
6
|
-
it('Should call callback', () => {
|
|
7
|
-
const mock = vi.fn()
|
|
8
|
-
playFallbackViewTransition(mock)
|
|
9
|
-
|
|
10
|
-
expect(mock).toHaveBeenCalled()
|
|
11
|
-
})
|
|
12
|
-
})
|
|
13
|
-
})
|