@semiont/react-ui 0.2.33-build.79 → 0.2.33-build.81
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/EventBusContext-CJjL_cCf.d.mts +462 -0
- package/dist/{PdfAnnotationCanvas.client-ADC4FFSE.mjs → PdfAnnotationCanvas.client-RAJRPQLU.mjs} +42 -27
- package/dist/PdfAnnotationCanvas.client-RAJRPQLU.mjs.map +1 -0
- package/dist/{ar-EMHEHPCJ.mjs → ar-4ZEORRW2.mjs} +7 -4
- package/dist/ar-4ZEORRW2.mjs.map +1 -0
- package/dist/{bn-OVCI4F6X.mjs → bn-SEDE5BQJ.mjs} +7 -4
- package/dist/bn-SEDE5BQJ.mjs.map +1 -0
- package/dist/{chunk-LIHZTECW.mjs → chunk-D7NBW4RV.mjs} +7 -4
- package/dist/chunk-D7NBW4RV.mjs.map +1 -0
- package/dist/{chunk-JZIO2A3B.mjs → chunk-QB52Q7EQ.mjs} +206 -146
- package/dist/chunk-QB52Q7EQ.mjs.map +1 -0
- package/dist/{cs-FAN66Q2F.mjs → cs-7W4WF5WD.mjs} +7 -4
- package/dist/cs-7W4WF5WD.mjs.map +1 -0
- package/dist/{da-YBBIHI2O.mjs → da-75XGBCBK.mjs} +7 -4
- package/dist/da-75XGBCBK.mjs.map +1 -0
- package/dist/{de-MAYU33LB.mjs → de-ODJVFLHM.mjs} +7 -4
- package/dist/de-ODJVFLHM.mjs.map +1 -0
- package/dist/{el-MKGSWN4O.mjs → el-C4PM4WB3.mjs} +7 -4
- package/dist/el-C4PM4WB3.mjs.map +1 -0
- package/dist/{en-DDLIXJCU.mjs → en-KJCJQ4OO.mjs} +2 -2
- package/dist/{es-52LHUWJD.mjs → es-WD33R7QL.mjs} +7 -4
- package/dist/es-WD33R7QL.mjs.map +1 -0
- package/dist/{fa-FJICRANB.mjs → fa-2BP6V56P.mjs} +7 -4
- package/dist/fa-2BP6V56P.mjs.map +1 -0
- package/dist/{fi-O455XFCR.mjs → fi-USRRW24J.mjs} +7 -4
- package/dist/fi-USRRW24J.mjs.map +1 -0
- package/dist/{fr-TXIXHOOE.mjs → fr-EC5S6WVF.mjs} +7 -4
- package/dist/fr-EC5S6WVF.mjs.map +1 -0
- package/dist/{he-JBSOX5IN.mjs → he-7TBVIKAA.mjs} +7 -4
- package/dist/he-7TBVIKAA.mjs.map +1 -0
- package/dist/{hi-KGHI3XVT.mjs → hi-FO4VIZLA.mjs} +7 -4
- package/dist/hi-FO4VIZLA.mjs.map +1 -0
- package/dist/{id-5OCPPZLO.mjs → id-7U7GGVWY.mjs} +7 -4
- package/dist/id-7U7GGVWY.mjs.map +1 -0
- package/dist/index.css +123 -85
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +715 -574
- package/dist/index.mjs +3898 -3575
- package/dist/index.mjs.map +1 -1
- package/dist/{it-PNBBZSM2.mjs → it-Y4OPL6I2.mjs} +7 -4
- package/dist/it-Y4OPL6I2.mjs.map +1 -0
- package/dist/{ja-LDD7R3TJ.mjs → ja-PK7SQL55.mjs} +7 -4
- package/dist/ja-PK7SQL55.mjs.map +1 -0
- package/dist/{ko-F47ZDEY3.mjs → ko-L25PXMYD.mjs} +7 -4
- package/dist/ko-L25PXMYD.mjs.map +1 -0
- package/dist/{ms-Z7LMXJWL.mjs → ms-STH777QM.mjs} +7 -4
- package/dist/ms-STH777QM.mjs.map +1 -0
- package/dist/{nl-6SJFBPJ3.mjs → nl-Y7LECDDR.mjs} +7 -4
- package/dist/nl-Y7LECDDR.mjs.map +1 -0
- package/dist/{no-YXPBPSGF.mjs → no-KEKCEWU6.mjs} +7 -4
- package/dist/no-KEKCEWU6.mjs.map +1 -0
- package/dist/{pl-P4AZ2QME.mjs → pl-7A7OC75O.mjs} +7 -4
- package/dist/pl-7A7OC75O.mjs.map +1 -0
- package/dist/{pt-LHWUS6U6.mjs → pt-35HTM7RA.mjs} +7 -4
- package/dist/pt-35HTM7RA.mjs.map +1 -0
- package/dist/{ro-EA5J2ZON.mjs → ro-VAWL5KQA.mjs} +7 -4
- package/dist/ro-VAWL5KQA.mjs.map +1 -0
- package/dist/{sv-DATBS3UQ.mjs → sv-7ZK5EQEB.mjs} +7 -4
- package/dist/sv-7ZK5EQEB.mjs.map +1 -0
- package/dist/test-utils.d.mts +18 -8
- package/dist/test-utils.mjs +36 -14
- package/dist/test-utils.mjs.map +1 -1
- package/dist/{th-WTFJRWPT.mjs → th-UDWZ4X34.mjs} +7 -4
- package/dist/th-UDWZ4X34.mjs.map +1 -0
- package/dist/{tr-IKO3RXOX.mjs → tr-4WMPK3UX.mjs} +7 -4
- package/dist/tr-4WMPK3UX.mjs.map +1 -0
- package/dist/{uk-CF6CTTRK.mjs → uk-SSLASQYJ.mjs} +7 -4
- package/dist/uk-SSLASQYJ.mjs.map +1 -0
- package/dist/{vi-AJLTXPZQ.mjs → vi-IF42Z5PU.mjs} +7 -4
- package/dist/vi-IF42Z5PU.mjs.map +1 -0
- package/dist/{zh-U3ORHHYH.mjs → zh-HRQTNTAI.mjs} +7 -4
- package/dist/zh-HRQTNTAI.mjs.map +1 -0
- package/package.json +3 -1
- package/src/components/CodeMirrorRenderer.tsx +66 -93
- package/src/components/DetectionProgressWidget.tsx +16 -5
- package/src/components/ResizeHandle.tsx +10 -4
- package/src/components/SessionExpiryBanner.tsx +2 -3
- package/src/components/SessionTimer.tsx +3 -3
- package/src/components/Toolbar.tsx +18 -9
- package/src/components/__tests__/SessionTimer.test.tsx +33 -33
- package/src/components/annotation/AnnotateToolbar.tsx +17 -15
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +165 -63
- package/src/components/annotation/annotation-entries.css +10 -0
- package/src/components/annotation-popups/JsonLdView.tsx +8 -2
- package/src/components/image-annotation/AnnotationOverlay.tsx +42 -22
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +27 -30
- package/src/components/layout/__tests__/LeftSidebar.test.tsx +12 -33
- package/src/components/layout/__tests__/PageLayout.test.tsx +37 -32
- package/src/components/layout/__tests__/UnifiedHeader.test.tsx +21 -40
- package/src/components/modals/ResourceSearchModal.tsx +2 -2
- package/src/components/modals/SearchModal.tsx +1 -1
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +14 -9
- package/src/components/navigation/NavigationTabs.css +36 -24
- package/src/components/navigation/ObservableLink.tsx +91 -0
- package/src/components/navigation/SimpleNavigation.tsx +20 -16
- package/src/components/navigation/SortableResourceTab.tsx +11 -5
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +51 -26
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +28 -22
- package/src/components/resource/AnnotateView.tsx +64 -134
- package/src/components/resource/BrowseView.tsx +86 -166
- package/src/components/resource/HistoryEvent.tsx +13 -7
- package/src/components/resource/ResourceViewer.tsx +122 -264
- package/src/components/resource/__tests__/BrowseView.test.tsx +631 -0
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +231 -0
- package/src/components/resource/panels/AssessmentEntry.tsx +25 -33
- package/src/components/resource/panels/AssessmentPanel.tsx +106 -28
- package/src/components/resource/panels/CommentEntry.tsx +38 -32
- package/src/components/resource/panels/CommentsPanel.tsx +121 -28
- package/src/components/resource/panels/DetectSection.css +36 -1
- package/src/components/resource/panels/DetectSection.tsx +49 -15
- package/src/components/resource/panels/HighlightEntry.tsx +25 -33
- package/src/components/resource/panels/HighlightPanel.tsx +100 -25
- package/src/components/resource/panels/ReferenceEntry.tsx +61 -75
- package/src/components/resource/panels/ReferencesPanel.tsx +134 -42
- package/src/components/resource/panels/ResourceInfoPanel.tsx +47 -48
- package/src/components/resource/panels/TagEntry.tsx +25 -33
- package/src/components/resource/panels/TaggingPanel.tsx +118 -30
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +30 -92
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +129 -110
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +86 -78
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +144 -149
- package/src/components/resource/panels/__tests__/DetectSection.test.tsx +480 -0
- package/src/components/resource/panels/__tests__/HighlightPanel.detectionProgress.test.tsx +362 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +226 -111
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +117 -61
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +128 -106
- package/src/components/settings/SettingsPanel.tsx +15 -12
- package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +1 -46
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +0 -9
- package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +0 -3
- package/src/features/admin-security/components/AdminSecurityPage.tsx +0 -9
- package/src/features/admin-users/__tests__/AdminUsersPage.test.tsx +0 -3
- package/src/features/admin-users/components/AdminUsersPage.tsx +0 -9
- package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +0 -3
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -9
- package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +0 -32
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -9
- package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +0 -32
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -9
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +51 -54
- package/src/features/resource-compose/components/ResourceComposePage.tsx +3 -13
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +39 -45
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +9 -13
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +234 -0
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +234 -0
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +388 -0
- package/src/features/resource-viewer/__tests__/DetectionProgressDismissal.test.tsx +318 -0
- package/src/features/resource-viewer/__tests__/GenerationFlowIntegration.test.tsx +503 -0
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +139 -93
- package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +322 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +341 -524
- package/translations/ar.json +6 -3
- package/translations/bn.json +6 -3
- package/translations/cs.json +6 -3
- package/translations/da.json +6 -3
- package/translations/de.json +6 -3
- package/translations/el.json +6 -3
- package/translations/en.json +6 -3
- package/translations/es.json +6 -3
- package/translations/fa.json +6 -3
- package/translations/fi.json +6 -3
- package/translations/fr.json +6 -3
- package/translations/he.json +6 -3
- package/translations/hi.json +6 -3
- package/translations/id.json +6 -3
- package/translations/it.json +6 -3
- package/translations/ja.json +6 -3
- package/translations/ko.json +6 -3
- package/translations/ms.json +6 -3
- package/translations/nl.json +6 -3
- package/translations/no.json +6 -3
- package/translations/pl.json +6 -3
- package/translations/pt.json +6 -3
- package/translations/ro.json +6 -3
- package/translations/sv.json +6 -3
- package/translations/th.json +6 -3
- package/translations/tr.json +6 -3
- package/translations/uk.json +6 -3
- package/translations/vi.json +6 -3
- package/translations/zh.json +6 -3
- package/dist/PdfAnnotationCanvas.client-ADC4FFSE.mjs.map +0 -1
- package/dist/TranslationManager-Co_5fSxl.d.mts +0 -118
- package/dist/ar-EMHEHPCJ.mjs.map +0 -1
- package/dist/bn-OVCI4F6X.mjs.map +0 -1
- package/dist/chunk-JZIO2A3B.mjs.map +0 -1
- package/dist/chunk-LIHZTECW.mjs.map +0 -1
- package/dist/cs-FAN66Q2F.mjs.map +0 -1
- package/dist/da-YBBIHI2O.mjs.map +0 -1
- package/dist/de-MAYU33LB.mjs.map +0 -1
- package/dist/el-MKGSWN4O.mjs.map +0 -1
- package/dist/es-52LHUWJD.mjs.map +0 -1
- package/dist/fa-FJICRANB.mjs.map +0 -1
- package/dist/fi-O455XFCR.mjs.map +0 -1
- package/dist/fr-TXIXHOOE.mjs.map +0 -1
- package/dist/he-JBSOX5IN.mjs.map +0 -1
- package/dist/hi-KGHI3XVT.mjs.map +0 -1
- package/dist/id-5OCPPZLO.mjs.map +0 -1
- package/dist/it-PNBBZSM2.mjs.map +0 -1
- package/dist/ja-LDD7R3TJ.mjs.map +0 -1
- package/dist/ko-F47ZDEY3.mjs.map +0 -1
- package/dist/ms-Z7LMXJWL.mjs.map +0 -1
- package/dist/nl-6SJFBPJ3.mjs.map +0 -1
- package/dist/no-YXPBPSGF.mjs.map +0 -1
- package/dist/pl-P4AZ2QME.mjs.map +0 -1
- package/dist/pt-LHWUS6U6.mjs.map +0 -1
- package/dist/ro-EA5J2ZON.mjs.map +0 -1
- package/dist/sv-DATBS3UQ.mjs.map +0 -1
- package/dist/th-WTFJRWPT.mjs.map +0 -1
- package/dist/tr-IKO3RXOX.mjs.map +0 -1
- package/dist/uk-CF6CTTRK.mjs.map +0 -1
- package/dist/vi-AJLTXPZQ.mjs.map +0 -1
- package/dist/zh-U3ORHHYH.mjs.map +0 -1
- /package/dist/{en-DDLIXJCU.mjs.map → en-KJCJQ4OO.mjs.map} +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 3 Integration Test: Detection Progress Flow UI/UX
|
|
3
|
+
*
|
|
4
|
+
* Tests the complete data flow from UI → EventBus → useResolutionFlow → SSE (mocked)
|
|
5
|
+
*
|
|
6
|
+
* This test uses COMPOSITION instead of mocking:
|
|
7
|
+
* - Real React components composed together (useDetectionFlow + HighlightPanel + DetectSection)
|
|
8
|
+
* - Real EventBus (mitt) passed via context
|
|
9
|
+
* - Real useResolutionFlow hook with mock API client passed as prop
|
|
10
|
+
* - Mock SSE stream (simulated API responses) provided via composition
|
|
11
|
+
*
|
|
12
|
+
* This test focuses on USER EXPERIENCE:
|
|
13
|
+
* - Verifies user clicks "Detect" button and sees progress
|
|
14
|
+
* - Tests progress messages appear and update correctly
|
|
15
|
+
* - Validates final message stays visible after completion
|
|
16
|
+
* - Ensures progress clears on error
|
|
17
|
+
*
|
|
18
|
+
* COMPLEMENTARY TEST: See DetectionFlowIntegration.test.tsx for architecture testing
|
|
19
|
+
* - That test verifies SYSTEM ARCHITECTURE (event wiring, API call count)
|
|
20
|
+
* - This test verifies USER EXPERIENCE (button clicks, UI feedback)
|
|
21
|
+
*
|
|
22
|
+
* UPDATED: Now tests useDetectionFlow hook instead of DetectionFlowContainer
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
26
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
27
|
+
import userEvent from '@testing-library/user-event';
|
|
28
|
+
import { act } from 'react';
|
|
29
|
+
import { HighlightPanel } from '../../../components/resource/panels/HighlightPanel';
|
|
30
|
+
import { useDetectionFlow } from '../../../hooks/useDetectionFlow';
|
|
31
|
+
import { EventBusProvider, resetEventBusForTesting } from '../../../contexts/EventBusContext';
|
|
32
|
+
import { ApiClientProvider } from '../../../contexts/ApiClientContext';
|
|
33
|
+
import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
|
|
34
|
+
import { SSEClient } from '@semiont/api-client';
|
|
35
|
+
import type { components } from '@semiont/api-client';
|
|
36
|
+
|
|
37
|
+
type Annotation = components['schemas']['Annotation'];
|
|
38
|
+
|
|
39
|
+
// Mock translations
|
|
40
|
+
const mockT = vi.fn((key: string) => {
|
|
41
|
+
const translations: Record<string, string> = {
|
|
42
|
+
title: 'Highlights',
|
|
43
|
+
noHighlights: 'No highlights yet',
|
|
44
|
+
detectHighlights: 'Detect Highlights',
|
|
45
|
+
instructions: 'Instructions',
|
|
46
|
+
optional: '(optional)',
|
|
47
|
+
instructionsPlaceholder: 'Enter custom instructions...',
|
|
48
|
+
densityLabel: 'Density',
|
|
49
|
+
densitySparse: 'Sparse',
|
|
50
|
+
densityDense: 'Dense',
|
|
51
|
+
detect: 'Detect',
|
|
52
|
+
};
|
|
53
|
+
return translations[key] || key;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
vi.mock('../../../contexts/TranslationContext', () => ({
|
|
57
|
+
useTranslations: () => mockT,
|
|
58
|
+
TranslationProvider: ({ children }: { children: React.ReactNode }) => children,
|
|
59
|
+
}));
|
|
60
|
+
|
|
61
|
+
// Create a mock SSE stream that we can control
|
|
62
|
+
class MockSSEStream {
|
|
63
|
+
private progressHandlers: Array<(chunk: any) => void> = [];
|
|
64
|
+
private completeHandlers: Array<(finalChunk?: any) => void> = [];
|
|
65
|
+
private errorHandlers: Array<(error: Error) => void> = [];
|
|
66
|
+
|
|
67
|
+
onProgress(handler: (chunk: any) => void) {
|
|
68
|
+
this.progressHandlers.push(handler);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
onComplete(handler: (finalChunk?: any) => void) {
|
|
72
|
+
this.completeHandlers.push(handler);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onError(handler: (error: Error) => void) {
|
|
76
|
+
this.errorHandlers.push(handler);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Test helper methods
|
|
80
|
+
emitProgress(chunk: any) {
|
|
81
|
+
this.progressHandlers.forEach(handler => handler(chunk));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
emitComplete(finalChunk?: any) {
|
|
85
|
+
this.completeHandlers.forEach(handler => handler(finalChunk));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
emitError(error: Error) {
|
|
89
|
+
this.errorHandlers.forEach(handler => handler(error));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Composition: Test component that wires together the pieces we're testing
|
|
94
|
+
function DetectionFlowTestHarness({
|
|
95
|
+
rUri,
|
|
96
|
+
annotations,
|
|
97
|
+
}: {
|
|
98
|
+
rUri: string;
|
|
99
|
+
annotations: Annotation[];
|
|
100
|
+
}) {
|
|
101
|
+
const { detectingMotivation, detectionProgress } = useDetectionFlow(rUri as any);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<HighlightPanel
|
|
105
|
+
annotations={annotations}
|
|
106
|
+
pendingAnnotation={null}
|
|
107
|
+
hoveredAnnotationId={null}
|
|
108
|
+
isDetecting={detectingMotivation === 'highlighting'}
|
|
109
|
+
detectionProgress={detectionProgress}
|
|
110
|
+
annotateMode={true}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
describe('Detection Progress Flow Integration (Layer 3)', () => {
|
|
116
|
+
let mockAnnotations: Annotation[];
|
|
117
|
+
let mockStream: MockSSEStream;
|
|
118
|
+
const rUri = 'https://example.com/resources/test-resource-1';
|
|
119
|
+
|
|
120
|
+
// Helper to render test harness with composition
|
|
121
|
+
const renderDetectionFlow = () => {
|
|
122
|
+
return render(
|
|
123
|
+
<EventBusProvider>
|
|
124
|
+
<AuthTokenProvider token={null}>
|
|
125
|
+
<ApiClientProvider baseUrl="http://localhost:4000">
|
|
126
|
+
<DetectionFlowTestHarness
|
|
127
|
+
rUri={rUri}
|
|
128
|
+
annotations={mockAnnotations}
|
|
129
|
+
/>
|
|
130
|
+
</ApiClientProvider>
|
|
131
|
+
</AuthTokenProvider>
|
|
132
|
+
</EventBusProvider>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
beforeEach(() => {
|
|
137
|
+
// Reset event bus for test isolation
|
|
138
|
+
resetEventBusForTesting();
|
|
139
|
+
vi.clearAllMocks();
|
|
140
|
+
|
|
141
|
+
// Reset mocks
|
|
142
|
+
mockStream = new MockSSEStream();
|
|
143
|
+
|
|
144
|
+
// Spy on SSEClient prototype methods to inject mock stream
|
|
145
|
+
vi.spyOn(SSEClient.prototype, 'detectHighlights').mockReturnValue(mockStream as any);
|
|
146
|
+
vi.spyOn(SSEClient.prototype, 'detectAssessments').mockReturnValue(mockStream as any);
|
|
147
|
+
vi.spyOn(SSEClient.prototype, 'detectComments').mockReturnValue(mockStream as any);
|
|
148
|
+
vi.spyOn(SSEClient.prototype, 'detectReferences').mockReturnValue(mockStream as any);
|
|
149
|
+
|
|
150
|
+
mockAnnotations = [];
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
afterEach(() => {
|
|
154
|
+
vi.restoreAllMocks();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should display detection progress from button click to completion', async () => {
|
|
158
|
+
const user = userEvent.setup();
|
|
159
|
+
|
|
160
|
+
// Render composed components with real EventBus and mock API client
|
|
161
|
+
renderDetectionFlow();
|
|
162
|
+
|
|
163
|
+
// Initial state: no progress visible
|
|
164
|
+
expect(screen.queryByText(/Analyzing/)).not.toBeInTheDocument();
|
|
165
|
+
|
|
166
|
+
// Click detect button
|
|
167
|
+
const detectButton = screen.getByRole('button', { name: /✨ Detect/ });
|
|
168
|
+
await user.click(detectButton);
|
|
169
|
+
|
|
170
|
+
// Simulate SSE progress chunk #1: Starting
|
|
171
|
+
act(() => {
|
|
172
|
+
mockStream.emitProgress({
|
|
173
|
+
status: 'started',
|
|
174
|
+
percentage: 0,
|
|
175
|
+
message: 'Starting detection...',
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Verify progress message appears
|
|
180
|
+
await waitFor(() => {
|
|
181
|
+
expect(screen.getByText('Starting detection...')).toBeInTheDocument();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Simulate SSE progress chunk #2: Analyzing
|
|
185
|
+
act(() => {
|
|
186
|
+
mockStream.emitProgress({
|
|
187
|
+
status: 'analyzing',
|
|
188
|
+
percentage: 30,
|
|
189
|
+
message: 'Analyzing text...',
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await waitFor(() => {
|
|
194
|
+
expect(screen.getByText('Analyzing text...')).toBeInTheDocument();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Simulate SSE progress chunk #3: Creating annotations
|
|
198
|
+
act(() => {
|
|
199
|
+
mockStream.emitProgress({
|
|
200
|
+
status: 'creating',
|
|
201
|
+
percentage: 60,
|
|
202
|
+
message: 'Creating 14 annotations...',
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
await waitFor(() => {
|
|
207
|
+
expect(screen.getByText('Creating 14 annotations...')).toBeInTheDocument();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Simulate stream completion with final chunk (onComplete receives the final progress)
|
|
211
|
+
act(() => {
|
|
212
|
+
mockStream.emitComplete({
|
|
213
|
+
status: 'complete',
|
|
214
|
+
percentage: 100,
|
|
215
|
+
message: 'Complete! Created 14 highlights',
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// CRITICAL TEST: Final message should still be visible after completion
|
|
220
|
+
await waitFor(() => {
|
|
221
|
+
expect(screen.getByText('Complete! Created 14 highlights')).toBeInTheDocument();
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should handle out-of-order SSE events (complete before final progress)', async () => {
|
|
226
|
+
const user = userEvent.setup();
|
|
227
|
+
|
|
228
|
+
renderDetectionFlow();
|
|
229
|
+
|
|
230
|
+
// Click detect
|
|
231
|
+
const detectButton = screen.getByRole('button', { name: /✨ Detect/ });
|
|
232
|
+
await user.click(detectButton);
|
|
233
|
+
|
|
234
|
+
// Simulate initial progress
|
|
235
|
+
act(() => {
|
|
236
|
+
mockStream.emitProgress({
|
|
237
|
+
status: 'analyzing',
|
|
238
|
+
message: 'Analyzing...',
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
await waitFor(() => {
|
|
243
|
+
expect(screen.getByText('Analyzing...')).toBeInTheDocument();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Simulate race condition: complete arrives BEFORE final progress
|
|
247
|
+
act(() => {
|
|
248
|
+
mockStream.emitComplete();
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Then final progress chunk arrives
|
|
252
|
+
act(() => {
|
|
253
|
+
mockStream.emitProgress({
|
|
254
|
+
status: 'complete',
|
|
255
|
+
percentage: 100,
|
|
256
|
+
message: 'Complete! Created 5 highlights',
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Final message should still be visible
|
|
261
|
+
await waitFor(() => {
|
|
262
|
+
expect(screen.getByText('Complete! Created 5 highlights')).toBeInTheDocument();
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should show progress with request parameters', async () => {
|
|
267
|
+
const user = userEvent.setup();
|
|
268
|
+
|
|
269
|
+
renderDetectionFlow();
|
|
270
|
+
|
|
271
|
+
const detectButton = screen.getByRole('button', { name: /✨ Detect/ });
|
|
272
|
+
await user.click(detectButton);
|
|
273
|
+
|
|
274
|
+
// Simulate progress with request parameters
|
|
275
|
+
act(() => {
|
|
276
|
+
mockStream.emitProgress({
|
|
277
|
+
status: 'analyzing',
|
|
278
|
+
message: 'Analyzing with custom instructions...',
|
|
279
|
+
requestParams: [
|
|
280
|
+
{ label: 'Instructions', value: 'Find important points' },
|
|
281
|
+
{ label: 'Density', value: '5' },
|
|
282
|
+
],
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await waitFor(() => {
|
|
287
|
+
expect(screen.getByText('Find important points')).toBeInTheDocument();
|
|
288
|
+
expect(screen.getByText('5')).toBeInTheDocument();
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should clear progress on detection:failed', async () => {
|
|
293
|
+
const user = userEvent.setup();
|
|
294
|
+
|
|
295
|
+
renderDetectionFlow();
|
|
296
|
+
|
|
297
|
+
const detectButton = screen.getByRole('button', { name: /✨ Detect/ });
|
|
298
|
+
await user.click(detectButton);
|
|
299
|
+
|
|
300
|
+
// Show progress
|
|
301
|
+
act(() => {
|
|
302
|
+
mockStream.emitProgress({
|
|
303
|
+
status: 'analyzing',
|
|
304
|
+
message: 'Analyzing...',
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
await waitFor(() => {
|
|
309
|
+
expect(screen.getByText('Analyzing...')).toBeInTheDocument();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Simulate error
|
|
313
|
+
act(() => {
|
|
314
|
+
mockStream.emitError(new Error('Network timeout'));
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Progress should be cleared
|
|
318
|
+
await waitFor(() => {
|
|
319
|
+
expect(screen.queryByText('Analyzing...')).not.toBeInTheDocument();
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
});
|