@playpilot/tpi 6.11.0 → 7.0.0-beta.2
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/.env +1 -0
- package/build.html +18 -0
- package/dist/link-injections.js +2 -11
- package/dist/mount.js +12 -0
- package/eslint.config.js +1 -0
- package/package.json +3 -2
- package/src/lib/api/auth.ts +1 -5
- package/src/lib/api/externalPages.ts +1 -1
- package/src/lib/api/titles.ts +1 -1
- package/src/lib/injection.ts +0 -91
- package/src/lib/injectionElements.ts +90 -0
- package/src/lib/language.ts +25 -0
- package/src/lib/localization.ts +1 -24
- package/src/lib/meta.ts +1 -1
- package/src/lib/tracking.ts +0 -1
- package/src/lib/types/global.d.ts +6 -3
- package/src/lib/types/script.d.ts +3 -1
- package/src/main.ts +21 -85
- package/src/mount.ts +29 -0
- package/src/routes/+page.svelte +10 -7
- package/src/routes/components/Editorial/EditorItem.svelte +2 -1
- package/src/routes/components/Editorial/ManualInjection.svelte +1 -1
- package/src/routes/components/Rails/ParticipantsRail.svelte +1 -1
- package/src/tests/lib/{injections.test.js → injection.test.js} +3 -207
- package/src/tests/lib/injectionElements.test.js +235 -0
- package/src/tests/lib/language.test.js +56 -0
- package/src/tests/lib/localization.test.js +3 -55
- package/vite._main.config.js +27 -0
- package/vite._mount.config.js +54 -0
- package/vite.config.js +2 -34
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { fireEvent } from '@testing-library/svelte'
|
|
2
2
|
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
|
3
3
|
|
|
4
|
-
import { injectLinksInDocument, clearLinkInjections, clearLinkInjection,
|
|
4
|
+
import { injectLinksInDocument, clearLinkInjections, clearLinkInjection, isAvailableAsManualInjection, filterRemovedAndInactiveInjections, isEquivalentInjection, filterInvalidInTextInjections, filterInvalidAfterArticleInjections, isValidInjection, isValidPlaylinkType, removePlayPilotTitleLinks } from '$lib/injection'
|
|
5
5
|
import { mount, unmount } from 'svelte'
|
|
6
6
|
import { fakeFetch, generateInjection } from '../helpers'
|
|
7
7
|
import { openModalForInjectedLink } from '$lib/modal'
|
|
8
|
+
import { getLinkInjectionElements } from '$lib/injectionElements'
|
|
8
9
|
|
|
9
10
|
vi.mock('svelte', () => ({
|
|
10
11
|
mount: vi.fn(),
|
|
@@ -33,7 +34,7 @@ function mockMatchMedia(matches = false) {
|
|
|
33
34
|
})
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
describe('
|
|
37
|
+
describe('injection.ts', () => {
|
|
37
38
|
beforeEach(() => {
|
|
38
39
|
vi.resetAllMocks()
|
|
39
40
|
clearLinkInjections()
|
|
@@ -979,211 +980,6 @@ describe('injections.js', () => {
|
|
|
979
980
|
})
|
|
980
981
|
})
|
|
981
982
|
|
|
982
|
-
describe('getLinkInjectionElements', () => {
|
|
983
|
-
it('Should return a list of elements inside the given parent', () => {
|
|
984
|
-
document.body.innerHTML = '<section><p>Some paragraph</p><div>Some div</div></section>'
|
|
985
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
986
|
-
|
|
987
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
988
|
-
})
|
|
989
|
-
|
|
990
|
-
it('Should ignore elements without text content', () => {
|
|
991
|
-
document.body.innerHTML = '<section><p>Some paragraph</p><div></div></section>'
|
|
992
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
993
|
-
|
|
994
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(1)
|
|
995
|
-
})
|
|
996
|
-
|
|
997
|
-
it('Should ignore child elements that were already included in parent text', () => {
|
|
998
|
-
document.body.innerHTML = '<section><p>Some paragraph <div>Some div</div></p></section>'
|
|
999
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1000
|
-
|
|
1001
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(1)
|
|
1002
|
-
})
|
|
1003
|
-
|
|
1004
|
-
it('Should return separate elements inside a parent if the parent has no direct text', () => {
|
|
1005
|
-
document.body.innerHTML = '<section><div>Some text</div> <div>Some div</div></section>'
|
|
1006
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1007
|
-
|
|
1008
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
1009
|
-
})
|
|
1010
|
-
|
|
1011
|
-
it('Should return separate elements when text content is deep', () => {
|
|
1012
|
-
document.body.innerHTML = '<section><div><div><main><p>Some <div>text<div></p><p>Some other text</p></main></div></div></section>'
|
|
1013
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1014
|
-
|
|
1015
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
1016
|
-
})
|
|
1017
|
-
|
|
1018
|
-
it('Should return a list as a single element', () => {
|
|
1019
|
-
document.body.innerHTML = `<section>
|
|
1020
|
-
<ul>
|
|
1021
|
-
<li>Hey!</li>
|
|
1022
|
-
<li>I</li>
|
|
1023
|
-
<li>am</li>
|
|
1024
|
-
<li>part</li>
|
|
1025
|
-
<li>of</li>
|
|
1026
|
-
<li>a</li>
|
|
1027
|
-
<li>list.</li>
|
|
1028
|
-
</ul>
|
|
1029
|
-
|
|
1030
|
-
<ol>
|
|
1031
|
-
<li>Hey!</li>
|
|
1032
|
-
<li>I</li>
|
|
1033
|
-
<li>am</li>
|
|
1034
|
-
<li>part</li>
|
|
1035
|
-
<li>of</li>
|
|
1036
|
-
<li>a</li>
|
|
1037
|
-
<li>list.</li>
|
|
1038
|
-
</ol>
|
|
1039
|
-
</section>`
|
|
1040
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1041
|
-
|
|
1042
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
1043
|
-
expect(getLinkInjectionElements(parent)[0].tagName).toBe('UL')
|
|
1044
|
-
expect(getLinkInjectionElements(parent)[1].tagName).toBe('OL')
|
|
1045
|
-
})
|
|
1046
|
-
|
|
1047
|
-
it('Should ignore links, buttons, script tags, style tags, iframes, h1, and more', () => {
|
|
1048
|
-
document.body.innerHTML = `<section>
|
|
1049
|
-
<h1>Some header</h1>
|
|
1050
|
-
|
|
1051
|
-
<a>I am a link</a>
|
|
1052
|
-
<button>And I am a button</button>
|
|
1053
|
-
|
|
1054
|
-
<script>I am a script</script>
|
|
1055
|
-
<style>I am styling</style>
|
|
1056
|
-
<iframe>I am an iframe</iframe>
|
|
1057
|
-
<noscript>I am noscript</noscript>
|
|
1058
|
-
<figcaption>I am a figcaption</figcaption>
|
|
1059
|
-
<time>I am time</time>
|
|
1060
|
-
<label>I am a label</label>
|
|
1061
|
-
<blockquote>I am a quote</blockquote>
|
|
1062
|
-
|
|
1063
|
-
<div>
|
|
1064
|
-
<a>I am another link</a>
|
|
1065
|
-
<h4>Some smaller header</h4>
|
|
1066
|
-
<p>And finally, some text</p>
|
|
1067
|
-
</div>
|
|
1068
|
-
</section>`
|
|
1069
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1070
|
-
|
|
1071
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(2) // The h4 tag and the paragraph tag
|
|
1072
|
-
})
|
|
1073
|
-
|
|
1074
|
-
it('Should ignore links, buttons, and headers even when deeply nested', () => {
|
|
1075
|
-
document.body.innerHTML = `<section>
|
|
1076
|
-
<div>
|
|
1077
|
-
<h2><button>Button inside header</button></h2>
|
|
1078
|
-
<div>
|
|
1079
|
-
<a><h3>Header inside link</h3></a>
|
|
1080
|
-
<p>Some text</p>
|
|
1081
|
-
</div>
|
|
1082
|
-
</div>
|
|
1083
|
-
</section>`
|
|
1084
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1085
|
-
|
|
1086
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(1)
|
|
1087
|
-
})
|
|
1088
|
-
|
|
1089
|
-
it('Should return an empty array if parent is empty', () => {
|
|
1090
|
-
document.body.innerHTML = '<section></section>'
|
|
1091
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1092
|
-
|
|
1093
|
-
expect(getLinkInjectionElements(parent)).toEqual([])
|
|
1094
|
-
})
|
|
1095
|
-
|
|
1096
|
-
it('Should ignore empty elements', () => {
|
|
1097
|
-
document.body.innerHTML = '<section><div> </div><div> </div><div>\n</div><div>Some text</div></section>'
|
|
1098
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1099
|
-
|
|
1100
|
-
expect(getLinkInjectionElements(parent)).toHaveLength(1)
|
|
1101
|
-
})
|
|
1102
|
-
|
|
1103
|
-
it('Should return elements in the same order they were given', () => {
|
|
1104
|
-
document.body.innerHTML = `<section>
|
|
1105
|
-
<div>
|
|
1106
|
-
<div><span><strong>Some first text</strong></span></div>
|
|
1107
|
-
<div>
|
|
1108
|
-
<em>Some empasized</em>
|
|
1109
|
-
<main><span><em><i>Some deeply nested text</i></em></span></main>
|
|
1110
|
-
<div>Some text</div>
|
|
1111
|
-
</div>
|
|
1112
|
-
</div>
|
|
1113
|
-
</section>`
|
|
1114
|
-
|
|
1115
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1116
|
-
const elements = getLinkInjectionElements(parent)
|
|
1117
|
-
|
|
1118
|
-
expect(elements).toHaveLength(4)
|
|
1119
|
-
expect(elements[0].nodeName).toBe('STRONG')
|
|
1120
|
-
expect(elements[1].nodeName).toBe('EM')
|
|
1121
|
-
expect(elements[2].nodeName).toBe('I')
|
|
1122
|
-
expect(elements[3].nodeName).toBe('DIV')
|
|
1123
|
-
})
|
|
1124
|
-
|
|
1125
|
-
it('Should exclude elements that match or are in the given excludeElementsSelector attribute', () => {
|
|
1126
|
-
document.body.innerHTML = `<section>
|
|
1127
|
-
<p>I am a regular element</p>
|
|
1128
|
-
<div data-exclude>I am to be excluded. <span>And so am I!</span></div>
|
|
1129
|
-
</section>
|
|
1130
|
-
`
|
|
1131
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1132
|
-
|
|
1133
|
-
const result = getLinkInjectionElements(parent, '[data-exclude]')
|
|
1134
|
-
expect(result).toHaveLength(1)
|
|
1135
|
-
expect(result[0].innerText).toBe('I am a regular element')
|
|
1136
|
-
})
|
|
1137
|
-
|
|
1138
|
-
it('Should return paragraphs fully even if they contain no direct text nodes, skipping empty paragraphs', () => {
|
|
1139
|
-
document.body.innerHTML = `<section>
|
|
1140
|
-
<p>Some text</p>
|
|
1141
|
-
<p><span>Some text</span> <span>broken up</span></p>
|
|
1142
|
-
<p><span>Some text</span> <span>broken up</span> but also contains direct text</p>
|
|
1143
|
-
<p></p>
|
|
1144
|
-
</section>`
|
|
1145
|
-
|
|
1146
|
-
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
1147
|
-
const elements = getLinkInjectionElements(parent)
|
|
1148
|
-
|
|
1149
|
-
expect(elements).toHaveLength(3)
|
|
1150
|
-
})
|
|
1151
|
-
})
|
|
1152
|
-
|
|
1153
|
-
describe('getLinkInjectionsParentElement', () => {
|
|
1154
|
-
it('Should return based on their given importance if no selector is given', () => {
|
|
1155
|
-
document.body.innerHTML = '<main><article></article></main>'
|
|
1156
|
-
expect(getLinkInjectionsParentElement().nodeName).toBe('ARTICLE')
|
|
1157
|
-
|
|
1158
|
-
document.body.innerHTML = '<main><div></div></main>'
|
|
1159
|
-
expect(getLinkInjectionsParentElement().nodeName).toBe('MAIN')
|
|
1160
|
-
|
|
1161
|
-
document.body.innerHTML = '<div></div>'
|
|
1162
|
-
expect(getLinkInjectionsParentElement().nodeName).toBe('BODY')
|
|
1163
|
-
})
|
|
1164
|
-
|
|
1165
|
-
it('Should return the element matching the given selector', () => {
|
|
1166
|
-
// @ts-ignore
|
|
1167
|
-
window.PlayPilotLinkInjections.selector = 'section'
|
|
1168
|
-
document.body.innerHTML = '<div><section></section></div>'
|
|
1169
|
-
expect(getLinkInjectionsParentElement().nodeName).toBe('SECTION')
|
|
1170
|
-
})
|
|
1171
|
-
|
|
1172
|
-
it('Should fall back to fallback elements if selector was given but matching element was not found', () => {
|
|
1173
|
-
// @ts-ignore
|
|
1174
|
-
window.PlayPilotLinkInjections.selector = 'section'
|
|
1175
|
-
document.body.innerHTML = '<div><article></article></div>'
|
|
1176
|
-
expect(getLinkInjectionsParentElement().nodeName).toBe('ARTICLE')
|
|
1177
|
-
})
|
|
1178
|
-
|
|
1179
|
-
it('Should escape selectors with :', () => {
|
|
1180
|
-
// @ts-ignore
|
|
1181
|
-
window.PlayPilotLinkInjections.selector = '.some:class'
|
|
1182
|
-
document.body.innerHTML = '<div><section class="some:class"></section></div>'
|
|
1183
|
-
expect(getLinkInjectionsParentElement().nodeName).toBe('SECTION')
|
|
1184
|
-
})
|
|
1185
|
-
})
|
|
1186
|
-
|
|
1187
983
|
describe('isAvailableAsManualInjection', () => {
|
|
1188
984
|
it('Should return true if the ai injection has a matching manual injection', () => {
|
|
1189
985
|
const aiInjection = generateInjection('Some sentence', 'Some title')
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { getLinkInjectionElements, getLinkInjectionsParentElement, getPageText } from '$lib/injectionElements'
|
|
4
|
+
|
|
5
|
+
describe('injectionElements.ts', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
vi.resetAllMocks()
|
|
8
|
+
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
window.PlayPilotLinkInjections = {}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
vi.useRealTimers()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe('getLinkInjectionElements', () => {
|
|
18
|
+
it('Should return a list of elements inside the given parent', () => {
|
|
19
|
+
document.body.innerHTML = '<section><p>Some paragraph</p><div>Some div</div></section>'
|
|
20
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
21
|
+
|
|
22
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('Should ignore elements without text content', () => {
|
|
26
|
+
document.body.innerHTML = '<section><p>Some paragraph</p><div></div></section>'
|
|
27
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
28
|
+
|
|
29
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(1)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('Should ignore child elements that were already included in parent text', () => {
|
|
33
|
+
document.body.innerHTML = '<section><p>Some paragraph <div>Some div</div></p></section>'
|
|
34
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
35
|
+
|
|
36
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(1)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('Should return separate elements inside a parent if the parent has no direct text', () => {
|
|
40
|
+
document.body.innerHTML = '<section><div>Some text</div> <div>Some div</div></section>'
|
|
41
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
42
|
+
|
|
43
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('Should return separate elements when text content is deep', () => {
|
|
47
|
+
document.body.innerHTML = '<section><div><div><main><p>Some <div>text<div></p><p>Some other text</p></main></div></div></section>'
|
|
48
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
49
|
+
|
|
50
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('Should return a list as a single element', () => {
|
|
54
|
+
document.body.innerHTML = `<section>
|
|
55
|
+
<ul>
|
|
56
|
+
<li>Hey!</li>
|
|
57
|
+
<li>I</li>
|
|
58
|
+
<li>am</li>
|
|
59
|
+
<li>part</li>
|
|
60
|
+
<li>of</li>
|
|
61
|
+
<li>a</li>
|
|
62
|
+
<li>list.</li>
|
|
63
|
+
</ul>
|
|
64
|
+
|
|
65
|
+
<ol>
|
|
66
|
+
<li>Hey!</li>
|
|
67
|
+
<li>I</li>
|
|
68
|
+
<li>am</li>
|
|
69
|
+
<li>part</li>
|
|
70
|
+
<li>of</li>
|
|
71
|
+
<li>a</li>
|
|
72
|
+
<li>list.</li>
|
|
73
|
+
</ol>
|
|
74
|
+
</section>`
|
|
75
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
76
|
+
|
|
77
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(2)
|
|
78
|
+
expect(getLinkInjectionElements(parent)[0].tagName).toBe('UL')
|
|
79
|
+
expect(getLinkInjectionElements(parent)[1].tagName).toBe('OL')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('Should ignore links, buttons, script tags, style tags, iframes, h1, and more', () => {
|
|
83
|
+
document.body.innerHTML = `<section>
|
|
84
|
+
<h1>Some header</h1>
|
|
85
|
+
|
|
86
|
+
<a>I am a link</a>
|
|
87
|
+
<button>And I am a button</button>
|
|
88
|
+
|
|
89
|
+
<script>I am a script</script>
|
|
90
|
+
<style>I am styling</style>
|
|
91
|
+
<iframe>I am an iframe</iframe>
|
|
92
|
+
<noscript>I am noscript</noscript>
|
|
93
|
+
<figcaption>I am a figcaption</figcaption>
|
|
94
|
+
<time>I am time</time>
|
|
95
|
+
<label>I am a label</label>
|
|
96
|
+
<blockquote>I am a quote</blockquote>
|
|
97
|
+
|
|
98
|
+
<div>
|
|
99
|
+
<a>I am another link</a>
|
|
100
|
+
<h4>Some smaller header</h4>
|
|
101
|
+
<p>And finally, some text</p>
|
|
102
|
+
</div>
|
|
103
|
+
</section>`
|
|
104
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
105
|
+
|
|
106
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(2) // The h4 tag and the paragraph tag
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('Should ignore links, buttons, and headers even when deeply nested', () => {
|
|
110
|
+
document.body.innerHTML = `<section>
|
|
111
|
+
<div>
|
|
112
|
+
<h2><button>Button inside header</button></h2>
|
|
113
|
+
<div>
|
|
114
|
+
<a><h3>Header inside link</h3></a>
|
|
115
|
+
<p>Some text</p>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</section>`
|
|
119
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
120
|
+
|
|
121
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(1)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('Should return an empty array if parent is empty', () => {
|
|
125
|
+
document.body.innerHTML = '<section></section>'
|
|
126
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
127
|
+
|
|
128
|
+
expect(getLinkInjectionElements(parent)).toEqual([])
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('Should ignore empty elements', () => {
|
|
132
|
+
document.body.innerHTML = '<section><div> </div><div> </div><div>\n</div><div>Some text</div></section>'
|
|
133
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
134
|
+
|
|
135
|
+
expect(getLinkInjectionElements(parent)).toHaveLength(1)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('Should return elements in the same order they were given', () => {
|
|
139
|
+
document.body.innerHTML = `<section>
|
|
140
|
+
<div>
|
|
141
|
+
<div><span><strong>Some first text</strong></span></div>
|
|
142
|
+
<div>
|
|
143
|
+
<em>Some empasized</em>
|
|
144
|
+
<main><span><em><i>Some deeply nested text</i></em></span></main>
|
|
145
|
+
<div>Some text</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</section>`
|
|
149
|
+
|
|
150
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
151
|
+
const elements = getLinkInjectionElements(parent)
|
|
152
|
+
|
|
153
|
+
expect(elements).toHaveLength(4)
|
|
154
|
+
expect(elements[0].nodeName).toBe('STRONG')
|
|
155
|
+
expect(elements[1].nodeName).toBe('EM')
|
|
156
|
+
expect(elements[2].nodeName).toBe('I')
|
|
157
|
+
expect(elements[3].nodeName).toBe('DIV')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('Should exclude elements that match or are in the given excludeElementsSelector attribute', () => {
|
|
161
|
+
document.body.innerHTML = `<section>
|
|
162
|
+
<p>I am a regular element</p>
|
|
163
|
+
<div data-exclude>I am to be excluded. <span>And so am I!</span></div>
|
|
164
|
+
</section>
|
|
165
|
+
`
|
|
166
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
167
|
+
|
|
168
|
+
const result = getLinkInjectionElements(parent, '[data-exclude]')
|
|
169
|
+
expect(result).toHaveLength(1)
|
|
170
|
+
expect(result[0].innerText).toBe('I am a regular element')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('Should return paragraphs fully even if they contain no direct text nodes, skipping empty paragraphs', () => {
|
|
174
|
+
document.body.innerHTML = `<section>
|
|
175
|
+
<p>Some text</p>
|
|
176
|
+
<p><span>Some text</span> <span>broken up</span></p>
|
|
177
|
+
<p><span>Some text</span> <span>broken up</span> but also contains direct text</p>
|
|
178
|
+
<p></p>
|
|
179
|
+
</section>`
|
|
180
|
+
|
|
181
|
+
const parent = /** @type {HTMLElement} */ (document.querySelector('section'))
|
|
182
|
+
const elements = getLinkInjectionElements(parent)
|
|
183
|
+
|
|
184
|
+
expect(elements).toHaveLength(3)
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
describe('getLinkInjectionsParentElement', () => {
|
|
189
|
+
it('Should return based on their given importance if no selector is given', () => {
|
|
190
|
+
document.body.innerHTML = '<main><article></article></main>'
|
|
191
|
+
expect(getLinkInjectionsParentElement().nodeName).toBe('ARTICLE')
|
|
192
|
+
|
|
193
|
+
document.body.innerHTML = '<main><div></div></main>'
|
|
194
|
+
expect(getLinkInjectionsParentElement().nodeName).toBe('MAIN')
|
|
195
|
+
|
|
196
|
+
document.body.innerHTML = '<div></div>'
|
|
197
|
+
expect(getLinkInjectionsParentElement().nodeName).toBe('BODY')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('Should return the element matching the given selector', () => {
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
window.PlayPilotLinkInjections.selector = 'section'
|
|
203
|
+
document.body.innerHTML = '<div><section></section></div>'
|
|
204
|
+
expect(getLinkInjectionsParentElement().nodeName).toBe('SECTION')
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('Should fall back to fallback elements if selector was given but matching element was not found', () => {
|
|
208
|
+
// @ts-ignore
|
|
209
|
+
window.PlayPilotLinkInjections.selector = 'section'
|
|
210
|
+
document.body.innerHTML = '<div><article></article></div>'
|
|
211
|
+
expect(getLinkInjectionsParentElement().nodeName).toBe('ARTICLE')
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('Should escape selectors with :', () => {
|
|
215
|
+
// @ts-ignore
|
|
216
|
+
window.PlayPilotLinkInjections.selector = '.some:class'
|
|
217
|
+
document.body.innerHTML = '<div><section class="some:class"></section></div>'
|
|
218
|
+
expect(getLinkInjectionsParentElement().nodeName).toBe('SECTION')
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
describe('getPageText', () => {
|
|
223
|
+
it('Should return page text for all given elements separated by newlines', () => {
|
|
224
|
+
document.body.innerHTML = `<section>
|
|
225
|
+
<p>Some text</p>
|
|
226
|
+
<p>Some second text</p>
|
|
227
|
+
<p>Some third text</p>
|
|
228
|
+
</section>`
|
|
229
|
+
|
|
230
|
+
expect(getPageText(getLinkInjectionElements(/** @type {HTMLElement} */ (document.querySelector('section'))))).toBe(
|
|
231
|
+
'Some text\n\nSome second text\n\nSome third text',
|
|
232
|
+
)
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest'
|
|
2
|
+
import { getLanguage } from '$lib/language'
|
|
3
|
+
|
|
4
|
+
describe('language.ts', () => {
|
|
5
|
+
describe('getLanguage', () => {
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
// @ts-expect-error
|
|
8
|
+
window.PlayPilotLinkInjections = {}
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('Should return the language given in config object before document language', () => {
|
|
12
|
+
// @ts-expect-error
|
|
13
|
+
window.PlayPilotLinkInjections = { language: 'sv-SE' }
|
|
14
|
+
|
|
15
|
+
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
16
|
+
html.setAttribute('lang', 'en-US')
|
|
17
|
+
|
|
18
|
+
expect(getLanguage()).toBe('sv-SE')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('Should return the language given as document language when config language is invalid', () => {
|
|
22
|
+
// @ts-expect-error
|
|
23
|
+
window.PlayPilotLinkInjections = { language: 'no' }
|
|
24
|
+
|
|
25
|
+
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
26
|
+
html.setAttribute('lang', 'sv-SE')
|
|
27
|
+
|
|
28
|
+
expect(getLanguage()).toBe('sv-SE')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('Should return the language given as document language when no config object is set', () => {
|
|
32
|
+
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
33
|
+
html.setAttribute('lang', 'sv-SE')
|
|
34
|
+
|
|
35
|
+
expect(getLanguage()).toBe('sv-SE')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('Should return the language given as document language even when using shorthand', () => {
|
|
39
|
+
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
40
|
+
html.setAttribute('lang', 'sv')
|
|
41
|
+
|
|
42
|
+
expect(getLanguage()).toBe('sv-SE')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('Should return the default language when document language is invalid', () => {
|
|
46
|
+
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
47
|
+
html.setAttribute('lang', 'no')
|
|
48
|
+
|
|
49
|
+
expect(getLanguage()).toBe('en-US')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('Should return the default language no document language or config language is given', () => {
|
|
53
|
+
expect(getLanguage()).toBe('en-US')
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe, it, expect
|
|
2
|
-
import {
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { t } from '$lib/localization'
|
|
3
3
|
import { Language } from '$lib/enums/Language'
|
|
4
4
|
import { translations } from '$lib/data/translations'
|
|
5
5
|
|
|
6
|
-
describe('localization.
|
|
6
|
+
describe('localization.ts', () => {
|
|
7
7
|
describe('t', () => {
|
|
8
8
|
it('Should return the value for the given language and key', () => {
|
|
9
9
|
expect(t(Object.keys(translations)[0], Language.English)).toBe(Object.values(translations)[0][Language.English])
|
|
@@ -19,56 +19,4 @@ describe('localization.js', () => {
|
|
|
19
19
|
expect(t(Object.keys(translations)[0], 'Some Invalid Language')).toBe(Object.keys(translations)[0])
|
|
20
20
|
})
|
|
21
21
|
})
|
|
22
|
-
|
|
23
|
-
describe('getLanguage', () => {
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
// @ts-expect-error
|
|
26
|
-
window.PlayPilotLinkInjections = {}
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('Should return the language given in config object before document language', () => {
|
|
30
|
-
// @ts-expect-error
|
|
31
|
-
window.PlayPilotLinkInjections = { language: 'sv-SE' }
|
|
32
|
-
|
|
33
|
-
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
34
|
-
html.setAttribute('lang', 'en-US')
|
|
35
|
-
|
|
36
|
-
expect(getLanguage()).toBe('sv-SE')
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('Should return the language given as document language when config language is invalid', () => {
|
|
40
|
-
// @ts-expect-error
|
|
41
|
-
window.PlayPilotLinkInjections = { language: 'no' }
|
|
42
|
-
|
|
43
|
-
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
44
|
-
html.setAttribute('lang', 'sv-SE')
|
|
45
|
-
|
|
46
|
-
expect(getLanguage()).toBe('sv-SE')
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('Should return the language given as document language when no config object is set', () => {
|
|
50
|
-
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
51
|
-
html.setAttribute('lang', 'sv-SE')
|
|
52
|
-
|
|
53
|
-
expect(getLanguage()).toBe('sv-SE')
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('Should return the language given as document language even when using shorthand', () => {
|
|
57
|
-
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
58
|
-
html.setAttribute('lang', 'sv')
|
|
59
|
-
|
|
60
|
-
expect(getLanguage()).toBe('sv-SE')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('Should return the default language when document language is invalid', () => {
|
|
64
|
-
const html = /** @type {HTMLElement} */ (document.querySelector('html'))
|
|
65
|
-
html.setAttribute('lang', 'no')
|
|
66
|
-
|
|
67
|
-
expect(getLanguage()).toBe('en-US')
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('Should return the default language no document language or config language is given', () => {
|
|
71
|
-
expect(getLanguage()).toBe('en-US')
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
22
|
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { defineConfig } from 'vitest/config'
|
|
3
|
+
import packageJson from './package.json'
|
|
4
|
+
|
|
5
|
+
export default defineConfig(() => ({
|
|
6
|
+
plugins: [],
|
|
7
|
+
|
|
8
|
+
build: {
|
|
9
|
+
rollupOptions: {
|
|
10
|
+
input: './src/main.ts',
|
|
11
|
+
output: {
|
|
12
|
+
name: 'PlayPilotLinkInjections',
|
|
13
|
+
entryFileNames: 'link-injections.js',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
resolve: {
|
|
19
|
+
alias: {
|
|
20
|
+
'$lib': path.resolve(__dirname, './src/lib'),
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
define: {
|
|
25
|
+
__SCRIPT_VERSION__: JSON.stringify(packageJson.version),
|
|
26
|
+
},
|
|
27
|
+
}))
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import * as dotenv from 'dotenv'
|
|
3
|
+
import { defineConfig } from 'vitest/config'
|
|
4
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
|
5
|
+
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
6
|
+
import packageJson from './package.json'
|
|
7
|
+
|
|
8
|
+
dotenv.config({ path: '.env' })
|
|
9
|
+
|
|
10
|
+
export default defineConfig(() => ({
|
|
11
|
+
plugins: [
|
|
12
|
+
svelte(),
|
|
13
|
+
cssInjectedByJsPlugin(),
|
|
14
|
+
injectEnvVariables,
|
|
15
|
+
],
|
|
16
|
+
|
|
17
|
+
build: {
|
|
18
|
+
emptyOutDir: false,
|
|
19
|
+
rollupOptions: {
|
|
20
|
+
input: './src/mount.ts',
|
|
21
|
+
output: {
|
|
22
|
+
name: 'PlayPilotMount',
|
|
23
|
+
entryFileNames: 'mount.js',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
resolve: {
|
|
29
|
+
alias: {
|
|
30
|
+
'$lib': path.resolve(__dirname, './src/lib'),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
define: {
|
|
35
|
+
__SCRIPT_VERSION__: JSON.stringify(packageJson.version),
|
|
36
|
+
},
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
const injectEnvVariables = {
|
|
40
|
+
name: 'resolve-env-variables',
|
|
41
|
+
/** @param {string} id */
|
|
42
|
+
resolveId(id) {
|
|
43
|
+
if (id === '$env/static/public') return '\0$env/static/public'
|
|
44
|
+
},
|
|
45
|
+
/** @param {string} id */
|
|
46
|
+
load(id) {
|
|
47
|
+
if (id === '\0$env/static/public') {
|
|
48
|
+
return Object.entries(process.env)
|
|
49
|
+
.filter(([key]) => key.startsWith('PUBLIC_'))
|
|
50
|
+
.map(([key, value]) => `export const ${key} = ${JSON.stringify(value)};`)
|
|
51
|
+
.join('\n')
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
}
|