@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
|
@@ -5,6 +5,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
5
5
|
import '@testing-library/jest-dom';
|
|
6
6
|
import { ReferencesPanel } from '../ReferencesPanel';
|
|
7
7
|
import type { EventBus } from '@semiont/core';
|
|
8
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
8
9
|
import { createTestSemiontWrapper } from '../../../../test-utils';
|
|
9
10
|
|
|
10
11
|
// Composition-based event tracker
|
|
@@ -29,8 +30,20 @@ function createEventTracker() {
|
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
// Per-test session/wrapper: created in beforeEach — test-utils disposes every
|
|
34
|
+
// created client in a module-scope afterEach, so a module-scope factory call
|
|
35
|
+
// would hand tests after the first a disposed client. The component is
|
|
36
|
+
// provider-free: the `session` passed as a prop (same factory call as the
|
|
37
|
+
// eventBus the tracker attaches to) is the only session it sees.
|
|
38
|
+
let session: SemiontSession;
|
|
39
|
+
let eventBus: EventBus;
|
|
40
|
+
let SemiontWrapper: React.ComponentType<{ children: React.ReactNode }>;
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
({ session, eventBus, SemiontWrapper } = createTestSemiontWrapper());
|
|
44
|
+
});
|
|
45
|
+
|
|
32
46
|
const renderWithEventBus = (component: React.ReactElement, tracker?: ReturnType<typeof createEventTracker>) => {
|
|
33
|
-
const { SemiontWrapper, eventBus } = createTestSemiontWrapper();
|
|
34
47
|
if (tracker) tracker._attach(eventBus);
|
|
35
48
|
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
36
49
|
<SemiontWrapper>{children}</SemiontWrapper>
|
|
@@ -96,6 +109,11 @@ describe('ReferencesPanel Component', () => {
|
|
|
96
109
|
pendingAnnotation: null,
|
|
97
110
|
};
|
|
98
111
|
|
|
112
|
+
// Per-test props: merges the beforeEach-created session at render time.
|
|
113
|
+
// (Module/describe-scope defaultProps must stay session-free — disposal
|
|
114
|
+
// hazard, see note above renderWithEventBus.)
|
|
115
|
+
const panelProps = () => ({ ...defaultProps, session });
|
|
116
|
+
|
|
99
117
|
beforeEach(() => {
|
|
100
118
|
vi.clearAllMocks();
|
|
101
119
|
});
|
|
@@ -106,13 +124,13 @@ describe('ReferencesPanel Component', () => {
|
|
|
106
124
|
|
|
107
125
|
describe('Rendering', () => {
|
|
108
126
|
it('should render panel with title', () => {
|
|
109
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
127
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
110
128
|
|
|
111
129
|
expect(screen.getByText('Annotate References')).toBeInTheDocument();
|
|
112
130
|
});
|
|
113
131
|
|
|
114
132
|
it('should render all entity type buttons', () => {
|
|
115
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
133
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
116
134
|
|
|
117
135
|
expect(screen.getByText('Person')).toBeInTheDocument();
|
|
118
136
|
expect(screen.getByText('Organization')).toBeInTheDocument();
|
|
@@ -121,13 +139,13 @@ describe('ReferencesPanel Component', () => {
|
|
|
121
139
|
});
|
|
122
140
|
|
|
123
141
|
it('should show message when no entity types available', () => {
|
|
124
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
142
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} allEntityTypes={[]} />);
|
|
125
143
|
|
|
126
144
|
expect(screen.getByText('No entity types available')).toBeInTheDocument();
|
|
127
145
|
});
|
|
128
146
|
|
|
129
147
|
it('should render start detection button', () => {
|
|
130
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
148
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
131
149
|
|
|
132
150
|
expect(screen.getByTitle('Annotate')).toBeInTheDocument();
|
|
133
151
|
});
|
|
@@ -135,7 +153,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
135
153
|
|
|
136
154
|
describe('Entity Type Selection', () => {
|
|
137
155
|
it('should toggle entity type selection on click', async () => {
|
|
138
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
156
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
139
157
|
|
|
140
158
|
const personButton = screen.getByText('Person');
|
|
141
159
|
|
|
@@ -154,7 +172,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
154
172
|
});
|
|
155
173
|
|
|
156
174
|
it('should allow multiple selections', async () => {
|
|
157
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
175
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
158
176
|
|
|
159
177
|
const personButton = screen.getByText('Person');
|
|
160
178
|
const orgButton = screen.getByText('Organization');
|
|
@@ -170,7 +188,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
170
188
|
});
|
|
171
189
|
|
|
172
190
|
it('should deselect when clicking selected type', async () => {
|
|
173
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
191
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
174
192
|
|
|
175
193
|
const personButton = screen.getByText('Person');
|
|
176
194
|
|
|
@@ -182,7 +200,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
182
200
|
});
|
|
183
201
|
|
|
184
202
|
it('should show selected count', async () => {
|
|
185
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
203
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
186
204
|
|
|
187
205
|
const personButton = screen.getByText('Person');
|
|
188
206
|
const orgButton = screen.getByText('Organization');
|
|
@@ -199,7 +217,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
199
217
|
});
|
|
200
218
|
|
|
201
219
|
it('should not show selected count when none selected', () => {
|
|
202
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
220
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
203
221
|
|
|
204
222
|
expect(screen.queryByText(/selected/i)).not.toBeInTheDocument();
|
|
205
223
|
});
|
|
@@ -207,7 +225,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
207
225
|
|
|
208
226
|
describe('Button Styling', () => {
|
|
209
227
|
it('should style selected buttons differently', async () => {
|
|
210
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
228
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
211
229
|
|
|
212
230
|
const personButton = screen.getByText('Person');
|
|
213
231
|
|
|
@@ -222,7 +240,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
222
240
|
});
|
|
223
241
|
|
|
224
242
|
it('should have proper ARIA attributes', () => {
|
|
225
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
243
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
226
244
|
|
|
227
245
|
const personButton = screen.getByText('Person');
|
|
228
246
|
|
|
@@ -231,7 +249,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
231
249
|
});
|
|
232
250
|
|
|
233
251
|
it('should have focus styles', () => {
|
|
234
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
252
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
235
253
|
|
|
236
254
|
const personButton = screen.getByText('Person');
|
|
237
255
|
|
|
@@ -241,7 +259,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
241
259
|
|
|
242
260
|
describe('Start Annotate Button', () => {
|
|
243
261
|
it('should be disabled when no types selected', () => {
|
|
244
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
262
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
245
263
|
|
|
246
264
|
const startButton = screen.getByTitle('Annotate');
|
|
247
265
|
|
|
@@ -249,7 +267,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
249
267
|
});
|
|
250
268
|
|
|
251
269
|
it('should be enabled when types are selected', async () => {
|
|
252
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
270
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
253
271
|
|
|
254
272
|
const personButton = screen.getByText('Person');
|
|
255
273
|
await userEvent.click(personButton);
|
|
@@ -261,7 +279,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
261
279
|
|
|
262
280
|
it('should emit annotate:detect-request event with selected types and includeDescriptiveReferences', async () => {
|
|
263
281
|
const tracker = createEventTracker();
|
|
264
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
282
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />, tracker);
|
|
265
283
|
|
|
266
284
|
await userEvent.click(screen.getByText('Person'));
|
|
267
285
|
await userEvent.click(screen.getByText('Organization'));
|
|
@@ -282,7 +300,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
282
300
|
|
|
283
301
|
it('should emit annotate:detect-request event with includeDescriptiveReferences when checkbox is checked', async () => {
|
|
284
302
|
const tracker = createEventTracker();
|
|
285
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
303
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />, tracker);
|
|
286
304
|
|
|
287
305
|
await userEvent.click(screen.getByText('Person'));
|
|
288
306
|
|
|
@@ -305,7 +323,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
305
323
|
});
|
|
306
324
|
|
|
307
325
|
it('should clear selected types after detection starts', async () => {
|
|
308
|
-
const { rerender } = renderWithEventBus(<ReferencesPanel {...
|
|
326
|
+
const { rerender } = renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
309
327
|
|
|
310
328
|
await userEvent.click(screen.getByText('Person'));
|
|
311
329
|
|
|
@@ -315,7 +333,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
315
333
|
// Simulate detection starting
|
|
316
334
|
rerender(
|
|
317
335
|
<ReferencesPanel
|
|
318
|
-
{...
|
|
336
|
+
{...panelProps()}
|
|
319
337
|
isAssisting={true}
|
|
320
338
|
progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
|
|
321
339
|
/>
|
|
@@ -324,7 +342,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
324
342
|
// Simulate detection completing
|
|
325
343
|
rerender(
|
|
326
344
|
<ReferencesPanel
|
|
327
|
-
{...
|
|
345
|
+
{...panelProps()}
|
|
328
346
|
isAssisting={false}
|
|
329
347
|
progress={{
|
|
330
348
|
stage: 'complete',
|
|
@@ -340,7 +358,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
340
358
|
});
|
|
341
359
|
|
|
342
360
|
it('should have proper styling when disabled', () => {
|
|
343
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
361
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
344
362
|
|
|
345
363
|
const startButton = screen.getByTitle('Annotate');
|
|
346
364
|
|
|
@@ -351,7 +369,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
351
369
|
});
|
|
352
370
|
|
|
353
371
|
it('should have proper styling when enabled', async () => {
|
|
354
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
372
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
355
373
|
|
|
356
374
|
await userEvent.click(screen.getByText('Person'));
|
|
357
375
|
|
|
@@ -368,7 +386,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
368
386
|
it('should show progress widget when detecting', () => {
|
|
369
387
|
renderWithEventBus(
|
|
370
388
|
<ReferencesPanel
|
|
371
|
-
{...
|
|
389
|
+
{...panelProps()}
|
|
372
390
|
isAssisting={true}
|
|
373
391
|
progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
|
|
374
392
|
/>
|
|
@@ -390,7 +408,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
390
408
|
|
|
391
409
|
renderWithEventBus(
|
|
392
410
|
<ReferencesPanel
|
|
393
|
-
{...
|
|
411
|
+
{...panelProps()}
|
|
394
412
|
isAssisting={true}
|
|
395
413
|
progress={progress}
|
|
396
414
|
/>
|
|
@@ -404,7 +422,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
404
422
|
it('should hide entity type selection during detection', () => {
|
|
405
423
|
renderWithEventBus(
|
|
406
424
|
<ReferencesPanel
|
|
407
|
-
{...
|
|
425
|
+
{...panelProps()}
|
|
408
426
|
isAssisting={true}
|
|
409
427
|
progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
|
|
410
428
|
/>
|
|
@@ -417,7 +435,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
417
435
|
it('should render cancel button when detecting', async () => {
|
|
418
436
|
renderWithEventBus(
|
|
419
437
|
<ReferencesPanel
|
|
420
|
-
{...
|
|
438
|
+
{...panelProps()}
|
|
421
439
|
isAssisting={true}
|
|
422
440
|
progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
|
|
423
441
|
/>
|
|
@@ -432,7 +450,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
432
450
|
it('should show completed log after detection finishes', () => {
|
|
433
451
|
const { rerender } = renderWithEventBus(
|
|
434
452
|
<ReferencesPanel
|
|
435
|
-
{...
|
|
453
|
+
{...panelProps()}
|
|
436
454
|
isAssisting={false}
|
|
437
455
|
progress={{
|
|
438
456
|
stage: 'complete',
|
|
@@ -449,7 +467,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
449
467
|
// Parent clears progress after completion
|
|
450
468
|
rerender(
|
|
451
469
|
<ReferencesPanel
|
|
452
|
-
{...
|
|
470
|
+
{...panelProps()}
|
|
453
471
|
isAssisting={false}
|
|
454
472
|
progress={null}
|
|
455
473
|
/>
|
|
@@ -462,7 +480,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
462
480
|
it('should show found counts in log', () => {
|
|
463
481
|
const { rerender } = renderWithEventBus(
|
|
464
482
|
<ReferencesPanel
|
|
465
|
-
{...
|
|
483
|
+
{...panelProps()}
|
|
466
484
|
isAssisting={false}
|
|
467
485
|
progress={{
|
|
468
486
|
stage: 'complete',
|
|
@@ -474,7 +492,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
474
492
|
);
|
|
475
493
|
|
|
476
494
|
rerender(
|
|
477
|
-
<ReferencesPanel {...
|
|
495
|
+
<ReferencesPanel {...panelProps()} isAssisting={false} progress={null} />
|
|
478
496
|
);
|
|
479
497
|
expect(screen.getByText(/Found.*5/i)).toBeInTheDocument();
|
|
480
498
|
});
|
|
@@ -482,7 +500,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
482
500
|
it('should show checkmarks for completed types', () => {
|
|
483
501
|
const { rerender } = renderWithEventBus(
|
|
484
502
|
<ReferencesPanel
|
|
485
|
-
{...
|
|
503
|
+
{...panelProps()}
|
|
486
504
|
isAssisting={false}
|
|
487
505
|
progress={{
|
|
488
506
|
stage: 'complete',
|
|
@@ -494,7 +512,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
494
512
|
);
|
|
495
513
|
|
|
496
514
|
rerender(
|
|
497
|
-
<ReferencesPanel {...
|
|
515
|
+
<ReferencesPanel {...panelProps()} isAssisting={false} progress={null} />
|
|
498
516
|
);
|
|
499
517
|
expect(screen.getByText('✓')).toBeInTheDocument();
|
|
500
518
|
});
|
|
@@ -502,7 +520,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
502
520
|
it('should show detection log and selection UI together after completion', () => {
|
|
503
521
|
const { rerender } = renderWithEventBus(
|
|
504
522
|
<ReferencesPanel
|
|
505
|
-
{...
|
|
523
|
+
{...panelProps()}
|
|
506
524
|
isAssisting={false}
|
|
507
525
|
progress={{
|
|
508
526
|
stage: 'complete',
|
|
@@ -514,7 +532,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
514
532
|
);
|
|
515
533
|
|
|
516
534
|
rerender(
|
|
517
|
-
<ReferencesPanel {...
|
|
535
|
+
<ReferencesPanel {...panelProps()} isAssisting={false} progress={null} />
|
|
518
536
|
);
|
|
519
537
|
|
|
520
538
|
// Should show both the completed log AND the selection UI
|
|
@@ -525,7 +543,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
525
543
|
it('should show selection UI immediately after detection completes', async () => {
|
|
526
544
|
const { rerender } = renderWithEventBus(
|
|
527
545
|
<ReferencesPanel
|
|
528
|
-
{...
|
|
546
|
+
{...panelProps()}
|
|
529
547
|
isAssisting={false}
|
|
530
548
|
progress={{
|
|
531
549
|
stage: 'complete',
|
|
@@ -537,7 +555,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
537
555
|
);
|
|
538
556
|
|
|
539
557
|
rerender(
|
|
540
|
-
<ReferencesPanel {...
|
|
558
|
+
<ReferencesPanel {...panelProps()} isAssisting={false} progress={null} />
|
|
541
559
|
);
|
|
542
560
|
|
|
543
561
|
// Selection UI should be immediately available (no button click needed)
|
|
@@ -548,7 +566,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
548
566
|
it('should not show log when empty', () => {
|
|
549
567
|
renderWithEventBus(
|
|
550
568
|
<ReferencesPanel
|
|
551
|
-
{...
|
|
569
|
+
{...panelProps()}
|
|
552
570
|
isAssisting={false}
|
|
553
571
|
progress={{
|
|
554
572
|
stage: 'complete',
|
|
@@ -567,7 +585,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
567
585
|
|
|
568
586
|
describe('State Transitions', () => {
|
|
569
587
|
it('should transition from idle to detecting', () => {
|
|
570
|
-
const { rerender } = renderWithEventBus(<ReferencesPanel {...
|
|
588
|
+
const { rerender } = renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
571
589
|
|
|
572
590
|
// Idle state
|
|
573
591
|
expect(screen.getByText('Select entity types')).toBeInTheDocument();
|
|
@@ -575,7 +593,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
575
593
|
// Start detecting
|
|
576
594
|
rerender(
|
|
577
595
|
<ReferencesPanel
|
|
578
|
-
{...
|
|
596
|
+
{...panelProps()}
|
|
579
597
|
isAssisting={true}
|
|
580
598
|
progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
|
|
581
599
|
/>
|
|
@@ -589,7 +607,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
589
607
|
it('should transition from detecting to complete', () => {
|
|
590
608
|
const { rerender } = renderWithEventBus(
|
|
591
609
|
<ReferencesPanel
|
|
592
|
-
{...
|
|
610
|
+
{...panelProps()}
|
|
593
611
|
isAssisting={true}
|
|
594
612
|
progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
|
|
595
613
|
/>
|
|
@@ -601,7 +619,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
601
619
|
// Complete - first trigger useEffect to copy to lastDetectionLog
|
|
602
620
|
rerender(
|
|
603
621
|
<ReferencesPanel
|
|
604
|
-
{...
|
|
622
|
+
{...panelProps()}
|
|
605
623
|
isAssisting={false}
|
|
606
624
|
progress={{
|
|
607
625
|
stage: 'complete',
|
|
@@ -614,7 +632,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
614
632
|
|
|
615
633
|
// Then clear progress to show the log
|
|
616
634
|
rerender(
|
|
617
|
-
<ReferencesPanel {...
|
|
635
|
+
<ReferencesPanel {...panelProps()} isAssisting={false} progress={null} />
|
|
618
636
|
);
|
|
619
637
|
|
|
620
638
|
expect(screen.queryByTestId('annotation-progress-widget')).not.toBeInTheDocument();
|
|
@@ -626,7 +644,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
626
644
|
it('should show selection UI after detection completes', async () => {
|
|
627
645
|
const { rerender } = renderWithEventBus(
|
|
628
646
|
<ReferencesPanel
|
|
629
|
-
{...
|
|
647
|
+
{...panelProps()}
|
|
630
648
|
isAssisting={false}
|
|
631
649
|
progress={{
|
|
632
650
|
stage: 'complete',
|
|
@@ -639,14 +657,14 @@ describe('ReferencesPanel Component', () => {
|
|
|
639
657
|
|
|
640
658
|
// Clear progress to show the log
|
|
641
659
|
rerender(
|
|
642
|
-
<ReferencesPanel {...
|
|
660
|
+
<ReferencesPanel {...panelProps()} isAssisting={false} progress={null} />
|
|
643
661
|
);
|
|
644
662
|
|
|
645
663
|
// Selection UI should be immediately available
|
|
646
664
|
expect(screen.getByText('Select entity types')).toBeInTheDocument();
|
|
647
665
|
|
|
648
666
|
rerender(
|
|
649
|
-
<ReferencesPanel {...
|
|
667
|
+
<ReferencesPanel {...panelProps()} />
|
|
650
668
|
);
|
|
651
669
|
|
|
652
670
|
expect(screen.getByText('Select entity types')).toBeInTheDocument();
|
|
@@ -656,7 +674,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
656
674
|
describe('Edge Cases', () => {
|
|
657
675
|
it('should handle empty entity types array', () => {
|
|
658
676
|
expect(() => {
|
|
659
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
677
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} allEntityTypes={[]} />);
|
|
660
678
|
}).not.toThrow();
|
|
661
679
|
});
|
|
662
680
|
|
|
@@ -664,7 +682,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
664
682
|
const manyTypes = Array.from({ length: 50 }, (_, i) => `Type${i}`);
|
|
665
683
|
|
|
666
684
|
expect(() => {
|
|
667
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
685
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} allEntityTypes={manyTypes} />);
|
|
668
686
|
}).not.toThrow();
|
|
669
687
|
|
|
670
688
|
expect(screen.getByText('Type0')).toBeInTheDocument();
|
|
@@ -674,7 +692,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
674
692
|
it('should handle entity types with special characters', () => {
|
|
675
693
|
const specialTypes = ['Type-A', 'Type_B', 'Type.C', 'Type/D'];
|
|
676
694
|
|
|
677
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
695
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} allEntityTypes={specialTypes} />);
|
|
678
696
|
|
|
679
697
|
specialTypes.forEach(type => {
|
|
680
698
|
expect(screen.getByText(type)).toBeInTheDocument();
|
|
@@ -682,7 +700,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
682
700
|
});
|
|
683
701
|
|
|
684
702
|
it('should handle selecting and deselecting all types', async () => {
|
|
685
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
703
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
686
704
|
|
|
687
705
|
// Select all
|
|
688
706
|
for (const type of defaultProps.allEntityTypes) {
|
|
@@ -704,7 +722,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
704
722
|
});
|
|
705
723
|
|
|
706
724
|
it('should handle rapid selection changes', async () => {
|
|
707
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
725
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
708
726
|
|
|
709
727
|
const personButton = screen.getByText('Person');
|
|
710
728
|
|
|
@@ -720,7 +738,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
720
738
|
it('should handle zero found count in results', () => {
|
|
721
739
|
renderWithEventBus(
|
|
722
740
|
<ReferencesPanel
|
|
723
|
-
{...
|
|
741
|
+
{...panelProps()}
|
|
724
742
|
isAssisting={false}
|
|
725
743
|
progress={{
|
|
726
744
|
stage: 'complete',
|
|
@@ -738,7 +756,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
738
756
|
expect(() => {
|
|
739
757
|
renderWithEventBus(
|
|
740
758
|
<ReferencesPanel
|
|
741
|
-
{...
|
|
759
|
+
{...panelProps()}
|
|
742
760
|
isAssisting={false}
|
|
743
761
|
progress={undefined as any}
|
|
744
762
|
/>
|
|
@@ -749,21 +767,21 @@ describe('ReferencesPanel Component', () => {
|
|
|
749
767
|
|
|
750
768
|
describe('Styling and Appearance', () => {
|
|
751
769
|
it('should have proper panel structure', () => {
|
|
752
|
-
const { container } = renderWithEventBus(<ReferencesPanel {...
|
|
770
|
+
const { container } = renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
753
771
|
|
|
754
772
|
const panel = container.firstChild as HTMLElement;
|
|
755
773
|
expect(panel).toHaveClass('semiont-panel');
|
|
756
774
|
});
|
|
757
775
|
|
|
758
776
|
it('should support dark mode', () => {
|
|
759
|
-
const { container } = renderWithEventBus(<ReferencesPanel {...
|
|
777
|
+
const { container } = renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
760
778
|
|
|
761
779
|
const panel = container.firstChild as HTMLElement;
|
|
762
780
|
expect(panel).toHaveClass('semiont-panel');
|
|
763
781
|
});
|
|
764
782
|
|
|
765
783
|
it('should have title without emoji', () => {
|
|
766
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
784
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
767
785
|
|
|
768
786
|
// The emoji is no longer in the title (it's only in the tab now)
|
|
769
787
|
const title = screen.getByRole('heading', { level: 2 });
|
|
@@ -772,7 +790,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
772
790
|
});
|
|
773
791
|
|
|
774
792
|
it('should have proper button layout', () => {
|
|
775
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
793
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
776
794
|
|
|
777
795
|
const buttonContainer = screen.getByText('Person').parentElement;
|
|
778
796
|
expect(buttonContainer).toHaveClass('semiont-assist-widget__chips');
|
|
@@ -781,7 +799,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
781
799
|
|
|
782
800
|
describe('Accessibility', () => {
|
|
783
801
|
it('should have proper ARIA labels for selection', async () => {
|
|
784
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
802
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
785
803
|
|
|
786
804
|
const personButton = screen.getByText('Person');
|
|
787
805
|
|
|
@@ -795,7 +813,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
795
813
|
});
|
|
796
814
|
|
|
797
815
|
it('should have proper ARIA pressed states', async () => {
|
|
798
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
816
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
799
817
|
|
|
800
818
|
const personButton = screen.getByText('Person');
|
|
801
819
|
|
|
@@ -807,7 +825,7 @@ describe('ReferencesPanel Component', () => {
|
|
|
807
825
|
});
|
|
808
826
|
|
|
809
827
|
it('should be keyboard navigable', () => {
|
|
810
|
-
renderWithEventBus(<ReferencesPanel {...
|
|
828
|
+
renderWithEventBus(<ReferencesPanel {...panelProps()} />);
|
|
811
829
|
|
|
812
830
|
const personButton = screen.getByText('Person');
|
|
813
831
|
personButton.focus();
|
|
@@ -3,7 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
4
4
|
import '@testing-library/jest-dom';
|
|
5
5
|
import type { EventBus } from '@semiont/core';
|
|
6
|
-
import type { SemiontClient } from '@semiont/sdk';
|
|
6
|
+
import type { SemiontClient, SemiontSession } from '@semiont/sdk';
|
|
7
7
|
import { ResourceInfoPanel } from '../ResourceInfoPanel';
|
|
8
8
|
import { createTestSemiontWrapper } from '../../../../test-utils';
|
|
9
9
|
|
|
@@ -87,13 +87,15 @@ function createEventTracker() {
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const renderWithEventBus = (component: React.ReactElement
|
|
91
|
-
const { SemiontWrapper, eventBus, client } = createTestSemiontWrapper();
|
|
90
|
+
const renderWithEventBus = (component: React.ReactElement<{ session: SemiontSession | null }>, tracker?: ReturnType<typeof createEventTracker>) => {
|
|
91
|
+
const { SemiontWrapper, eventBus, client, session } = createTestSemiontWrapper();
|
|
92
92
|
if (tracker) tracker._attach(eventBus, client);
|
|
93
93
|
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
94
94
|
<SemiontWrapper>{children}</SemiontWrapper>
|
|
95
95
|
);
|
|
96
|
-
|
|
96
|
+
// The component is provider-free: inject the factory session so it is the
|
|
97
|
+
// SAME session whose client the tracker spies on / whose bus it subscribes.
|
|
98
|
+
return render(React.cloneElement(component, { session }), { wrapper: Wrapper });
|
|
97
99
|
};
|
|
98
100
|
|
|
99
101
|
describe('ResourceInfoPanel Component', () => {
|
|
@@ -103,6 +105,11 @@ describe('ResourceInfoPanel Component', () => {
|
|
|
103
105
|
documentLocale: undefined,
|
|
104
106
|
primaryMediaType: undefined,
|
|
105
107
|
primaryByteSize: undefined,
|
|
108
|
+
// Satisfies JSX at element construction only; renderWithEventBus always
|
|
109
|
+
// replaces it (via cloneElement) with the per-test factory session. Never
|
|
110
|
+
// put a live session here — module-scope clients get disposed after the
|
|
111
|
+
// first test.
|
|
112
|
+
session: null,
|
|
106
113
|
};
|
|
107
114
|
|
|
108
115
|
beforeEach(() => {
|