|
3 | 3 | */ |
4 | 4 | import { describe, it, expect } from 'vitest'; |
5 | 5 | import { render, waitFor } from '@testing-library/react'; |
| 6 | +import userEvent from '@testing-library/user-event'; |
6 | 7 | import { Verse } from './verse'; |
7 | 8 |
|
8 | 9 | describe('Verse.Html - XSS Protection', () => { |
@@ -220,6 +221,104 @@ describe('Verse.Html - XSS Protection', () => { |
220 | 221 | }); |
221 | 222 | }); |
222 | 223 |
|
| 224 | +describe('Verse.Html - Footnotes', () => { |
| 225 | + it('should extract footnotes and create placeholders', async () => { |
| 226 | + const htmlWithFootnotes = ` |
| 227 | + <div class="p"> |
| 228 | + <span class="yv-v" v="5"></span><span class="yv-vlbl">5</span>The light shines in the darkness, and the |
| 229 | + darkness has not overcome<span class="yv-n f"><span class="fr">1:5 </span><span class="ft">Or </span><span class="fqa">understood</span></span> |
| 230 | + it. |
| 231 | + </div> |
| 232 | + `; |
| 233 | + |
| 234 | + const { container } = render(<Verse.Html html={htmlWithFootnotes} renderNotes={true} />); |
| 235 | + |
| 236 | + await waitFor(() => { |
| 237 | + const placeholder = container.querySelector('[data-verse-footnote="5"]'); |
| 238 | + expect(placeholder).not.toBeNull(); |
| 239 | + }); |
| 240 | + }); |
| 241 | + |
| 242 | + it('should remove original footnote elements', async () => { |
| 243 | + const htmlWithFootnotes = ` |
| 244 | + <div class="p"> |
| 245 | + <span class="yv-v" v="5"></span><span class="yv-vlbl">5</span>The light shines<span class="yv-n f"><span class="fr">1:5 </span><span class="ft">Or understood</span></span> it. |
| 246 | + </div> |
| 247 | + `; |
| 248 | + |
| 249 | + const { container } = render(<Verse.Html html={htmlWithFootnotes} renderNotes={true} />); |
| 250 | + |
| 251 | + await waitFor(() => { |
| 252 | + const footnoteElements = container.querySelectorAll('.yv-n.f'); |
| 253 | + expect(footnoteElements.length).toBe(0); |
| 254 | + }); |
| 255 | + }); |
| 256 | + |
| 257 | + it('should place footnote at end of correct verse (v42 not v43)', async () => { |
| 258 | + const htmlWithFootnotes = ` |
| 259 | + <div class="p"> |
| 260 | + <span class="yv-v" v="42"></span><span class="yv-vlbl">42</span>And he brought him to Jesus. |
| 261 | + </div> |
| 262 | + <div class="p"> |
| 263 | + Jesus looked at him and said, |
| 264 | + <span class="wj">"You are Simon son of John. You will be called Cephas"</span> |
| 265 | + (which, when translated, is Peter<span class="yv-n f"><span class="fr">1:42 </span><span class="fq">Cephas </span><span class="ft">(Aramaic) and </span><span class="fq">Peter </span><span class="ft">(Greek) both mean </span><span class="fqa">rock.</span></span>). |
| 266 | + </div> |
| 267 | + <div class="s1 yv-h">Jesus Calls Philip and Nathanael</div> |
| 268 | + <div class="p"> |
| 269 | + <span class="yv-v" v="43"></span><span class="yv-vlbl">43</span>The next day Jesus decided to leave for Galilee. |
| 270 | + </div> |
| 271 | + `; |
| 272 | + |
| 273 | + const { container } = render(<Verse.Html html={htmlWithFootnotes} renderNotes={true} />); |
| 274 | + |
| 275 | + await waitFor(() => { |
| 276 | + const placeholder42 = container.querySelector('[data-verse-footnote="42"]'); |
| 277 | + expect(placeholder42).not.toBeNull(); |
| 278 | + |
| 279 | + const verse43Marker = container.querySelector('.yv-v[v="43"]'); |
| 280 | + expect(verse43Marker).not.toBeNull(); |
| 281 | + |
| 282 | + const position42 = placeholder42?.compareDocumentPosition(verse43Marker!); |
| 283 | + expect(position42! & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy(); |
| 284 | + }); |
| 285 | + }); |
| 286 | + |
| 287 | + it('should handle multiple footnotes in a single verse', async () => { |
| 288 | + const htmlWithMultipleNotes = ` |
| 289 | + <div class="p"> |
| 290 | + <span class="yv-v" v="51"></span><span class="yv-vlbl">51</span>He then added, |
| 291 | + <span class="wj">"Very truly I tell you,</span><span class="yv-n f"><span class="fr">1:51 </span><span class="ft">The Greek is plural.</span></span> |
| 292 | + <span class="wj">you</span><span class="yv-n f"><span class="fr">1:51 </span><span class="ft">The Greek is plural.</span></span> |
| 293 | + <span class="wj">will see heaven open."</span> |
| 294 | + </div> |
| 295 | + `; |
| 296 | + |
| 297 | + const { container } = render(<Verse.Html html={htmlWithMultipleNotes} renderNotes={true} />); |
| 298 | + |
| 299 | + await waitFor(() => { |
| 300 | + const placeholder = container.querySelector('[data-verse-footnote="51"]'); |
| 301 | + expect(placeholder).not.toBeNull(); |
| 302 | + |
| 303 | + const footnoteElements = container.querySelectorAll('.yv-n.f'); |
| 304 | + expect(footnoteElements.length).toBe(0); |
| 305 | + }); |
| 306 | + |
| 307 | + const footnoteButton = container.querySelector('[data-verse-footnote="51"] button'); |
| 308 | + expect(footnoteButton).not.toBeNull(); |
| 309 | + |
| 310 | + await userEvent.click(footnoteButton!); |
| 311 | + |
| 312 | + await waitFor(() => { |
| 313 | + const popover = document.body.querySelector('[role="dialog"]'); |
| 314 | + expect(popover).not.toBeNull(); |
| 315 | + |
| 316 | + const listItems = popover?.querySelectorAll('ul li'); |
| 317 | + expect(listItems?.length).toBe(2); |
| 318 | + }); |
| 319 | + }); |
| 320 | +}); |
| 321 | + |
223 | 322 | describe('Verse.Text', () => { |
224 | 323 | it('should render verse with number and text (default size)', () => { |
225 | 324 | const { container } = render(<Verse.Text number={1} text="In the beginning" />); |
|
0 commit comments