@semiont/react-ui 0.2.46 → 0.3.0
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-COQREPXU.mjs → PdfAnnotationCanvas.client-PVTVPDBQ.mjs} +3 -4
- package/dist/PdfAnnotationCanvas.client-PVTVPDBQ.mjs.map +1 -0
- package/dist/{ar-7SUXNE34.mjs → ar-APUOG2AP.mjs} +46 -6
- package/dist/ar-APUOG2AP.mjs.map +1 -0
- package/dist/{bn-XOET3DOI.mjs → bn-EFK2LJGK.mjs} +46 -6
- package/dist/bn-EFK2LJGK.mjs.map +1 -0
- package/dist/{chunk-JH7BXE2P.mjs → chunk-7DW2P4UE.mjs} +45 -5
- package/dist/chunk-7DW2P4UE.mjs.map +1 -0
- package/dist/{chunk-Q2KV6Y2J.mjs → chunk-7GEYABC6.mjs} +32 -32
- package/dist/{chunk-3JTO27MH.mjs → chunk-D4GAAQMM.mjs} +2 -9
- package/dist/{cs-X63DXX7L.mjs → cs-A26MEEQE.mjs} +46 -6
- package/dist/cs-A26MEEQE.mjs.map +1 -0
- package/dist/{da-OWTCV57A.mjs → da-U3L2FHSZ.mjs} +46 -6
- package/dist/da-U3L2FHSZ.mjs.map +1 -0
- package/dist/{de-77BMFDVF.mjs → de-Y5BHEBT7.mjs} +46 -6
- package/dist/de-Y5BHEBT7.mjs.map +1 -0
- package/dist/dist-YLEIY3JJ.mjs +547 -0
- package/dist/dist-YLEIY3JJ.mjs.map +1 -0
- package/dist/{el-FIBNLH2V.mjs → el-HU7LAWQY.mjs} +46 -6
- package/dist/el-HU7LAWQY.mjs.map +1 -0
- package/dist/{en-XWEPVTB4.mjs → en-HAKDCFKL.mjs} +5 -3
- package/dist/{es-726NTS53.mjs → es-4BN64QH5.mjs} +46 -6
- package/dist/es-4BN64QH5.mjs.map +1 -0
- package/dist/{fa-3N4CIWE6.mjs → fa-6ELTBARU.mjs} +46 -6
- package/dist/fa-6ELTBARU.mjs.map +1 -0
- package/dist/{fi-JOM3M7Z4.mjs → fi-DJ4WGIFW.mjs} +46 -6
- package/dist/fi-DJ4WGIFW.mjs.map +1 -0
- package/dist/{fr-56QSXS7E.mjs → fr-23XM6H6H.mjs} +46 -6
- package/dist/fr-23XM6H6H.mjs.map +1 -0
- package/dist/{he-SNAXPJEK.mjs → he-JSWJC2XU.mjs} +46 -6
- package/dist/he-JSWJC2XU.mjs.map +1 -0
- package/dist/{hi-CRBRD5TB.mjs → hi-BENHG3OJ.mjs} +46 -6
- package/dist/hi-BENHG3OJ.mjs.map +1 -0
- package/dist/{id-BRCVLICF.mjs → id-4HHQJQNF.mjs} +46 -6
- package/dist/id-4HHQJQNF.mjs.map +1 -0
- package/dist/index.css +108 -12
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +351 -107
- package/dist/index.mjs +3111 -1811
- package/dist/index.mjs.map +1 -1
- package/dist/{it-M2Z27BNB.mjs → it-U6I5PDKU.mjs} +46 -6
- package/dist/it-U6I5PDKU.mjs.map +1 -0
- package/dist/{ja-TZUKW7HD.mjs → ja-K3YBDWDP.mjs} +46 -6
- package/dist/ja-K3YBDWDP.mjs.map +1 -0
- package/dist/{ko-NKBGGOL6.mjs → ko-KC2HXRXG.mjs} +46 -6
- package/dist/ko-KC2HXRXG.mjs.map +1 -0
- package/dist/{magic-string.es-7FJ3LUGB.mjs → magic-string.es-K77I4ZQN.mjs} +2 -2
- package/dist/{ms-XFXPN6RX.mjs → ms-KY5QGBNN.mjs} +46 -6
- package/dist/ms-KY5QGBNN.mjs.map +1 -0
- package/dist/{nl-MVYXAS5C.mjs → nl-6PZFLGY2.mjs} +46 -6
- package/dist/nl-6PZFLGY2.mjs.map +1 -0
- package/dist/{no-XOLO4JPV.mjs → no-5QR7PLVJ.mjs} +46 -6
- package/dist/no-5QR7PLVJ.mjs.map +1 -0
- package/dist/{pl-TRWLMMC4.mjs → pl-4GV2NQXE.mjs} +46 -6
- package/dist/pl-4GV2NQXE.mjs.map +1 -0
- package/dist/{pt-M3TE24UI.mjs → pt-F3F5QD2P.mjs} +46 -6
- package/dist/pt-F3F5QD2P.mjs.map +1 -0
- package/dist/{ro-QBFG2T64.mjs → ro-TFYL2IQB.mjs} +46 -6
- package/dist/ro-TFYL2IQB.mjs.map +1 -0
- package/dist/{sv-IUECBXWX.mjs → sv-PRVF2QLR.mjs} +46 -6
- package/dist/sv-PRVF2QLR.mjs.map +1 -0
- package/dist/test-utils.mjs +16994 -22140
- package/dist/test-utils.mjs.map +1 -1
- package/dist/{th-US7KIN5Q.mjs → th-SUQOQFUZ.mjs} +46 -6
- package/dist/th-SUQOQFUZ.mjs.map +1 -0
- package/dist/{tr-DWJ2FFUK.mjs → tr-AYUJZOFJ.mjs} +46 -6
- package/dist/tr-AYUJZOFJ.mjs.map +1 -0
- package/dist/{uk-M4ZE4DPZ.mjs → uk-YY5WGLBM.mjs} +46 -6
- package/dist/uk-YY5WGLBM.mjs.map +1 -0
- package/dist/{vi-FERZNPSH.mjs → vi-6RO77ITD.mjs} +46 -6
- package/dist/{vi-FERZNPSH.mjs.map → vi-6RO77ITD.mjs.map} +1 -1
- package/dist/{zh-3J2I3WYK.mjs → zh-L6GA65H6.mjs} +46 -6
- package/dist/zh-L6GA65H6.mjs.map +1 -0
- package/package.json +18 -14
- package/src/components/Button/Button.tsx +23 -25
- package/src/components/annotation/AnnotateToolbar.tsx +1 -1
- package/src/components/annotation-popups/SharedPopupElements.tsx +5 -7
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +3 -4
- package/src/components/modals/ConfigureGenerationStep.tsx +190 -0
- package/src/components/modals/ConfigureSearchStep.tsx +105 -0
- package/src/components/modals/ContextSummary.tsx +183 -0
- package/src/components/modals/GatherContextStep.tsx +89 -0
- package/src/components/modals/KeyboardShortcutsHelpModal.tsx +4 -6
- package/src/components/modals/ProposeEntitiesModal.tsx +4 -6
- package/src/components/modals/ReferenceWizardModal.tsx +326 -0
- package/src/components/modals/ResourceSearchModal.tsx +4 -6
- package/src/components/modals/SearchModal.css +43 -0
- package/src/components/modals/SearchModal.tsx +4 -6
- package/src/components/modals/SearchResultsStep.tsx +126 -0
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +3 -4
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +36 -14
- package/src/components/resource/AnnotateView.tsx +4 -4
- package/src/components/resource/AnnotationHistory.tsx +2 -2
- package/src/components/resource/BrowseView.tsx +4 -4
- package/src/components/resource/ResourceViewer.tsx +4 -7
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +16 -16
- package/src/components/resource/__tests__/BrowseView.test.tsx +2 -2
- package/src/components/resource/__tests__/HistoryEvent.test.tsx +1 -1
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +1 -1
- package/src/components/resource/panels/AssessmentEntry.tsx +9 -11
- package/src/components/resource/panels/CommentEntry.tsx +10 -12
- package/src/components/resource/panels/HighlightEntry.tsx +9 -11
- package/src/components/resource/panels/ReferenceEntry.tsx +57 -104
- package/src/components/resource/panels/ReferencesPanel.css +85 -13
- package/src/components/resource/panels/ReferencesPanel.tsx +1 -2
- package/src/components/resource/panels/TagEntry.tsx +9 -11
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +7 -7
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +2 -2
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +64 -101
- package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +3 -3
- package/src/components/viewers/ImageViewer.tsx +3 -6
- package/src/components/viewers/__tests__/ImageViewer.test.tsx +3 -3
- package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +5 -5
- package/src/features/admin-exchange/__tests__/AdminExchangePage.test.tsx +141 -0
- package/src/features/admin-exchange/__tests__/ExportCard.test.tsx +41 -0
- package/src/features/admin-exchange/__tests__/ImportCard.test.tsx +148 -0
- package/src/features/admin-exchange/__tests__/ImportProgress.test.tsx +106 -0
- package/src/features/admin-exchange/components/AdminExchangePage.tsx +120 -0
- package/src/features/admin-exchange/components/ExportCard.tsx +35 -0
- package/src/features/admin-exchange/components/ImportCard.tsx +188 -0
- package/src/features/admin-exchange/components/ImportProgress.tsx +86 -0
- package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +3 -3
- package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +2 -2
- package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +4 -4
- package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +3 -3
- package/src/features/moderation-linked-data/__tests__/LinkedDataPage.test.tsx +117 -0
- package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +121 -0
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +5 -5
- package/src/features/resource-compose/components/ResourceComposePage.tsx +56 -1
- package/src/features/resource-discovery/__tests__/ResourceCard.test.tsx +1 -1
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +2 -2
- package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +3 -3
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +11 -10
- package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +2 -2
- package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +22 -115
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +3 -3
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +20 -20
- package/src/features/resource-viewer/__tests__/ResourceMutations.test.tsx +7 -7
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +5 -21
- package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +2 -2
- package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +45 -82
- package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +4 -4
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +144 -72
- package/src/integrations/tailwind-plugin.js +3 -3
- package/src/styles/core/buttons.css +31 -0
- package/src/styles/features/exchange.css +404 -0
- package/src/styles/index.css +1 -0
- package/translations/ar.json +42 -4
- package/translations/bn.json +42 -4
- package/translations/cs.json +42 -4
- package/translations/da.json +128 -90
- package/translations/de.json +122 -84
- package/translations/el.json +42 -4
- package/translations/en.json +42 -4
- package/translations/es.json +42 -4
- package/translations/fa.json +42 -4
- package/translations/fi.json +68 -30
- package/translations/fr.json +42 -4
- package/translations/he.json +42 -4
- package/translations/hi.json +42 -4
- package/translations/id.json +43 -5
- package/translations/it.json +62 -24
- package/translations/ja.json +43 -5
- package/translations/ko.json +42 -4
- package/translations/ms.json +43 -5
- package/translations/nl.json +41 -3
- package/translations/no.json +104 -66
- package/translations/pl.json +42 -4
- package/translations/pt.json +43 -5
- package/translations/ro.json +42 -4
- package/translations/sv.json +42 -4
- package/translations/th.json +42 -4
- package/translations/tr.json +42 -4
- package/translations/uk.json +42 -4
- package/translations/vi.json +42 -4
- package/translations/zh.json +42 -4
- package/dist/PdfAnnotationCanvas.client-COQREPXU.mjs.map +0 -1
- package/dist/ar-7SUXNE34.mjs.map +0 -1
- package/dist/bn-XOET3DOI.mjs.map +0 -1
- package/dist/chunk-JH7BXE2P.mjs.map +0 -1
- package/dist/cs-X63DXX7L.mjs.map +0 -1
- package/dist/da-OWTCV57A.mjs.map +0 -1
- package/dist/de-77BMFDVF.mjs.map +0 -1
- package/dist/el-FIBNLH2V.mjs.map +0 -1
- package/dist/es-726NTS53.mjs.map +0 -1
- package/dist/fa-3N4CIWE6.mjs.map +0 -1
- package/dist/fi-JOM3M7Z4.mjs.map +0 -1
- package/dist/fr-56QSXS7E.mjs.map +0 -1
- package/dist/he-SNAXPJEK.mjs.map +0 -1
- package/dist/hi-CRBRD5TB.mjs.map +0 -1
- package/dist/id-BRCVLICF.mjs.map +0 -1
- package/dist/it-M2Z27BNB.mjs.map +0 -1
- package/dist/ja-TZUKW7HD.mjs.map +0 -1
- package/dist/ko-NKBGGOL6.mjs.map +0 -1
- package/dist/ms-XFXPN6RX.mjs.map +0 -1
- package/dist/nl-MVYXAS5C.mjs.map +0 -1
- package/dist/no-XOLO4JPV.mjs.map +0 -1
- package/dist/pl-TRWLMMC4.mjs.map +0 -1
- package/dist/pt-M3TE24UI.mjs.map +0 -1
- package/dist/ro-QBFG2T64.mjs.map +0 -1
- package/dist/sv-IUECBXWX.mjs.map +0 -1
- package/dist/th-US7KIN5Q.mjs.map +0 -1
- package/dist/tr-DWJ2FFUK.mjs.map +0 -1
- package/dist/uk-M4ZE4DPZ.mjs.map +0 -1
- package/dist/zh-3J2I3WYK.mjs.map +0 -1
- package/src/examples/ButtonUsageExample.tsx +0 -242
- package/src/examples/button-css-modules.module.css +0 -164
- package/src/examples/button-styled-components.tsx +0 -215
- package/src/examples/button-tailwind.css +0 -51
- /package/dist/{chunk-Q2KV6Y2J.mjs.map → chunk-7GEYABC6.mjs.map} +0 -0
- /package/dist/{chunk-3JTO27MH.mjs.map → chunk-D4GAAQMM.mjs.map} +0 -0
- /package/dist/{en-XWEPVTB4.mjs.map → en-HAKDCFKL.mjs.map} +0 -0
- /package/dist/{magic-string.es-7FJ3LUGB.mjs.map → magic-string.es-K77I4ZQN.mjs.map} +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Layer 3: Feature Integration Test -
|
|
2
|
+
* Layer 3: Feature Integration Test - Bind Flow (body update)
|
|
3
3
|
*
|
|
4
|
-
* Tests the
|
|
5
|
-
* - bind:link → emits bind:search-requested
|
|
6
|
-
* - bind:search-requested → opens search modal with pendingReferenceId
|
|
7
|
-
* - onCloseSearchModal → closes modal
|
|
4
|
+
* Tests the write side of useBindFlow:
|
|
8
5
|
* - bind:update-body → calls updateAnnotationBody API
|
|
9
6
|
* - bind:update-body → emits bind:body-updated on success
|
|
10
7
|
* - bind:update-body → emits bind:body-update-failed on error
|
|
11
8
|
* - auth token passed to updateAnnotationBody
|
|
12
9
|
*
|
|
13
|
-
* The
|
|
10
|
+
* The wizard modal (ReferenceWizardModal) handles modal state, context
|
|
11
|
+
* gathering, search configuration, and result display. This test covers
|
|
12
|
+
* only the downstream API calls after the wizard emits bind:update-body.
|
|
14
13
|
*
|
|
15
14
|
* Uses real providers (EventBus, ApiClient, AuthToken) with mocked API boundary.
|
|
16
15
|
*/
|
|
@@ -23,7 +22,7 @@ import { EventBusProvider, useEventBus, resetEventBusForTesting } from '../../..
|
|
|
23
22
|
import { ApiClientProvider } from '../../../contexts/ApiClientContext';
|
|
24
23
|
import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
|
|
25
24
|
import { SemiontApiClient } from '@semiont/api-client';
|
|
26
|
-
import {
|
|
25
|
+
import { resourceId, accessToken, annotationId } from '@semiont/core';
|
|
27
26
|
|
|
28
27
|
// Mock Toast module to prevent "useToast must be used within a ToastProvider" errors
|
|
29
28
|
vi.mock('../../../components/Toast', () => ({
|
|
@@ -35,9 +34,9 @@ vi.mock('../../../components/Toast', () => ({
|
|
|
35
34
|
}),
|
|
36
35
|
}));
|
|
37
36
|
|
|
38
|
-
describe('
|
|
37
|
+
describe('Bind Flow - Body Update Integration', () => {
|
|
39
38
|
let updateAnnotationBodySpy: ReturnType<typeof vi.fn>;
|
|
40
|
-
const
|
|
39
|
+
const testId = resourceId('test-resource');
|
|
41
40
|
const testToken = 'test-resolution-token';
|
|
42
41
|
const testBaseUrl = 'http://localhost:4000';
|
|
43
42
|
|
|
@@ -57,11 +56,10 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
|
|
|
57
56
|
|
|
58
57
|
function renderBindFlow() {
|
|
59
58
|
let eventBusInstance: ReturnType<typeof useEventBus> | null = null;
|
|
60
|
-
let lastState: ReturnType<typeof useBindFlow> | null = null;
|
|
61
59
|
|
|
62
60
|
function TestComponent() {
|
|
63
61
|
eventBusInstance = useEventBus();
|
|
64
|
-
|
|
62
|
+
useBindFlow(testId);
|
|
65
63
|
return null;
|
|
66
64
|
}
|
|
67
65
|
|
|
@@ -76,109 +74,18 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
|
|
|
76
74
|
);
|
|
77
75
|
|
|
78
76
|
return {
|
|
79
|
-
getState: () => lastState!,
|
|
80
77
|
getEventBus: () => eventBusInstance!,
|
|
81
78
|
};
|
|
82
79
|
}
|
|
83
80
|
|
|
84
|
-
// ─── Initial state ──────────────────────────────────────────────────────────
|
|
85
|
-
|
|
86
|
-
it('starts with search modal closed and no pending reference', () => {
|
|
87
|
-
const { getState } = renderBindFlow();
|
|
88
|
-
expect(getState().searchModalOpen).toBe(false);
|
|
89
|
-
expect(getState().pendingReferenceId).toBeNull();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// ─── bind:link ─────────────────────────────────────────────────────────
|
|
93
|
-
|
|
94
|
-
it('bind:link emits bind:search-requested with referenceId and searchTerm', () => {
|
|
95
|
-
const { getEventBus } = renderBindFlow();
|
|
96
|
-
const searchRequestedSpy = vi.fn();
|
|
97
|
-
|
|
98
|
-
const subscription = getEventBus().get('bind:search-requested').subscribe(searchRequestedSpy);
|
|
99
|
-
act(() => { getEventBus().get('bind:link').next({ annotationUri: 'ann-uri-123', searchTerm: 'climate change' }); });
|
|
100
|
-
subscription.unsubscribe();
|
|
101
|
-
|
|
102
|
-
expect(searchRequestedSpy).toHaveBeenCalledTimes(1);
|
|
103
|
-
expect(searchRequestedSpy).toHaveBeenCalledWith({
|
|
104
|
-
referenceId: 'ann-uri-123',
|
|
105
|
-
searchTerm: 'climate change',
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// ─── bind:search-requested ────────────────────────────────────────────
|
|
110
|
-
|
|
111
|
-
it('bind:search-requested opens the search modal', async () => {
|
|
112
|
-
const { getState, getEventBus } = renderBindFlow();
|
|
113
|
-
|
|
114
|
-
expect(getState().searchModalOpen).toBe(false);
|
|
115
|
-
|
|
116
|
-
act(() => { getEventBus().get('bind:search-requested').next({ referenceId: 'ref-abc', searchTerm: 'oceans' }); });
|
|
117
|
-
|
|
118
|
-
await waitFor(() => {
|
|
119
|
-
expect(getState().searchModalOpen).toBe(true);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('bind:search-requested sets pendingReferenceId', async () => {
|
|
124
|
-
const { getState, getEventBus } = renderBindFlow();
|
|
125
|
-
|
|
126
|
-
act(() => { getEventBus().get('bind:search-requested').next({ referenceId: 'ref-xyz', searchTerm: 'forests' }); });
|
|
127
|
-
|
|
128
|
-
await waitFor(() => {
|
|
129
|
-
expect(getState().pendingReferenceId).toBe('ref-xyz');
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('bind:link → bind:search-requested chain opens modal end-to-end', async () => {
|
|
134
|
-
const { getState, getEventBus } = renderBindFlow();
|
|
135
|
-
|
|
136
|
-
// Simulate the full user journey: user clicks "Link Document" on a reference entry
|
|
137
|
-
act(() => { getEventBus().get('bind:link').next({ annotationUri: 'ann-full-chain', searchTerm: 'biodiversity' }); });
|
|
138
|
-
|
|
139
|
-
await waitFor(() => {
|
|
140
|
-
expect(getState().searchModalOpen).toBe(true);
|
|
141
|
-
expect(getState().pendingReferenceId).toBe('ann-full-chain');
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// ─── onCloseSearchModal ──────────────────────────────────────────────────────
|
|
146
|
-
|
|
147
|
-
it('onCloseSearchModal closes the search modal', async () => {
|
|
148
|
-
const { getState, getEventBus } = renderBindFlow();
|
|
149
|
-
|
|
150
|
-
act(() => { getEventBus().get('bind:search-requested').next({ referenceId: 'ref-close', searchTerm: 'test' }); });
|
|
151
|
-
|
|
152
|
-
await waitFor(() => expect(getState().searchModalOpen).toBe(true));
|
|
153
|
-
|
|
154
|
-
act(() => { getState().onCloseSearchModal(); });
|
|
155
|
-
|
|
156
|
-
await waitFor(() => {
|
|
157
|
-
expect(getState().searchModalOpen).toBe(false);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it('onCloseSearchModal does not clear pendingReferenceId (preserves for re-open)', async () => {
|
|
162
|
-
const { getState, getEventBus } = renderBindFlow();
|
|
163
|
-
|
|
164
|
-
act(() => { getEventBus().get('bind:search-requested').next({ referenceId: 'ref-persist', searchTerm: 'test' }); });
|
|
165
|
-
await waitFor(() => expect(getState().searchModalOpen).toBe(true));
|
|
166
|
-
|
|
167
|
-
act(() => { getState().onCloseSearchModal(); });
|
|
168
|
-
await waitFor(() => expect(getState().searchModalOpen).toBe(false));
|
|
169
|
-
|
|
170
|
-
// pendingReferenceId remains — modal may reopen
|
|
171
|
-
expect(getState().pendingReferenceId).toBe('ref-persist');
|
|
172
|
-
});
|
|
173
|
-
|
|
174
81
|
// ─── bind:update-body ──────────────────────────────────────────────────
|
|
175
82
|
|
|
176
83
|
it('bind:update-body calls updateAnnotationBody API', async () => {
|
|
177
84
|
const { getEventBus } = renderBindFlow();
|
|
178
85
|
|
|
179
86
|
act(() => { getEventBus().get('bind:update-body').next({
|
|
180
|
-
|
|
181
|
-
resourceId: 'linked-resource-id',
|
|
87
|
+
annotationId: annotationId('ann-body-1'),
|
|
88
|
+
resourceId: resourceId('linked-resource-id'),
|
|
182
89
|
operations: [{ op: 'add', item: { id: 'linked-resource-id' } }],
|
|
183
90
|
}); });
|
|
184
91
|
|
|
@@ -191,8 +98,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
|
|
|
191
98
|
const { getEventBus } = renderBindFlow();
|
|
192
99
|
|
|
193
100
|
act(() => { getEventBus().get('bind:update-body').next({
|
|
194
|
-
|
|
195
|
-
resourceId: 'resource-id',
|
|
101
|
+
annotationId: annotationId('ann-auth'),
|
|
102
|
+
resourceId: resourceId('resource-id'),
|
|
196
103
|
operations: [{ op: 'replace', newItem: { id: 'resource-id' } }],
|
|
197
104
|
}); });
|
|
198
105
|
|
|
@@ -201,8 +108,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
|
|
|
201
108
|
});
|
|
202
109
|
|
|
203
110
|
const callArgs = updateAnnotationBodySpy.mock.calls[0];
|
|
204
|
-
expect(callArgs[
|
|
205
|
-
expect(callArgs[
|
|
111
|
+
expect(callArgs[3]).toHaveProperty('auth');
|
|
112
|
+
expect(callArgs[3].auth).toBe(accessToken(testToken));
|
|
206
113
|
});
|
|
207
114
|
|
|
208
115
|
it('bind:update-body emits bind:body-updated on success', async () => {
|
|
@@ -212,8 +119,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
|
|
|
212
119
|
const subscription = getEventBus().get('bind:body-updated').subscribe(bodyUpdatedSpy);
|
|
213
120
|
|
|
214
121
|
act(() => { getEventBus().get('bind:update-body').next({
|
|
215
|
-
|
|
216
|
-
resourceId: 'resource-id',
|
|
122
|
+
annotationId: annotationId('ann-success'),
|
|
123
|
+
resourceId: resourceId('resource-id'),
|
|
217
124
|
operations: [{ op: 'add', item: { id: 'resource-id' } }],
|
|
218
125
|
}); });
|
|
219
126
|
|
|
@@ -224,7 +131,7 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
|
|
|
224
131
|
subscription.unsubscribe();
|
|
225
132
|
|
|
226
133
|
expect(bodyUpdatedSpy).toHaveBeenCalledWith({
|
|
227
|
-
|
|
134
|
+
annotationId: annotationId('ann-success'),
|
|
228
135
|
});
|
|
229
136
|
});
|
|
230
137
|
|
|
@@ -237,8 +144,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
|
|
|
237
144
|
const subscription = getEventBus().get('bind:body-update-failed').subscribe(bodyUpdateFailedSpy);
|
|
238
145
|
|
|
239
146
|
act(() => { getEventBus().get('bind:update-body').next({
|
|
240
|
-
|
|
241
|
-
resourceId: 'resource-id',
|
|
147
|
+
annotationId: annotationId('ann-fail'),
|
|
148
|
+
resourceId: resourceId('resource-id'),
|
|
242
149
|
operations: [{ op: 'remove', item: { id: 'old-id' } }],
|
|
243
150
|
}); });
|
|
244
151
|
|
|
@@ -257,8 +164,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
|
|
|
257
164
|
const { getEventBus } = renderBindFlow();
|
|
258
165
|
|
|
259
166
|
act(() => { getEventBus().get('bind:update-body').next({
|
|
260
|
-
|
|
261
|
-
resourceId: 'resource-id',
|
|
167
|
+
annotationId: annotationId('ann-dedup'),
|
|
168
|
+
resourceId: resourceId('resource-id'),
|
|
262
169
|
operations: [{ op: 'add', item: { id: 'resource-id' } }],
|
|
263
170
|
}); });
|
|
264
171
|
|
|
@@ -52,7 +52,7 @@ describe('REPRODUCING BUG: Detection state not updating', () => {
|
|
|
52
52
|
// Component to capture EventBus and hook state
|
|
53
53
|
function TestComponent() {
|
|
54
54
|
eventBusInstance = useEventBus();
|
|
55
|
-
const state = useMarkFlow('
|
|
55
|
+
const state = useMarkFlow('test' as any);
|
|
56
56
|
currentState = state;
|
|
57
57
|
|
|
58
58
|
console.log('[TEST] useMarkFlow state:', {
|
|
@@ -109,7 +109,7 @@ describe('REPRODUCING BUG: Detection state not updating', () => {
|
|
|
109
109
|
|
|
110
110
|
function TestComponent() {
|
|
111
111
|
eventBusInstance = useEventBus();
|
|
112
|
-
const state = useMarkFlow('
|
|
112
|
+
const state = useMarkFlow('test' as any);
|
|
113
113
|
currentState = state;
|
|
114
114
|
|
|
115
115
|
return (
|
|
@@ -162,7 +162,7 @@ describe('REPRODUCING BUG: Detection state not updating', () => {
|
|
|
162
162
|
|
|
163
163
|
function TestComponent() {
|
|
164
164
|
eventBusInstance = useEventBus();
|
|
165
|
-
const state = useMarkFlow('
|
|
165
|
+
const state = useMarkFlow('f45fd44f9cb0b0fe1b7980d3d034bc61' as any);
|
|
166
166
|
|
|
167
167
|
stateSnapshots.push({
|
|
168
168
|
assistingMotivation: state.assistingMotivation,
|
|
@@ -31,7 +31,7 @@ import { ApiClientProvider } from '../../../contexts/ApiClientContext';
|
|
|
31
31
|
import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
|
|
32
32
|
import { SSEClient } from '@semiont/api-client';
|
|
33
33
|
import type { Motivation } from '@semiont/core';
|
|
34
|
-
import {
|
|
34
|
+
import { resourceId } from '@semiont/core';
|
|
35
35
|
import type { Emitter } from 'mitt';
|
|
36
36
|
|
|
37
37
|
// Mock Toast module to prevent "useToast must be used within a ToastProvider" errors
|
|
@@ -77,10 +77,10 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
it('should call annotateReferences exactly ONCE when detection starts (not twice)', async () => {
|
|
80
|
-
const
|
|
80
|
+
const testId = resourceId('test-resource');
|
|
81
81
|
|
|
82
82
|
// Render with real component composition
|
|
83
|
-
const { emitDetectionStart } = renderDetectionFlow(
|
|
83
|
+
const { emitDetectionStart } = renderDetectionFlow(testId);
|
|
84
84
|
|
|
85
85
|
// Trigger detection for linking (uses annotateReferences)
|
|
86
86
|
act(() => {
|
|
@@ -98,7 +98,7 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
98
98
|
|
|
99
99
|
// Verify correct parameters (eventBus is passed but we don't need to verify its exact value)
|
|
100
100
|
expect(annotateReferencesSpy).toHaveBeenCalledWith(
|
|
101
|
-
|
|
101
|
+
testId,
|
|
102
102
|
{
|
|
103
103
|
entityTypes: ['Person', 'Organization'],
|
|
104
104
|
includeDescriptiveReferences: false,
|
|
@@ -108,10 +108,10 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
it('should propagate SSE progress events to useMarkFlow state', async () => {
|
|
111
|
-
const
|
|
111
|
+
const testId = resourceId('test-resource');
|
|
112
112
|
|
|
113
113
|
// Render with state observer
|
|
114
|
-
const { emitDetectionStart, getEventBus } = renderDetectionFlow(
|
|
114
|
+
const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
|
|
115
115
|
|
|
116
116
|
// Start detection
|
|
117
117
|
act(() => {
|
|
@@ -145,8 +145,8 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
it('should handle multiple progress updates correctly', async () => {
|
|
148
|
-
const
|
|
149
|
-
const { emitDetectionStart, getEventBus } = renderDetectionFlow(
|
|
148
|
+
const testId = resourceId('test-resource');
|
|
149
|
+
const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
|
|
150
150
|
|
|
151
151
|
// Start detection
|
|
152
152
|
act(() => {
|
|
@@ -200,8 +200,8 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
it('should keep progress visible after detection completes', async () => {
|
|
203
|
-
const
|
|
204
|
-
const { emitDetectionStart, getEventBus } = renderDetectionFlow(
|
|
203
|
+
const testId = resourceId('test-resource');
|
|
204
|
+
const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
|
|
205
205
|
|
|
206
206
|
// Start detection
|
|
207
207
|
act(() => {
|
|
@@ -237,8 +237,8 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
237
237
|
});
|
|
238
238
|
|
|
239
239
|
it('should clear progress on detection failure', async () => {
|
|
240
|
-
const
|
|
241
|
-
const { emitDetectionStart, getEventBus } = renderDetectionFlow(
|
|
240
|
+
const testId = resourceId('test-resource');
|
|
241
|
+
const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
|
|
242
242
|
|
|
243
243
|
// Start detection
|
|
244
244
|
act(() => {
|
|
@@ -282,8 +282,8 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
282
282
|
});
|
|
283
283
|
|
|
284
284
|
it('should handle different detection motivations with correct API calls', async () => {
|
|
285
|
-
const
|
|
286
|
-
const { emitDetectionStart } = renderDetectionFlow(
|
|
285
|
+
const testId = resourceId('test-resource');
|
|
286
|
+
const { emitDetectionStart } = renderDetectionFlow(testId);
|
|
287
287
|
|
|
288
288
|
// Test highlighting
|
|
289
289
|
act(() => {
|
|
@@ -292,7 +292,7 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
292
292
|
|
|
293
293
|
await waitFor(() => {
|
|
294
294
|
expect(annotateHighlightsSpy).toHaveBeenCalledTimes(1);
|
|
295
|
-
expect(annotateHighlightsSpy).toHaveBeenCalledWith(
|
|
295
|
+
expect(annotateHighlightsSpy).toHaveBeenCalledWith(testId, {
|
|
296
296
|
instructions: 'Find important text',
|
|
297
297
|
}, expect.objectContaining({ auth: undefined }));
|
|
298
298
|
});
|
|
@@ -312,7 +312,7 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
312
312
|
|
|
313
313
|
await waitFor(() => {
|
|
314
314
|
expect(detectCommentsSpy).toHaveBeenCalledTimes(1);
|
|
315
|
-
expect(detectCommentsSpy).toHaveBeenCalledWith(
|
|
315
|
+
expect(detectCommentsSpy).toHaveBeenCalledWith(testId, {
|
|
316
316
|
instructions: 'Add helpful comments',
|
|
317
317
|
tone: 'educational',
|
|
318
318
|
}, expect.objectContaining({ auth: undefined }));
|
|
@@ -320,11 +320,11 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
320
320
|
});
|
|
321
321
|
|
|
322
322
|
it('should only call API once even with multiple event listeners', async () => {
|
|
323
|
-
const
|
|
323
|
+
const testId = resourceId('test-resource');
|
|
324
324
|
|
|
325
325
|
// This test specifically catches the duplicate useBindFlow bug
|
|
326
326
|
// If multiple components call useBindFlow, we'll see multiple API calls
|
|
327
|
-
const { emitDetectionStart, getEventBus } = renderDetectionFlow(
|
|
327
|
+
const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
|
|
328
328
|
|
|
329
329
|
// Add an additional event listener (simulating multiple subscribers)
|
|
330
330
|
const additionalListener = vi.fn();
|
|
@@ -354,7 +354,7 @@ describe('Detection Flow - Feature Integration', () => {
|
|
|
354
354
|
* Helper: Render useMarkFlow hook with real component composition
|
|
355
355
|
* Returns methods to interact with the rendered component
|
|
356
356
|
*/
|
|
357
|
-
function renderDetectionFlow(
|
|
357
|
+
function renderDetectionFlow(testId: string) {
|
|
358
358
|
let eventBusInstance: Emitter<EventMap>;
|
|
359
359
|
|
|
360
360
|
// Component to capture EventBus instance
|
|
@@ -365,7 +365,7 @@ function renderDetectionFlow(testUri: string) {
|
|
|
365
365
|
|
|
366
366
|
// Test harness component that uses the hook
|
|
367
367
|
function DetectionFlowTestHarness() {
|
|
368
|
-
const { progress, assistingMotivation } = useMarkFlow(
|
|
368
|
+
const { progress, assistingMotivation } = useMarkFlow(testId as any);
|
|
369
369
|
return (
|
|
370
370
|
<div>
|
|
371
371
|
<div data-testid="detecting">{assistingMotivation || 'none'}</div>
|
|
@@ -26,7 +26,7 @@ import { render, waitFor } from '@testing-library/react';
|
|
|
26
26
|
import { act } from 'react';
|
|
27
27
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
28
28
|
import { SemiontApiClient } from '@semiont/api-client';
|
|
29
|
-
import {
|
|
29
|
+
import { resourceId, accessToken } from '@semiont/core';
|
|
30
30
|
import { EventBusProvider, useEventBus, resetEventBusForTesting } from '../../../contexts/EventBusContext';
|
|
31
31
|
import { useEventSubscriptions } from '../../../contexts/useEventSubscription';
|
|
32
32
|
import { ApiClientProvider } from '../../../contexts/ApiClientContext';
|
|
@@ -36,7 +36,7 @@ import type { EventMap, EventBus } from '@semiont/core';
|
|
|
36
36
|
|
|
37
37
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
38
38
|
|
|
39
|
-
const TEST_URI =
|
|
39
|
+
const TEST_URI = resourceId('test-resource');
|
|
40
40
|
const TEST_TOKEN = 'test-auth-token-123';
|
|
41
41
|
const BASE_URL = 'http://localhost:4000';
|
|
42
42
|
const CLONE_TOKEN = 'generated-clone-token-xyz';
|
|
@@ -66,11 +66,11 @@ function ResourceMutationHarness({ onEventBus }: { onEventBus: (eventBus: EventB
|
|
|
66
66
|
const generateCloneTokenMutation = resources.generateCloneToken.useMutation();
|
|
67
67
|
|
|
68
68
|
const handleResourceArchive = useCallback(async () => {
|
|
69
|
-
await updateMutation.mutateAsync({
|
|
69
|
+
await updateMutation.mutateAsync({ id: TEST_URI, data: { archived: true } });
|
|
70
70
|
}, [updateMutation]);
|
|
71
71
|
|
|
72
72
|
const handleResourceUnarchive = useCallback(async () => {
|
|
73
|
-
await updateMutation.mutateAsync({
|
|
73
|
+
await updateMutation.mutateAsync({ id: TEST_URI, data: { archived: false } });
|
|
74
74
|
}, [updateMutation]);
|
|
75
75
|
|
|
76
76
|
const handleResourceClone = useCallback(async () => {
|
|
@@ -175,7 +175,7 @@ describe('Resource mutations — hooks hoisted to top level', () => {
|
|
|
175
175
|
await waitFor(() => {
|
|
176
176
|
expect(generateCloneTokenSpy).toHaveBeenCalledWith(
|
|
177
177
|
TEST_URI,
|
|
178
|
-
expect.anything()
|
|
178
|
+
expect.anything(),
|
|
179
179
|
);
|
|
180
180
|
});
|
|
181
181
|
});
|
|
@@ -241,7 +241,7 @@ describe('Resource mutations — hooks hoisted to top level', () => {
|
|
|
241
241
|
expect(updateResourceSpy).toHaveBeenCalledWith(
|
|
242
242
|
TEST_URI,
|
|
243
243
|
expect.objectContaining({ archived: true }),
|
|
244
|
-
expect.anything()
|
|
244
|
+
expect.anything(),
|
|
245
245
|
);
|
|
246
246
|
});
|
|
247
247
|
|
|
@@ -275,7 +275,7 @@ describe('Resource mutations — hooks hoisted to top level', () => {
|
|
|
275
275
|
expect(updateResourceSpy).toHaveBeenCalledWith(
|
|
276
276
|
TEST_URI,
|
|
277
277
|
expect.objectContaining({ archived: false }),
|
|
278
|
-
expect.anything()
|
|
278
|
+
expect.anything(),
|
|
279
279
|
);
|
|
280
280
|
});
|
|
281
281
|
|
|
@@ -129,19 +129,11 @@ vi.mock('@/components/toolbar/ToolbarPanels', () => ({
|
|
|
129
129
|
ToolbarPanels: ({ children }: any) => <div data-testid="toolbar-panels">{children}</div>,
|
|
130
130
|
}));
|
|
131
131
|
|
|
132
|
-
vi.mock('@/components/modals/SearchResourcesModal', () => ({
|
|
133
|
-
SearchResourcesModal: () => <div data-testid="search-modal">Search Modal</div>,
|
|
134
|
-
}));
|
|
135
|
-
|
|
136
|
-
vi.mock('@/components/modals/GenerationConfigModal', () => ({
|
|
137
|
-
GenerationConfigModal: () => <div data-testid="generation-modal">Generation Modal</div>,
|
|
138
|
-
}));
|
|
139
|
-
|
|
140
132
|
// Create mock props matching the current ResourceViewerPageProps
|
|
141
133
|
const createMockProps = (overrides?: Partial<ResourceViewerPageProps>): ResourceViewerPageProps => ({
|
|
142
134
|
resource: {
|
|
143
135
|
'@context': 'https://www.w3.org/ns/anno.jsonld',
|
|
144
|
-
'@id': '
|
|
136
|
+
'@id': 'test-123',
|
|
145
137
|
'@type': 'schema:DigitalDocument',
|
|
146
138
|
name: 'Test Resource',
|
|
147
139
|
description: 'A test resource for unit testing',
|
|
@@ -155,7 +147,7 @@ const createMockProps = (overrides?: Partial<ResourceViewerPageProps>): Resource
|
|
|
155
147
|
},
|
|
156
148
|
],
|
|
157
149
|
},
|
|
158
|
-
rUri: '
|
|
150
|
+
rUri: 'test-123' as any,
|
|
159
151
|
locale: 'en',
|
|
160
152
|
cacheManager: {},
|
|
161
153
|
Link: ({ children }: any) => <a>{children}</a>,
|
|
@@ -163,8 +155,6 @@ const createMockProps = (overrides?: Partial<ResourceViewerPageProps>): Resource
|
|
|
163
155
|
refetchDocument: vi.fn().mockResolvedValue(undefined),
|
|
164
156
|
ToolbarPanels: ({ children, activePanel }: any) =>
|
|
165
157
|
!activePanel ? null : <div data-testid="toolbar-panels">{children}</div>,
|
|
166
|
-
SearchResourcesModal: () => <div data-testid="search-modal">Search Modal</div>,
|
|
167
|
-
GenerationConfigModal: () => <div data-testid="generation-modal">Generation Modal</div>,
|
|
168
158
|
...overrides,
|
|
169
159
|
});
|
|
170
160
|
|
|
@@ -323,18 +313,12 @@ describe('ResourceViewerPage', () => {
|
|
|
323
313
|
});
|
|
324
314
|
|
|
325
315
|
describe('Modals', () => {
|
|
326
|
-
it('renders
|
|
327
|
-
const props = createMockProps();
|
|
328
|
-
renderWithProviders(<ResourceViewerPage {...props} />);
|
|
329
|
-
|
|
330
|
-
expect(screen.getByTestId('search-modal')).toBeInTheDocument();
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('renders generation config modal', () => {
|
|
316
|
+
it('renders reference wizard modal', () => {
|
|
334
317
|
const props = createMockProps();
|
|
335
318
|
renderWithProviders(<ResourceViewerPage {...props} />);
|
|
336
319
|
|
|
337
|
-
|
|
320
|
+
// Wizard modal is rendered but closed by default
|
|
321
|
+
// It opens when bind:initiate is emitted from ReferenceEntry
|
|
338
322
|
});
|
|
339
323
|
});
|
|
340
324
|
|
|
@@ -19,7 +19,7 @@ import { render, waitFor, act } from '@testing-library/react';
|
|
|
19
19
|
import { EventBusProvider, resetEventBusForTesting, useEventBus } from '../../../contexts/EventBusContext';
|
|
20
20
|
import { ApiClientProvider } from '../../../contexts/ApiClientContext';
|
|
21
21
|
import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
|
|
22
|
-
import {
|
|
22
|
+
import { resourceId } from '@semiont/core';
|
|
23
23
|
import { useMarkFlow } from '../../../hooks/useMarkFlow';
|
|
24
24
|
import { useYieldFlow } from '../../../hooks/useYieldFlow';
|
|
25
25
|
|
|
@@ -38,7 +38,7 @@ vi.mock('../../../components/Toast', () => ({
|
|
|
38
38
|
|
|
39
39
|
describe('Toast Notifications - Verifies Toast Integration', () => {
|
|
40
40
|
let eventBusInstance: any;
|
|
41
|
-
const rUri =
|
|
41
|
+
const rUri = resourceId('test');
|
|
42
42
|
|
|
43
43
|
beforeEach(() => {
|
|
44
44
|
resetEventBusForTesting();
|