@semiont/react-ui 0.5.11 → 0.5.13
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/{PdfAnnotationCanvas.client-KWQ7XDWT.js → PdfAnnotationCanvas.client-75GY2EDR.js} +12 -6
- package/dist/PdfAnnotationCanvas.client-75GY2EDR.js.map +1 -0
- package/dist/{chunk-O2MD7TGE.js → chunk-VXASXU4K.js} +10 -2
- package/dist/chunk-VXASXU4K.js.map +1 -0
- package/dist/index.d.ts +54 -16
- package/dist/index.js +395 -343
- package/dist/index.js.map +1 -1
- package/dist/test-utils.d.ts +5 -1
- package/dist/test-utils.js +3 -2
- package/dist/test-utils.js.map +1 -1
- package/package.json +5 -5
- package/src/components/image-annotation/AnnotationOverlay.tsx +6 -6
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +12 -4
- package/src/components/image-annotation/__tests__/AnnotationOverlay.click.test.tsx +137 -0
- package/src/components/image-annotation/__tests__/SvgDrawingCanvas.click.test.tsx +108 -0
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +12 -4
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +137 -1
- package/src/components/resource/BrowseView.tsx +14 -6
- package/src/components/resource/ResourceViewer.tsx +6 -4
- package/src/components/resource/__tests__/BrowseView.test.tsx +98 -31
- package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +40 -1
- package/src/components/resource/__tests__/browse-renderers.dispatch.test.tsx +113 -0
- package/src/components/resource/browse-renderers.tsx +26 -4
- package/src/components/resource/panels/AssessmentEntry.tsx +5 -4
- package/src/components/resource/panels/AssessmentPanel.tsx +8 -5
- package/src/components/resource/panels/AssistSection.tsx +4 -3
- package/src/components/resource/panels/CommentEntry.tsx +5 -4
- package/src/components/resource/panels/CommentsPanel.tsx +8 -5
- package/src/components/resource/panels/HighlightEntry.tsx +5 -4
- package/src/components/resource/panels/HighlightPanel.tsx +8 -5
- package/src/components/resource/panels/ReferenceEntry.tsx +9 -10
- package/src/components/resource/panels/ReferencesPanel.tsx +11 -6
- package/src/components/resource/panels/ResourceInfoPanel.tsx +4 -3
- package/src/components/resource/panels/TagEntry.tsx +5 -3
- package/src/components/resource/panels/TaggingPanel.tsx +7 -4
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +10 -0
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +28 -20
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +38 -28
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +46 -17
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +65 -57
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +53 -24
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +27 -17
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +23 -17
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +48 -77
- package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +87 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +15 -9
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +77 -59
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +11 -4
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +33 -19
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +15 -7
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +3 -0
- package/dist/PdfAnnotationCanvas.client-KWQ7XDWT.js.map +0 -1
- package/dist/chunk-O2MD7TGE.js.map +0 -1
|
@@ -6,6 +6,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
6
6
|
import '@testing-library/jest-dom';
|
|
7
7
|
import { CommentsPanel } from '../CommentsPanel';
|
|
8
8
|
import type { EventBus } from '@semiont/core';
|
|
9
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
9
10
|
import { createTestSemiontWrapper } from '../../../../test-utils';
|
|
10
11
|
|
|
11
12
|
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
@@ -32,8 +33,18 @@ function createEventTracker() {
|
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
// Per-test session/bus/wrapper — created in beforeEach because test-utils
|
|
37
|
+
// disposes every created client after each test (a module-scope wrapper
|
|
38
|
+
// would hand later tests a disposed client).
|
|
39
|
+
let session: SemiontSession;
|
|
40
|
+
let eventBus: EventBus;
|
|
41
|
+
let SemiontWrapper: React.ComponentType<{ children: React.ReactNode }>;
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
({ session, eventBus, SemiontWrapper } = createTestSemiontWrapper());
|
|
45
|
+
});
|
|
46
|
+
|
|
35
47
|
const renderWithEventBus = (component: React.ReactElement, tracker?: ReturnType<typeof createEventTracker>) => {
|
|
36
|
-
const { SemiontWrapper, eventBus } = createTestSemiontWrapper();
|
|
37
48
|
if (tracker) tracker._attach(eventBus);
|
|
38
49
|
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
39
50
|
<SemiontWrapper>{children}</SemiontWrapper>
|
|
@@ -168,7 +179,7 @@ describe('CommentsPanel Component', () => {
|
|
|
168
179
|
|
|
169
180
|
describe('Rendering', () => {
|
|
170
181
|
it('should render panel header with title and count', () => {
|
|
171
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
182
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />);
|
|
172
183
|
|
|
173
184
|
const headings = screen.getAllByText(/Comments/);
|
|
174
185
|
expect(headings.length).toBeGreaterThan(0);
|
|
@@ -176,13 +187,13 @@ describe('CommentsPanel Component', () => {
|
|
|
176
187
|
});
|
|
177
188
|
|
|
178
189
|
it('should show empty state when no comments', () => {
|
|
179
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
190
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
180
191
|
|
|
181
192
|
expect(screen.getByText(/No comments yet/)).toBeInTheDocument();
|
|
182
193
|
});
|
|
183
194
|
|
|
184
195
|
it('should render all comments', () => {
|
|
185
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
196
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />);
|
|
186
197
|
|
|
187
198
|
expect(screen.getByTestId('comment-1')).toBeInTheDocument();
|
|
188
199
|
expect(screen.getByTestId('comment-2')).toBeInTheDocument();
|
|
@@ -190,7 +201,7 @@ describe('CommentsPanel Component', () => {
|
|
|
190
201
|
});
|
|
191
202
|
|
|
192
203
|
it('should have proper panel structure', () => {
|
|
193
|
-
const { container } = renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
204
|
+
const { container } = renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
194
205
|
|
|
195
206
|
// Find the root panel div (first child of the container)
|
|
196
207
|
const panel = container.firstChild as HTMLElement;
|
|
@@ -199,7 +210,7 @@ describe('CommentsPanel Component', () => {
|
|
|
199
210
|
|
|
200
211
|
it('should have scrollable comments list', () => {
|
|
201
212
|
const { container } = renderWithEventBus(
|
|
202
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.many} />
|
|
213
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.many} />
|
|
203
214
|
);
|
|
204
215
|
|
|
205
216
|
const commentsList = container.querySelector('.semiont-panel__list');
|
|
@@ -209,7 +220,7 @@ describe('CommentsPanel Component', () => {
|
|
|
209
220
|
|
|
210
221
|
describe('Comment Sorting', () => {
|
|
211
222
|
it('should sort comments by position in resource', () => {
|
|
212
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
223
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />);
|
|
213
224
|
|
|
214
225
|
const comments = screen.getAllByTestId(/comment-/);
|
|
215
226
|
|
|
@@ -223,13 +234,13 @@ describe('CommentsPanel Component', () => {
|
|
|
223
234
|
mockGetTextPositionSelector.mockReturnValue(null);
|
|
224
235
|
|
|
225
236
|
expect(() => {
|
|
226
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />);
|
|
237
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />);
|
|
227
238
|
}).not.toThrow();
|
|
228
239
|
});
|
|
229
240
|
|
|
230
241
|
it('should maintain sort order when comments update', () => {
|
|
231
242
|
const { rerender } = renderWithEventBus(
|
|
232
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
243
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />
|
|
233
244
|
);
|
|
234
245
|
|
|
235
246
|
// Add a new comment at position 25
|
|
@@ -239,7 +250,7 @@ describe('CommentsPanel Component', () => {
|
|
|
239
250
|
];
|
|
240
251
|
|
|
241
252
|
rerender(
|
|
242
|
-
<CommentsPanel {...defaultProps} annotations={updatedComments} />
|
|
253
|
+
<CommentsPanel session={session} {...defaultProps} annotations={updatedComments} />
|
|
243
254
|
);
|
|
244
255
|
|
|
245
256
|
const comments = screen.getAllByTestId(/comment-/);
|
|
@@ -254,7 +265,7 @@ describe('CommentsPanel Component', () => {
|
|
|
254
265
|
|
|
255
266
|
describe('New Comment Creation', () => {
|
|
256
267
|
it('should not show new comment input by default', () => {
|
|
257
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
268
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
258
269
|
|
|
259
270
|
expect(screen.queryByPlaceholderText(/Add your comment/)).not.toBeInTheDocument();
|
|
260
271
|
});
|
|
@@ -264,6 +275,7 @@ describe('CommentsPanel Component', () => {
|
|
|
264
275
|
|
|
265
276
|
renderWithEventBus(
|
|
266
277
|
<CommentsPanel
|
|
278
|
+
session={session}
|
|
267
279
|
{...defaultProps}
|
|
268
280
|
pendingAnnotation={pendingAnnotation}
|
|
269
281
|
/>
|
|
@@ -277,6 +289,7 @@ describe('CommentsPanel Component', () => {
|
|
|
277
289
|
|
|
278
290
|
renderWithEventBus(
|
|
279
291
|
<CommentsPanel
|
|
292
|
+
session={session}
|
|
280
293
|
{...defaultProps}
|
|
281
294
|
pendingAnnotation={pendingAnnotation}
|
|
282
295
|
/>
|
|
@@ -291,6 +304,7 @@ describe('CommentsPanel Component', () => {
|
|
|
291
304
|
|
|
292
305
|
renderWithEventBus(
|
|
293
306
|
<CommentsPanel
|
|
307
|
+
session={session}
|
|
294
308
|
{...defaultProps}
|
|
295
309
|
pendingAnnotation={pendingAnnotation}
|
|
296
310
|
/>
|
|
@@ -305,6 +319,7 @@ describe('CommentsPanel Component', () => {
|
|
|
305
319
|
|
|
306
320
|
renderWithEventBus(
|
|
307
321
|
<CommentsPanel
|
|
322
|
+
session={session}
|
|
308
323
|
{...defaultProps}
|
|
309
324
|
pendingAnnotation={pendingAnnotation}
|
|
310
325
|
/>
|
|
@@ -321,6 +336,7 @@ describe('CommentsPanel Component', () => {
|
|
|
321
336
|
|
|
322
337
|
renderWithEventBus(
|
|
323
338
|
<CommentsPanel
|
|
339
|
+
session={session}
|
|
324
340
|
{...defaultProps}
|
|
325
341
|
pendingAnnotation={pendingAnnotation}
|
|
326
342
|
/>
|
|
@@ -339,6 +355,7 @@ describe('CommentsPanel Component', () => {
|
|
|
339
355
|
|
|
340
356
|
renderWithEventBus(
|
|
341
357
|
<CommentsPanel
|
|
358
|
+
session={session}
|
|
342
359
|
{...defaultProps}
|
|
343
360
|
pendingAnnotation={pendingAnnotation}
|
|
344
361
|
/>
|
|
@@ -353,6 +370,7 @@ describe('CommentsPanel Component', () => {
|
|
|
353
370
|
|
|
354
371
|
renderWithEventBus(
|
|
355
372
|
<CommentsPanel
|
|
373
|
+
session={session}
|
|
356
374
|
{...defaultProps}
|
|
357
375
|
pendingAnnotation={pendingAnnotation}
|
|
358
376
|
/>
|
|
@@ -368,6 +386,7 @@ describe('CommentsPanel Component', () => {
|
|
|
368
386
|
|
|
369
387
|
renderWithEventBus(
|
|
370
388
|
<CommentsPanel
|
|
389
|
+
session={session}
|
|
371
390
|
{...defaultProps}
|
|
372
391
|
pendingAnnotation={pendingAnnotation}
|
|
373
392
|
/>,
|
|
@@ -394,6 +413,7 @@ describe('CommentsPanel Component', () => {
|
|
|
394
413
|
|
|
395
414
|
renderWithEventBus(
|
|
396
415
|
<CommentsPanel
|
|
416
|
+
session={session}
|
|
397
417
|
{...defaultProps}
|
|
398
418
|
pendingAnnotation={pendingAnnotation}
|
|
399
419
|
/>
|
|
@@ -411,6 +431,7 @@ describe('CommentsPanel Component', () => {
|
|
|
411
431
|
|
|
412
432
|
renderWithEventBus(
|
|
413
433
|
<CommentsPanel
|
|
434
|
+
session={session}
|
|
414
435
|
{...defaultProps}
|
|
415
436
|
pendingAnnotation={pendingAnnotation}
|
|
416
437
|
/>
|
|
@@ -425,6 +446,7 @@ describe('CommentsPanel Component', () => {
|
|
|
425
446
|
|
|
426
447
|
renderWithEventBus(
|
|
427
448
|
<CommentsPanel
|
|
449
|
+
session={session}
|
|
428
450
|
{...defaultProps}
|
|
429
451
|
pendingAnnotation={pendingAnnotation}
|
|
430
452
|
/>
|
|
@@ -442,6 +464,7 @@ describe('CommentsPanel Component', () => {
|
|
|
442
464
|
|
|
443
465
|
renderWithEventBus(
|
|
444
466
|
<CommentsPanel
|
|
467
|
+
session={session}
|
|
445
468
|
{...defaultProps}
|
|
446
469
|
pendingAnnotation={pendingAnnotation}
|
|
447
470
|
/>
|
|
@@ -459,6 +482,7 @@ describe('CommentsPanel Component', () => {
|
|
|
459
482
|
|
|
460
483
|
const { container } = renderWithEventBus(
|
|
461
484
|
<CommentsPanel
|
|
485
|
+
session={session}
|
|
462
486
|
{...defaultProps}
|
|
463
487
|
pendingAnnotation={pendingAnnotation}
|
|
464
488
|
/>
|
|
@@ -474,6 +498,7 @@ describe('CommentsPanel Component', () => {
|
|
|
474
498
|
it('should render comment entries', () => {
|
|
475
499
|
renderWithEventBus(
|
|
476
500
|
<CommentsPanel
|
|
501
|
+
session={session}
|
|
477
502
|
{...defaultProps}
|
|
478
503
|
annotations={mockComments.single}
|
|
479
504
|
/>
|
|
@@ -490,6 +515,7 @@ describe('CommentsPanel Component', () => {
|
|
|
490
515
|
expect(() => {
|
|
491
516
|
renderWithEventBus(
|
|
492
517
|
<CommentsPanel
|
|
518
|
+
session={session}
|
|
493
519
|
{...defaultProps}
|
|
494
520
|
annotations={mockComments.single}
|
|
495
521
|
/>
|
|
@@ -502,6 +528,7 @@ describe('CommentsPanel Component', () => {
|
|
|
502
528
|
it('should render comments', () => {
|
|
503
529
|
renderWithEventBus(
|
|
504
530
|
<CommentsPanel
|
|
531
|
+
session={session}
|
|
505
532
|
{...defaultProps}
|
|
506
533
|
annotations={mockComments.multiple}
|
|
507
534
|
/>
|
|
@@ -515,7 +542,7 @@ describe('CommentsPanel Component', () => {
|
|
|
515
542
|
describe('Panel Structure and Styling', () => {
|
|
516
543
|
it('should have fixed header that does not scroll', () => {
|
|
517
544
|
renderWithEventBus(
|
|
518
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.many} />
|
|
545
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.many} />
|
|
519
546
|
);
|
|
520
547
|
|
|
521
548
|
const headers = screen.getAllByText(/Comments/);
|
|
@@ -524,7 +551,7 @@ describe('CommentsPanel Component', () => {
|
|
|
524
551
|
});
|
|
525
552
|
|
|
526
553
|
it('should support dark mode', () => {
|
|
527
|
-
const { container } = renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
554
|
+
const { container } = renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
528
555
|
|
|
529
556
|
const panel = container.firstChild as HTMLElement;
|
|
530
557
|
expect(panel).toHaveClass('semiont-panel');
|
|
@@ -532,7 +559,7 @@ describe('CommentsPanel Component', () => {
|
|
|
532
559
|
|
|
533
560
|
it('should have proper spacing between comments', () => {
|
|
534
561
|
const { container } = renderWithEventBus(
|
|
535
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
562
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />
|
|
536
563
|
);
|
|
537
564
|
|
|
538
565
|
const commentsList = container.querySelector('.semiont-panel__list');
|
|
@@ -540,7 +567,7 @@ describe('CommentsPanel Component', () => {
|
|
|
540
567
|
});
|
|
541
568
|
|
|
542
569
|
it('should have proper border styling', () => {
|
|
543
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
570
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
544
571
|
|
|
545
572
|
const headers = screen.getAllByText(/Comments/);
|
|
546
573
|
const header = headers[0].closest('div');
|
|
@@ -551,7 +578,7 @@ describe('CommentsPanel Component', () => {
|
|
|
551
578
|
describe('Edge Cases', () => {
|
|
552
579
|
it('should handle empty comments array', () => {
|
|
553
580
|
expect(() => {
|
|
554
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={[]} />);
|
|
581
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={[]} />);
|
|
555
582
|
}).not.toThrow();
|
|
556
583
|
|
|
557
584
|
expect(screen.getByText(/No comments yet/)).toBeInTheDocument();
|
|
@@ -559,7 +586,7 @@ describe('CommentsPanel Component', () => {
|
|
|
559
586
|
|
|
560
587
|
it('should handle rapid comment additions', () => {
|
|
561
588
|
const { rerender } = renderWithEventBus(
|
|
562
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.empty} />
|
|
589
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.empty} />
|
|
563
590
|
);
|
|
564
591
|
|
|
565
592
|
for (let i = 1; i <= 5; i++) {
|
|
@@ -567,7 +594,7 @@ describe('CommentsPanel Component', () => {
|
|
|
567
594
|
createMockComment(`${j + 1}`, j * 10, (j + 1) * 10)
|
|
568
595
|
);
|
|
569
596
|
rerender(
|
|
570
|
-
<CommentsPanel {...defaultProps} annotations={comments} />
|
|
597
|
+
<CommentsPanel session={session} {...defaultProps} annotations={comments} />
|
|
571
598
|
);
|
|
572
599
|
}
|
|
573
600
|
|
|
@@ -576,13 +603,13 @@ describe('CommentsPanel Component', () => {
|
|
|
576
603
|
|
|
577
604
|
it('should handle comment removal', () => {
|
|
578
605
|
const { rerender } = renderWithEventBus(
|
|
579
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
606
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />
|
|
580
607
|
);
|
|
581
608
|
|
|
582
609
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(3);
|
|
583
610
|
|
|
584
611
|
rerender(
|
|
585
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.single} />
|
|
612
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.single} />
|
|
586
613
|
);
|
|
587
614
|
|
|
588
615
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(1);
|
|
@@ -593,6 +620,7 @@ describe('CommentsPanel Component', () => {
|
|
|
593
620
|
|
|
594
621
|
renderWithEventBus(
|
|
595
622
|
<CommentsPanel
|
|
623
|
+
session={session}
|
|
596
624
|
{...defaultProps}
|
|
597
625
|
pendingAnnotation={pendingAnnotation}
|
|
598
626
|
/>
|
|
@@ -608,7 +636,7 @@ describe('CommentsPanel Component', () => {
|
|
|
608
636
|
);
|
|
609
637
|
|
|
610
638
|
expect(() => {
|
|
611
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={manyComments} />);
|
|
639
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={manyComments} />);
|
|
612
640
|
}).not.toThrow();
|
|
613
641
|
|
|
614
642
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(100);
|
|
@@ -622,7 +650,7 @@ describe('CommentsPanel Component', () => {
|
|
|
622
650
|
];
|
|
623
651
|
|
|
624
652
|
expect(() => {
|
|
625
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} annotations={commentsAtSamePosition} />);
|
|
653
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} annotations={commentsAtSamePosition} />);
|
|
626
654
|
}).not.toThrow();
|
|
627
655
|
|
|
628
656
|
expect(screen.getAllByTestId(/comment-/)).toHaveLength(3);
|
|
@@ -631,7 +659,7 @@ describe('CommentsPanel Component', () => {
|
|
|
631
659
|
|
|
632
660
|
describe('Accessibility', () => {
|
|
633
661
|
it('should have proper heading structure', () => {
|
|
634
|
-
renderWithEventBus(<CommentsPanel {...defaultProps} />);
|
|
662
|
+
renderWithEventBus(<CommentsPanel session={session} {...defaultProps} />);
|
|
635
663
|
|
|
636
664
|
const headings = screen.getAllByText(/Comments/);
|
|
637
665
|
expect(headings[0]).toHaveClass('semiont-panel-header__text');
|
|
@@ -648,6 +676,7 @@ describe('CommentsPanel Component', () => {
|
|
|
648
676
|
|
|
649
677
|
renderWithEventBus(
|
|
650
678
|
<CommentsPanel
|
|
679
|
+
session={session}
|
|
651
680
|
{...defaultProps}
|
|
652
681
|
pendingAnnotation={pendingAnnotation}
|
|
653
682
|
/>
|
|
@@ -659,7 +688,7 @@ describe('CommentsPanel Component', () => {
|
|
|
659
688
|
|
|
660
689
|
it('should have semantic HTML structure', () => {
|
|
661
690
|
const { container } = renderWithEventBus(
|
|
662
|
-
<CommentsPanel {...defaultProps} annotations={mockComments.multiple} />
|
|
691
|
+
<CommentsPanel session={session} {...defaultProps} annotations={mockComments.multiple} />
|
|
663
692
|
);
|
|
664
693
|
|
|
665
694
|
// Panel should be a properly structured div hierarchy
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
2
|
import { screen } from '@testing-library/react';
|
|
3
3
|
import '@testing-library/jest-dom';
|
|
4
|
-
import { renderWithProviders } from '../../../../test-utils';
|
|
4
|
+
import { renderWithProviders, createTestSemiontWrapper } from '../../../../test-utils';
|
|
5
5
|
import userEvent from '@testing-library/user-event';
|
|
6
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
6
7
|
|
|
7
|
-
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
8
|
+
import type { Annotation, AnnotationId, EventBus } from '@semiont/core';
|
|
8
9
|
|
|
9
10
|
// Mock @semiont/http-transport
|
|
10
11
|
vi.mock('@semiont/core', async () => {
|
|
@@ -49,14 +50,21 @@ describe('HighlightEntry', () => {
|
|
|
49
50
|
isFocused: false,
|
|
50
51
|
};
|
|
51
52
|
|
|
53
|
+
// Created per-test: test-utils disposes every created client in a
|
|
54
|
+
// module-scope afterEach, so a module-scope session would be dead
|
|
55
|
+
// after the first test.
|
|
56
|
+
let session: SemiontSession;
|
|
57
|
+
let eventBus: EventBus;
|
|
58
|
+
|
|
52
59
|
beforeEach(() => {
|
|
53
60
|
vi.clearAllMocks();
|
|
54
61
|
mockGetAnnotationExactText.mockReturnValue('This is the highlighted text');
|
|
62
|
+
({ session, eventBus } = createTestSemiontWrapper());
|
|
55
63
|
});
|
|
56
64
|
|
|
57
65
|
describe('Rendering', () => {
|
|
58
66
|
it('should render the selected text in quotes', () => {
|
|
59
|
-
renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
67
|
+
renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
60
68
|
|
|
61
69
|
expect(screen.getByText(/This is the highlighted text/)).toBeInTheDocument();
|
|
62
70
|
});
|
|
@@ -65,7 +73,7 @@ describe('HighlightEntry', () => {
|
|
|
65
73
|
const longText = 'A'.repeat(250);
|
|
66
74
|
mockGetAnnotationExactText.mockReturnValue(longText);
|
|
67
75
|
|
|
68
|
-
renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
76
|
+
renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
69
77
|
|
|
70
78
|
// Should show first 200 chars followed by ellipsis
|
|
71
79
|
expect(screen.getByText(new RegExp(`"${'A'.repeat(200)}`))).toBeInTheDocument();
|
|
@@ -76,7 +84,7 @@ describe('HighlightEntry', () => {
|
|
|
76
84
|
const exactText = 'B'.repeat(200);
|
|
77
85
|
mockGetAnnotationExactText.mockReturnValue(exactText);
|
|
78
86
|
|
|
79
|
-
const { container } = renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
87
|
+
const { container } = renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
80
88
|
|
|
81
89
|
const quote = container.querySelector('.semiont-annotation-entry__quote');
|
|
82
90
|
expect(quote).toBeInTheDocument();
|
|
@@ -84,7 +92,7 @@ describe('HighlightEntry', () => {
|
|
|
84
92
|
});
|
|
85
93
|
|
|
86
94
|
it('should show creator name', () => {
|
|
87
|
-
renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
95
|
+
renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
88
96
|
|
|
89
97
|
expect(screen.getByText(/alice@example.com/)).toBeInTheDocument();
|
|
90
98
|
});
|
|
@@ -94,7 +102,7 @@ describe('HighlightEntry', () => {
|
|
|
94
102
|
delete (highlight as Record<string, unknown>).creator;
|
|
95
103
|
|
|
96
104
|
renderWithProviders(
|
|
97
|
-
<HighlightEntry highlight={highlight} isFocused={false} />
|
|
105
|
+
<HighlightEntry highlight={highlight} isFocused={false} session={session} />
|
|
98
106
|
);
|
|
99
107
|
|
|
100
108
|
expect(screen.getByText(/Unknown/)).toBeInTheDocument();
|
|
@@ -106,7 +114,7 @@ describe('HighlightEntry', () => {
|
|
|
106
114
|
});
|
|
107
115
|
|
|
108
116
|
renderWithProviders(
|
|
109
|
-
<HighlightEntry highlight={recentHighlight} isFocused={false} />
|
|
117
|
+
<HighlightEntry highlight={recentHighlight} isFocused={false} session={session} />
|
|
110
118
|
);
|
|
111
119
|
|
|
112
120
|
expect(screen.getByText(/just now/)).toBeInTheDocument();
|
|
@@ -115,7 +123,7 @@ describe('HighlightEntry', () => {
|
|
|
115
123
|
it('should not render quote section when selectedText is empty', () => {
|
|
116
124
|
mockGetAnnotationExactText.mockReturnValue('');
|
|
117
125
|
|
|
118
|
-
const { container } = renderWithProviders(<HighlightEntry {...defaultProps} />);
|
|
126
|
+
const { container } = renderWithProviders(<HighlightEntry {...defaultProps} session={session} />);
|
|
119
127
|
|
|
120
128
|
expect(container.querySelector('.semiont-annotation-entry__quote')).not.toBeInTheDocument();
|
|
121
129
|
});
|
|
@@ -125,12 +133,14 @@ describe('HighlightEntry', () => {
|
|
|
125
133
|
it('should emit browse:click on click', async () => {
|
|
126
134
|
const clickHandler = vi.fn();
|
|
127
135
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
// The component only sees the `session` prop now, so the assertion
|
|
137
|
+
// must subscribe on that same session's bus — not the bus of the
|
|
138
|
+
// wrapper renderWithProviders creates internally.
|
|
139
|
+
const { container } = renderWithProviders(
|
|
140
|
+
<HighlightEntry {...defaultProps} session={session} />
|
|
131
141
|
);
|
|
132
142
|
|
|
133
|
-
const subscription = eventBus
|
|
143
|
+
const subscription = eventBus.get('browse:click').subscribe(clickHandler);
|
|
134
144
|
|
|
135
145
|
const entry = container.firstChild as HTMLElement;
|
|
136
146
|
await userEvent.click(entry);
|
|
@@ -147,7 +157,7 @@ describe('HighlightEntry', () => {
|
|
|
147
157
|
describe('Hover state', () => {
|
|
148
158
|
it('should apply pulse class when isHovered is true', () => {
|
|
149
159
|
const { container } = renderWithProviders(
|
|
150
|
-
<HighlightEntry {...defaultProps} isHovered={true} />
|
|
160
|
+
<HighlightEntry {...defaultProps} isHovered={true} session={session} />
|
|
151
161
|
);
|
|
152
162
|
|
|
153
163
|
const entry = container.firstChild as HTMLElement;
|
|
@@ -156,7 +166,7 @@ describe('HighlightEntry', () => {
|
|
|
156
166
|
|
|
157
167
|
it('should not apply pulse class when isHovered is false', () => {
|
|
158
168
|
const { container } = renderWithProviders(
|
|
159
|
-
<HighlightEntry {...defaultProps} isHovered={false} />
|
|
169
|
+
<HighlightEntry {...defaultProps} isHovered={false} session={session} />
|
|
160
170
|
);
|
|
161
171
|
|
|
162
172
|
const entry = container.firstChild as HTMLElement;
|
|
@@ -167,7 +177,7 @@ describe('HighlightEntry', () => {
|
|
|
167
177
|
describe('Focus state', () => {
|
|
168
178
|
it('should set data-focused to true when focused', () => {
|
|
169
179
|
const { container } = renderWithProviders(
|
|
170
|
-
<HighlightEntry {...defaultProps} isFocused={true} />
|
|
180
|
+
<HighlightEntry {...defaultProps} isFocused={true} session={session} />
|
|
171
181
|
);
|
|
172
182
|
|
|
173
183
|
const entry = container.firstChild as HTMLElement;
|
|
@@ -176,7 +186,7 @@ describe('HighlightEntry', () => {
|
|
|
176
186
|
|
|
177
187
|
it('should set data-focused to false when not focused', () => {
|
|
178
188
|
const { container } = renderWithProviders(
|
|
179
|
-
<HighlightEntry {...defaultProps} isFocused={false} />
|
|
189
|
+
<HighlightEntry {...defaultProps} isFocused={false} session={session} />
|
|
180
190
|
);
|
|
181
191
|
|
|
182
192
|
const entry = container.firstChild as HTMLElement;
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
15
15
|
import React from 'react';
|
|
16
16
|
import { screen } from '@testing-library/react';
|
|
17
|
-
import { renderWithProviders } from '../../../../test-utils';
|
|
17
|
+
import { renderWithProviders, createTestSemiontWrapper } from '../../../../test-utils';
|
|
18
18
|
import { HighlightPanel } from '../HighlightPanel';
|
|
19
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
19
20
|
|
|
20
21
|
import type { Annotation, AnnotationId } from '@semiont/core';
|
|
21
22
|
|
|
@@ -48,9 +49,14 @@ vi.mock('../../../../contexts/useEventSubscription', () => ({
|
|
|
48
49
|
|
|
49
50
|
describe('HighlightPanel + AssistSection Integration', () => {
|
|
50
51
|
let mockAnnotations: Annotation[];
|
|
52
|
+
// Created per-test: test-utils disposes every created client in a
|
|
53
|
+
// module-scope afterEach, so a module-scope session would be dead
|
|
54
|
+
// after the first test.
|
|
55
|
+
let session: SemiontSession;
|
|
51
56
|
|
|
52
57
|
beforeEach(() => {
|
|
53
58
|
vi.clearAllMocks();
|
|
59
|
+
({ session } = createTestSemiontWrapper());
|
|
54
60
|
|
|
55
61
|
mockAnnotations = [
|
|
56
62
|
{
|
|
@@ -74,7 +80,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
74
80
|
describe('Detection Progress Prop Passing', () => {
|
|
75
81
|
it('should pass progress to AssistSection when provided', () => {
|
|
76
82
|
renderWithProviders(
|
|
77
|
-
<HighlightPanel resourceId="res-1"
|
|
83
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
78
84
|
annotations={mockAnnotations}
|
|
79
85
|
pendingAnnotation={null}
|
|
80
86
|
isAssisting={true}
|
|
@@ -93,7 +99,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
93
99
|
|
|
94
100
|
it('should pass null progress to AssistSection', () => {
|
|
95
101
|
renderWithProviders(
|
|
96
|
-
<HighlightPanel resourceId="res-1"
|
|
102
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
97
103
|
annotations={mockAnnotations}
|
|
98
104
|
pendingAnnotation={null}
|
|
99
105
|
isAssisting={false}
|
|
@@ -109,7 +115,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
109
115
|
|
|
110
116
|
it('should pass undefined progress to AssistSection', () => {
|
|
111
117
|
renderWithProviders(
|
|
112
|
-
<HighlightPanel resourceId="res-1"
|
|
118
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
113
119
|
annotations={mockAnnotations}
|
|
114
120
|
pendingAnnotation={null}
|
|
115
121
|
isAssisting={false}
|
|
@@ -125,7 +131,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
125
131
|
|
|
126
132
|
it('should keep progress visible after detection completes (isAssisting=false)', () => {
|
|
127
133
|
renderWithProviders(
|
|
128
|
-
<HighlightPanel resourceId="res-1"
|
|
134
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
129
135
|
annotations={mockAnnotations}
|
|
130
136
|
pendingAnnotation={null}
|
|
131
137
|
isAssisting={false}
|
|
@@ -146,7 +152,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
146
152
|
|
|
147
153
|
it('should pass progress with request parameters to AssistSection', () => {
|
|
148
154
|
renderWithProviders(
|
|
149
|
-
<HighlightPanel resourceId="res-1"
|
|
155
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
150
156
|
annotations={mockAnnotations}
|
|
151
157
|
pendingAnnotation={null}
|
|
152
158
|
isAssisting={true}
|
|
@@ -172,7 +178,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
172
178
|
describe('Annotate Mode Toggling', () => {
|
|
173
179
|
it('should render AssistSection when annotateMode is true', () => {
|
|
174
180
|
renderWithProviders(
|
|
175
|
-
<HighlightPanel resourceId="res-1"
|
|
181
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
176
182
|
annotations={mockAnnotations}
|
|
177
183
|
pendingAnnotation={null}
|
|
178
184
|
isAssisting={false}
|
|
@@ -186,7 +192,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
186
192
|
|
|
187
193
|
it('should NOT render AssistSection when annotateMode is false', () => {
|
|
188
194
|
renderWithProviders(
|
|
189
|
-
<HighlightPanel resourceId="res-1"
|
|
195
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
190
196
|
annotations={mockAnnotations}
|
|
191
197
|
pendingAnnotation={null}
|
|
192
198
|
isAssisting={false}
|
|
@@ -200,7 +206,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
200
206
|
|
|
201
207
|
it('should hide progress when switching to browse mode (annotateMode=false)', () => {
|
|
202
208
|
const { rerender } = renderWithProviders(
|
|
203
|
-
<HighlightPanel resourceId="res-1"
|
|
209
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
204
210
|
annotations={mockAnnotations}
|
|
205
211
|
pendingAnnotation={null}
|
|
206
212
|
isAssisting={true}
|
|
@@ -217,7 +223,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
217
223
|
|
|
218
224
|
// Switch to browse mode
|
|
219
225
|
rerender(
|
|
220
|
-
<HighlightPanel resourceId="res-1"
|
|
226
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
221
227
|
annotations={mockAnnotations}
|
|
222
228
|
pendingAnnotation={null}
|
|
223
229
|
isAssisting={true}
|
|
@@ -238,7 +244,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
238
244
|
describe('State Combinations', () => {
|
|
239
245
|
it('should handle isAssisting=true with no progress (starting state)', () => {
|
|
240
246
|
renderWithProviders(
|
|
241
|
-
<HighlightPanel resourceId="res-1"
|
|
247
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
242
248
|
annotations={mockAnnotations}
|
|
243
249
|
pendingAnnotation={null}
|
|
244
250
|
isAssisting={true}
|
|
@@ -253,7 +259,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
253
259
|
|
|
254
260
|
it('should handle isAssisting=false with progress (final state)', () => {
|
|
255
261
|
renderWithProviders(
|
|
256
|
-
<HighlightPanel resourceId="res-1"
|
|
262
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
257
263
|
annotations={mockAnnotations}
|
|
258
264
|
pendingAnnotation={null}
|
|
259
265
|
isAssisting={false}
|
|
@@ -274,7 +280,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
274
280
|
|
|
275
281
|
it('should handle multiple progress updates', () => {
|
|
276
282
|
const { rerender } = renderWithProviders(
|
|
277
|
-
<HighlightPanel resourceId="res-1"
|
|
283
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
278
284
|
annotations={mockAnnotations}
|
|
279
285
|
pendingAnnotation={null}
|
|
280
286
|
isAssisting={true}
|
|
@@ -291,7 +297,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
291
297
|
|
|
292
298
|
// Update to analyzing
|
|
293
299
|
rerender(
|
|
294
|
-
<HighlightPanel resourceId="res-1"
|
|
300
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
295
301
|
annotations={mockAnnotations}
|
|
296
302
|
pendingAnnotation={null}
|
|
297
303
|
isAssisting={true}
|
|
@@ -309,7 +315,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
309
315
|
|
|
310
316
|
// Update to complete
|
|
311
317
|
rerender(
|
|
312
|
-
<HighlightPanel resourceId="res-1"
|
|
318
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
313
319
|
annotations={mockAnnotations}
|
|
314
320
|
pendingAnnotation={null}
|
|
315
321
|
isAssisting={false}
|
|
@@ -330,7 +336,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
330
336
|
describe('Highlights List Rendering', () => {
|
|
331
337
|
it('should render highlights list alongside detection progress', () => {
|
|
332
338
|
renderWithProviders(
|
|
333
|
-
<HighlightPanel resourceId="res-1"
|
|
339
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
334
340
|
annotations={mockAnnotations}
|
|
335
341
|
pendingAnnotation={null}
|
|
336
342
|
isAssisting={true}
|
|
@@ -350,7 +356,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
|
|
|
350
356
|
|
|
351
357
|
it('should show empty state when no highlights', () => {
|
|
352
358
|
renderWithProviders(
|
|
353
|
-
<HighlightPanel resourceId="res-1"
|
|
359
|
+
<HighlightPanel session={session} resourceId="res-1"
|
|
354
360
|
annotations={[]}
|
|
355
361
|
pendingAnnotation={null}
|
|
356
362
|
isAssisting={false}
|