@solucx/react-native-solucx-widget 0.2.5 → 2.0.7
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/README.md +526 -182
- package/lib/SoluCXWidget.d.ts +50 -7
- package/lib/SoluCXWidget.d.ts.map +1 -1
- package/lib/SoluCXWidget.js +105 -101
- package/lib/SoluCXWidget.js.map +1 -1
- package/lib/SoluCXWidgetHost.d.ts +3 -0
- package/lib/SoluCXWidgetHost.d.ts.map +1 -0
- package/lib/SoluCXWidgetHost.js +34 -0
- package/lib/SoluCXWidgetHost.js.map +1 -0
- package/lib/SoluCXWidgetView.d.ts +12 -0
- package/lib/SoluCXWidgetView.d.ts.map +1 -0
- package/lib/SoluCXWidgetView.js +61 -0
- package/lib/SoluCXWidgetView.js.map +1 -0
- package/lib/components/CloseButton.d.ts +1 -1
- package/lib/components/CloseButton.d.ts.map +1 -1
- package/lib/components/CloseButton.js +4 -1
- package/lib/components/CloseButton.js.map +1 -1
- package/lib/components/InlineWidget.d.ts.map +1 -1
- package/lib/components/InlineWidget.js +2 -7
- package/lib/components/InlineWidget.js.map +1 -1
- package/lib/components/ModalWidget.d.ts +1 -1
- package/lib/components/ModalWidget.d.ts.map +1 -1
- package/lib/components/ModalWidget.js +3 -16
- package/lib/components/ModalWidget.js.map +1 -1
- package/lib/components/OverlayWidget.d.ts.map +1 -1
- package/lib/components/OverlayWidget.js +5 -15
- package/lib/components/OverlayWidget.js.map +1 -1
- package/lib/components/index.d.ts +5 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/index.js +12 -0
- package/lib/components/index.js.map +1 -0
- package/lib/constants/Constants.d.ts +11 -0
- package/lib/constants/Constants.d.ts.map +1 -1
- package/lib/constants/Constants.js +16 -1
- package/lib/constants/Constants.js.map +1 -1
- package/lib/{interfaces → domain}/WidgetCallbacks.d.ts +2 -2
- package/lib/domain/WidgetCallbacks.d.ts.map +1 -0
- package/lib/domain/WidgetCallbacks.js.map +1 -0
- package/{src/interfaces/WidgetData.ts → lib/domain/WidgetData.d.ts} +5 -2
- package/lib/domain/WidgetData.d.ts.map +1 -0
- package/lib/{interfaces → domain}/WidgetData.js.map +1 -1
- package/lib/domain/WidgetDisplayResult.d.ts +6 -0
- package/lib/domain/WidgetDisplayResult.d.ts.map +1 -0
- package/lib/domain/WidgetDisplayResult.js +3 -0
- package/lib/domain/WidgetDisplayResult.js.map +1 -0
- package/lib/domain/WidgetOptions.d.ts +27 -0
- package/lib/domain/WidgetOptions.d.ts.map +1 -0
- package/lib/domain/WidgetOptions.js +30 -0
- package/lib/domain/WidgetOptions.js.map +1 -0
- package/lib/domain/WidgetResponse.d.ts +5 -0
- package/lib/domain/WidgetResponse.d.ts.map +1 -0
- package/lib/{interfaces/WidgetOptions.js → domain/WidgetResponse.js} +1 -1
- package/lib/domain/WidgetResponse.js.map +1 -0
- package/lib/domain/WidgetSamplerLog.d.ts +12 -0
- package/lib/domain/WidgetSamplerLog.d.ts.map +1 -0
- package/lib/domain/WidgetSamplerLog.js.map +1 -0
- package/lib/{interfaces → domain}/index.d.ts +1 -2
- package/lib/domain/index.d.ts.map +1 -0
- package/lib/{interfaces → domain}/index.js.map +1 -1
- package/lib/hooks/index.d.ts +2 -2
- package/lib/hooks/index.d.ts.map +1 -1
- package/lib/hooks/index.js +5 -5
- package/lib/hooks/index.js.map +1 -1
- package/lib/hooks/useClientVersionCollector.d.ts +3 -0
- package/lib/hooks/useClientVersionCollector.d.ts.map +1 -0
- package/lib/{services/ClientVersionCollector.js → hooks/useClientVersionCollector.js} +7 -2
- package/lib/hooks/useClientVersionCollector.js.map +1 -0
- package/lib/hooks/useHeightAnimation.d.ts +0 -1
- package/lib/hooks/useHeightAnimation.d.ts.map +1 -1
- package/lib/hooks/useHeightAnimation.js +4 -2
- package/lib/hooks/useHeightAnimation.js.map +1 -1
- package/lib/hooks/useWidget.d.ts +13 -0
- package/lib/hooks/useWidget.d.ts.map +1 -0
- package/lib/hooks/useWidget.js +44 -0
- package/lib/hooks/useWidget.js.map +1 -0
- package/lib/hooks/useWidgetBootstrap.d.ts +21 -0
- package/lib/hooks/useWidgetBootstrap.d.ts.map +1 -0
- package/lib/hooks/useWidgetBootstrap.js +87 -0
- package/lib/hooks/useWidgetBootstrap.js.map +1 -0
- package/lib/hooks/useWidgetServices.d.ts +19 -0
- package/lib/hooks/useWidgetServices.d.ts.map +1 -0
- package/lib/hooks/useWidgetServices.js +34 -0
- package/lib/hooks/useWidgetServices.js.map +1 -0
- package/lib/hooks/useWidgetUI.d.ts +9 -0
- package/lib/hooks/useWidgetUI.d.ts.map +1 -0
- package/lib/hooks/useWidgetUI.js +33 -0
- package/lib/hooks/useWidgetUI.js.map +1 -0
- package/lib/index.d.ts +10 -11
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +13 -38
- package/lib/index.js.map +1 -1
- package/lib/services/UserIdentificationService.d.ts +3 -0
- package/lib/services/UserIdentificationService.d.ts.map +1 -0
- package/lib/services/UserIdentificationService.js +17 -0
- package/lib/services/UserIdentificationService.js.map +1 -0
- package/lib/services/WidgetBootstrapService.d.ts +12 -0
- package/lib/services/WidgetBootstrapService.d.ts.map +1 -0
- package/lib/services/{widgetBootstrapService.js → WidgetBootstrapService.js} +36 -15
- package/lib/services/WidgetBootstrapService.js.map +1 -0
- package/lib/services/WidgetEventService.d.ts +8 -0
- package/lib/services/WidgetEventService.d.ts.map +1 -0
- package/lib/services/WidgetEventService.js +14 -0
- package/lib/services/WidgetEventService.js.map +1 -0
- package/lib/services/WidgetStateManager.d.ts +20 -0
- package/lib/services/WidgetStateManager.d.ts.map +1 -0
- package/lib/services/WidgetStateManager.js +93 -0
- package/lib/services/WidgetStateManager.js.map +1 -0
- package/lib/services/WidgetValidationService.d.ts +17 -0
- package/lib/services/WidgetValidationService.d.ts.map +1 -0
- package/lib/services/WidgetValidationService.js +132 -0
- package/lib/services/WidgetValidationService.js.map +1 -0
- package/lib/services/events/EventHandlerFactory.d.ts +18 -0
- package/lib/services/events/EventHandlerFactory.d.ts.map +1 -0
- package/lib/services/events/EventHandlerFactory.js +67 -0
- package/lib/services/events/EventHandlerFactory.js.map +1 -0
- package/lib/services/events/EventHandlers.d.ts +10 -0
- package/lib/services/events/EventHandlers.d.ts.map +1 -0
- package/lib/services/events/EventHandlers.js +72 -0
- package/lib/services/events/EventHandlers.js.map +1 -0
- package/lib/services/events/index.d.ts +3 -0
- package/lib/services/events/index.d.ts.map +1 -0
- package/lib/services/events/index.js +21 -0
- package/lib/services/events/index.js.map +1 -0
- package/lib/services/height/HeightStrategies.d.ts +3 -0
- package/lib/services/height/HeightStrategies.d.ts.map +1 -0
- package/lib/services/height/HeightStrategies.js +14 -0
- package/lib/services/height/HeightStrategies.js.map +1 -0
- package/lib/services/storage/AsyncStorageService.d.ts +13 -0
- package/lib/services/storage/AsyncStorageService.d.ts.map +1 -0
- package/lib/services/storage/AsyncStorageService.js +73 -0
- package/lib/services/storage/AsyncStorageService.js.map +1 -0
- package/lib/services/storage/IStorageService.d.ts +30 -0
- package/lib/services/storage/IStorageService.d.ts.map +1 -0
- package/lib/services/storage/IStorageService.js +3 -0
- package/lib/services/storage/IStorageService.js.map +1 -0
- package/lib/services/storage/StorageIdBuilder.d.ts +11 -0
- package/lib/services/storage/StorageIdBuilder.d.ts.map +1 -0
- package/lib/services/storage/StorageIdBuilder.js +17 -0
- package/lib/services/storage/StorageIdBuilder.js.map +1 -0
- package/lib/services/storage/index.d.ts +3 -0
- package/lib/services/storage/index.d.ts.map +1 -0
- package/lib/services/storage/index.js +6 -0
- package/lib/services/storage/index.js.map +1 -0
- package/lib/styles/widgetStyles.d.ts +1 -1
- package/lib/styles/widgetStyles.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/SoluCXWidget.ts +144 -0
- package/src/SoluCXWidgetHost.tsx +44 -0
- package/src/SoluCXWidgetView.tsx +97 -0
- package/src/__tests__/ClientVersionCollector.test.ts +5 -5
- package/src/__tests__/OverlayWidget.rendering.test.tsx +12 -14
- package/src/__tests__/SoluCXWidget.rendering.test.tsx +103 -60
- package/src/__tests__/SoluCXWidget.test.ts +448 -0
- package/src/__tests__/WidgetValidationService.test.ts +408 -0
- package/src/__tests__/e2e/widget-lifecycle.test.tsx +14 -23
- package/src/__tests__/index.test.tsx +39 -0
- package/src/__tests__/integration/webview-communication-simple.test.tsx +8 -6
- package/src/__tests__/integration/webview-communication.test.tsx +127 -130
- package/src/__tests__/normalizeWidgetOptions.test.ts +80 -0
- package/src/__tests__/useWidgetBootstrap.test.ts +634 -0
- package/src/__tests__/useWidgetState.test.ts +56 -13
- package/src/__tests__/widgetBootstrapService.test.ts +15 -17
- package/src/components/CloseButton.tsx +6 -2
- package/src/components/InlineWidget.tsx +4 -9
- package/src/components/ModalWidget.tsx +15 -45
- package/src/components/OverlayWidget.tsx +5 -15
- package/src/components/index.ts +4 -0
- package/src/constants/Constants.ts +15 -0
- package/src/{interfaces → domain}/WidgetCallbacks.ts +2 -2
- package/{lib/interfaces/WidgetData.d.ts → src/domain/WidgetData.ts} +3 -2
- package/src/domain/WidgetDisplayResult.ts +16 -0
- package/src/domain/WidgetOptions.ts +53 -0
- package/src/domain/WidgetResponse.ts +5 -0
- package/src/domain/WidgetSamplerLog.ts +11 -0
- package/src/{interfaces → domain}/index.ts +1 -2
- package/src/hooks/index.ts +2 -2
- package/src/{services/ClientVersionCollector.ts → hooks/useClientVersionCollector.ts} +6 -0
- package/src/hooks/useHeightAnimation.ts +6 -3
- package/src/hooks/useWidget.ts +46 -0
- package/src/hooks/useWidgetBootstrap.ts +117 -0
- package/src/hooks/useWidgetServices.ts +44 -0
- package/src/hooks/useWidgetUI.ts +38 -0
- package/src/index.ts +16 -11
- package/src/services/UserIdentificationService.ts +14 -0
- package/src/services/{widgetBootstrapService.ts → WidgetBootstrapService.ts} +43 -19
- package/src/services/WidgetEventService.ts +15 -0
- package/src/services/WidgetStateManager.ts +115 -0
- package/src/services/WidgetValidationService.ts +149 -0
- package/src/services/events/EventHandlerFactory.ts +70 -0
- package/src/services/events/EventHandlers.ts +67 -0
- package/src/services/events/index.ts +2 -0
- package/src/services/height/HeightStrategies.ts +15 -0
- package/src/services/storage/AsyncStorageService.ts +74 -0
- package/src/services/storage/IStorageService.ts +32 -0
- package/src/services/storage/StorageIdBuilder.ts +15 -0
- package/src/services/storage/index.ts +2 -0
- package/src/styles/widgetStyles.ts +1 -1
- package/README.intern.md +0 -490
- package/lib/constants/webViewConstants.d.ts +0 -12
- package/lib/constants/webViewConstants.d.ts.map +0 -1
- package/lib/constants/webViewConstants.js +0 -19
- package/lib/constants/webViewConstants.js.map +0 -1
- package/lib/hooks/useWidgetHeight.d.ts +0 -13
- package/lib/hooks/useWidgetHeight.d.ts.map +0 -1
- package/lib/hooks/useWidgetHeight.js +0 -21
- package/lib/hooks/useWidgetHeight.js.map +0 -1
- package/lib/hooks/useWidgetState.d.ts +0 -15
- package/lib/hooks/useWidgetState.d.ts.map +0 -1
- package/lib/hooks/useWidgetState.js +0 -79
- package/lib/hooks/useWidgetState.js.map +0 -1
- package/lib/interfaces/WidgetCallbacks.d.ts.map +0 -1
- package/lib/interfaces/WidgetCallbacks.js.map +0 -1
- package/lib/interfaces/WidgetData.d.ts.map +0 -1
- package/lib/interfaces/WidgetOptions.d.ts +0 -9
- package/lib/interfaces/WidgetOptions.d.ts.map +0 -1
- package/lib/interfaces/WidgetOptions.js.map +0 -1
- package/lib/interfaces/WidgetResponse.d.ts +0 -10
- package/lib/interfaces/WidgetResponse.d.ts.map +0 -1
- package/lib/interfaces/WidgetResponse.js +0 -12
- package/lib/interfaces/WidgetResponse.js.map +0 -1
- package/lib/interfaces/WidgetSamplerLog.d.ts +0 -7
- package/lib/interfaces/WidgetSamplerLog.d.ts.map +0 -1
- package/lib/interfaces/WidgetSamplerLog.js.map +0 -1
- package/lib/interfaces/index.d.ts.map +0 -1
- package/lib/services/ClientVersionCollector.d.ts +0 -2
- package/lib/services/ClientVersionCollector.d.ts.map +0 -1
- package/lib/services/ClientVersionCollector.js.map +0 -1
- package/lib/services/storage.d.ts +0 -8
- package/lib/services/storage.d.ts.map +0 -1
- package/lib/services/storage.js +0 -23
- package/lib/services/storage.js.map +0 -1
- package/lib/services/widgetBootstrapService.d.ts +0 -6
- package/lib/services/widgetBootstrapService.d.ts.map +0 -1
- package/lib/services/widgetBootstrapService.js.map +0 -1
- package/lib/services/widgetEventService.d.ts +0 -19
- package/lib/services/widgetEventService.d.ts.map +0 -1
- package/lib/services/widgetEventService.js +0 -79
- package/lib/services/widgetEventService.js.map +0 -1
- package/lib/services/widgetValidationService.d.ts +0 -18
- package/lib/services/widgetValidationService.d.ts.map +0 -1
- package/lib/services/widgetValidationService.js +0 -71
- package/lib/services/widgetValidationService.js.map +0 -1
- package/src/SoluCXWidget.tsx +0 -179
- package/src/constants/webViewConstants.ts +0 -15
- package/src/hooks/useWidgetHeight.ts +0 -38
- package/src/hooks/useWidgetState.ts +0 -101
- package/src/interfaces/WidgetOptions.ts +0 -8
- package/src/interfaces/WidgetResponse.ts +0 -15
- package/src/interfaces/WidgetSamplerLog.ts +0 -6
- package/src/services/storage.ts +0 -21
- package/src/services/widgetEventService.ts +0 -110
- package/src/services/widgetValidationService.ts +0 -102
- /package/lib/{interfaces → domain}/WidgetCallbacks.js +0 -0
- /package/lib/{interfaces → domain}/WidgetData.js +0 -0
- /package/lib/{interfaces → domain}/WidgetSamplerLog.js +0 -0
- /package/lib/{interfaces → domain}/index.js +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { render, waitFor } from "@testing-library/react-native";
|
|
3
|
-
import { SoluCXWidget } from "../
|
|
4
|
-
import { WidgetData, WidgetOptions } from "../
|
|
5
|
-
import { requestWidgetUrl } from "../services/
|
|
3
|
+
import { SoluCXWidgetView as SoluCXWidget } from "../SoluCXWidgetView";
|
|
4
|
+
import { WidgetData, WidgetOptions } from "../domain";
|
|
5
|
+
import { requestWidgetOptions, requestWidgetUrl } from "../services/WidgetBootstrapService";
|
|
6
6
|
|
|
7
7
|
jest.mock("react-native-webview", () => {
|
|
8
8
|
const { forwardRef } = require("react");
|
|
@@ -13,38 +13,50 @@ jest.mock("react-native-webview", () => {
|
|
|
13
13
|
};
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
const mockLoadSavedData = jest.fn();
|
|
17
16
|
const mockClose = jest.fn();
|
|
18
|
-
const
|
|
17
|
+
const mockHide = jest.fn();
|
|
19
18
|
const mockOpen = jest.fn();
|
|
20
19
|
const mockShouldDisplayWidget = jest.fn();
|
|
20
|
+
const mockShouldDisplayForTransaction = jest.fn();
|
|
21
|
+
|
|
22
|
+
const mockStateManager = {
|
|
23
|
+
getLogs: jest.fn().mockResolvedValue({ lastFirstAccess: 0, lastDisplay: 0 }),
|
|
24
|
+
updateTimestamp: jest.fn().mockResolvedValue(undefined),
|
|
25
|
+
incrementAttempt: jest.fn().mockResolvedValue(undefined),
|
|
26
|
+
hasAnsweredTransaction: jest.fn().mockResolvedValue(false),
|
|
27
|
+
markTransactionAnswered: jest.fn().mockResolvedValue(undefined),
|
|
28
|
+
resetAttempts: jest.fn().mockResolvedValue(undefined),
|
|
29
|
+
};
|
|
21
30
|
|
|
22
|
-
jest.mock("../hooks/
|
|
23
|
-
|
|
31
|
+
jest.mock("../hooks/useWidget", () => ({
|
|
32
|
+
useWidget: () => ({
|
|
24
33
|
widgetHeight: 400,
|
|
25
34
|
isWidgetVisible: true,
|
|
26
|
-
|
|
27
|
-
loadSavedData: mockLoadSavedData,
|
|
35
|
+
hide: mockHide,
|
|
28
36
|
resize: jest.fn(),
|
|
29
37
|
open: mockOpen,
|
|
30
38
|
close: mockClose,
|
|
31
39
|
userId: "test-user-123",
|
|
40
|
+
stateManager: mockStateManager,
|
|
32
41
|
}),
|
|
33
42
|
}));
|
|
34
43
|
|
|
35
|
-
jest.mock("../services/
|
|
44
|
+
jest.mock("../services/WidgetEventService", () => ({
|
|
36
45
|
WidgetEventService: jest.fn().mockImplementation(() => ({
|
|
37
46
|
handleMessage: jest.fn(),
|
|
38
47
|
})),
|
|
39
48
|
}));
|
|
40
49
|
|
|
41
|
-
jest.mock("../services/
|
|
50
|
+
jest.mock("../services/WidgetValidationService", () => ({
|
|
42
51
|
WidgetValidationService: jest.fn().mockImplementation(() => ({
|
|
52
|
+
shouldDisplayForTransaction: mockShouldDisplayForTransaction,
|
|
53
|
+
shouldDisplayForTransactionAlreadyAnswered: mockShouldDisplayForTransaction,
|
|
43
54
|
shouldDisplayWidget: mockShouldDisplayWidget,
|
|
44
55
|
})),
|
|
45
56
|
}));
|
|
46
57
|
|
|
47
|
-
jest.mock("../services/
|
|
58
|
+
jest.mock("../services/WidgetBootstrapService", () => ({
|
|
59
|
+
requestWidgetOptions: jest.fn(),
|
|
48
60
|
requestWidgetUrl: jest.fn(),
|
|
49
61
|
}));
|
|
50
62
|
|
|
@@ -53,7 +65,7 @@ jest.mock("../constants/Constants", () => ({
|
|
|
53
65
|
SDK_VERSION: "0.1.16",
|
|
54
66
|
}));
|
|
55
67
|
|
|
56
|
-
jest.mock("../
|
|
68
|
+
jest.mock("../hooks/useClientVersionCollector", () => ({
|
|
57
69
|
getClientVersion: jest.fn(() => "1.0.0"),
|
|
58
70
|
}));
|
|
59
71
|
|
|
@@ -73,6 +85,7 @@ jest.mock("../hooks/useDeviceInfoCollector", () => ({
|
|
|
73
85
|
}));
|
|
74
86
|
|
|
75
87
|
const mockRequestWidgetUrl = requestWidgetUrl as jest.MockedFunction<typeof requestWidgetUrl>;
|
|
88
|
+
const mockRequestWidgetOptions = requestWidgetOptions as jest.MockedFunction<typeof requestWidgetOptions>;
|
|
76
89
|
const bootstrappedWidgetUrl = "https://widgets.solucx.com/widget/bootstrap-result";
|
|
77
90
|
|
|
78
91
|
const baseProps = {
|
|
@@ -89,26 +102,30 @@ const baseProps = {
|
|
|
89
102
|
|
|
90
103
|
beforeEach(() => {
|
|
91
104
|
jest.clearAllMocks();
|
|
105
|
+
mockShouldDisplayForTransaction.mockReset();
|
|
106
|
+
mockShouldDisplayForTransaction.mockResolvedValue({ canDisplay: true });
|
|
92
107
|
mockShouldDisplayWidget.mockReset();
|
|
93
108
|
mockShouldDisplayWidget.mockResolvedValue({ canDisplay: true });
|
|
109
|
+
mockRequestWidgetOptions.mockReset();
|
|
110
|
+
mockRequestWidgetOptions.mockResolvedValue({ height: 400 });
|
|
94
111
|
mockRequestWidgetUrl.mockReset();
|
|
95
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
112
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
96
113
|
});
|
|
97
114
|
|
|
98
115
|
describe("SoluCXWidget type routing", () => {
|
|
99
116
|
it("should render ModalWidget when type is modal", async () => {
|
|
100
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
117
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
101
118
|
|
|
102
119
|
const { UNSAFE_getByType } = render(<SoluCXWidget {...baseProps} type="modal" />);
|
|
103
120
|
|
|
104
121
|
await waitFor(() => {
|
|
105
122
|
const modal = UNSAFE_getByType(require("react-native").Modal);
|
|
106
123
|
expect(modal).toBeTruthy();
|
|
107
|
-
});
|
|
108
|
-
},
|
|
124
|
+
}, { timeout: 10000 });
|
|
125
|
+
}, 15000);
|
|
109
126
|
|
|
110
127
|
it("should render close button inside ModalWidget", async () => {
|
|
111
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
128
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
112
129
|
|
|
113
130
|
const { getByText } = render(<SoluCXWidget {...baseProps} type="modal" />);
|
|
114
131
|
|
|
@@ -118,7 +135,7 @@ describe("SoluCXWidget type routing", () => {
|
|
|
118
135
|
});
|
|
119
136
|
|
|
120
137
|
it("should render WebView inside ModalWidget with correct source", async () => {
|
|
121
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
138
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
122
139
|
|
|
123
140
|
const { getByTestId } = render(<SoluCXWidget {...baseProps} type="modal" />);
|
|
124
141
|
|
|
@@ -129,7 +146,7 @@ describe("SoluCXWidget type routing", () => {
|
|
|
129
146
|
});
|
|
130
147
|
|
|
131
148
|
it("should render InlineWidget when type is inline", async () => {
|
|
132
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
149
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
133
150
|
|
|
134
151
|
const { queryByTestId, getByText } = render(<SoluCXWidget {...baseProps} type="inline" />);
|
|
135
152
|
|
|
@@ -140,7 +157,7 @@ describe("SoluCXWidget type routing", () => {
|
|
|
140
157
|
});
|
|
141
158
|
|
|
142
159
|
it("should not render Modal when type is inline", async () => {
|
|
143
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
160
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
144
161
|
|
|
145
162
|
const { UNSAFE_queryByType } = render(<SoluCXWidget {...baseProps} type="inline" />);
|
|
146
163
|
|
|
@@ -151,7 +168,7 @@ describe("SoluCXWidget type routing", () => {
|
|
|
151
168
|
});
|
|
152
169
|
|
|
153
170
|
it("should render OverlayWidget when type is bottom", async () => {
|
|
154
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
171
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
155
172
|
|
|
156
173
|
const { queryByTestId, getByText } = render(<SoluCXWidget {...baseProps} type="bottom" />);
|
|
157
174
|
|
|
@@ -162,7 +179,7 @@ describe("SoluCXWidget type routing", () => {
|
|
|
162
179
|
});
|
|
163
180
|
|
|
164
181
|
it("should render OverlayWidget when type is top", async () => {
|
|
165
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
182
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
166
183
|
|
|
167
184
|
const { queryByTestId, getByText } = render(<SoluCXWidget {...baseProps} type="top" />);
|
|
168
185
|
|
|
@@ -173,7 +190,7 @@ describe("SoluCXWidget type routing", () => {
|
|
|
173
190
|
});
|
|
174
191
|
|
|
175
192
|
it("should not render Modal when type is bottom", async () => {
|
|
176
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
193
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
177
194
|
|
|
178
195
|
const { UNSAFE_queryByType } = render(<SoluCXWidget {...baseProps} type="bottom" />);
|
|
179
196
|
|
|
@@ -182,17 +199,11 @@ describe("SoluCXWidget type routing", () => {
|
|
|
182
199
|
expect(modal).toBeNull();
|
|
183
200
|
});
|
|
184
201
|
});
|
|
185
|
-
|
|
186
|
-
it("should call loadSavedData on mount", () => {
|
|
187
|
-
render(<SoluCXWidget {...baseProps} type="modal" />);
|
|
188
|
-
|
|
189
|
-
expect(mockLoadSavedData).toHaveBeenCalledTimes(1);
|
|
190
|
-
});
|
|
191
202
|
});
|
|
192
203
|
|
|
193
204
|
describe("SoluCXWidget WebView configuration", () => {
|
|
194
205
|
it("should set originWhitelist to allow all origins", async () => {
|
|
195
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
206
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
196
207
|
|
|
197
208
|
const { getByTestId } = render(<SoluCXWidget {...baseProps} type="modal" />);
|
|
198
209
|
|
|
@@ -203,7 +214,7 @@ describe("SoluCXWidget WebView configuration", () => {
|
|
|
203
214
|
});
|
|
204
215
|
|
|
205
216
|
it("should set WebView width style to screen width", async () => {
|
|
206
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
217
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
207
218
|
|
|
208
219
|
const { getByTestId } = render(<SoluCXWidget {...baseProps} type="modal" />);
|
|
209
220
|
|
|
@@ -215,7 +226,7 @@ describe("SoluCXWidget WebView configuration", () => {
|
|
|
215
226
|
});
|
|
216
227
|
|
|
217
228
|
it("should set WebView height style to widgetHeight", async () => {
|
|
218
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
229
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
219
230
|
|
|
220
231
|
const { getByTestId } = render(<SoluCXWidget {...baseProps} type="modal" />);
|
|
221
232
|
|
|
@@ -227,7 +238,7 @@ describe("SoluCXWidget WebView configuration", () => {
|
|
|
227
238
|
});
|
|
228
239
|
|
|
229
240
|
it("should use bootstrapped URL for widgets", async () => {
|
|
230
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
241
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
231
242
|
|
|
232
243
|
const { getByTestId } = render(<SoluCXWidget {...baseProps} type="inline" />);
|
|
233
244
|
|
|
@@ -240,7 +251,7 @@ describe("SoluCXWidget WebView configuration", () => {
|
|
|
240
251
|
|
|
241
252
|
describe("SoluCXWidget bootstrap flow", () => {
|
|
242
253
|
it("fetches widget URL for form widgets", async () => {
|
|
243
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
254
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
244
255
|
|
|
245
256
|
const props = {
|
|
246
257
|
...baseProps,
|
|
@@ -270,7 +281,7 @@ describe("SoluCXWidget bootstrap flow", () => {
|
|
|
270
281
|
});
|
|
271
282
|
|
|
272
283
|
it("fetches widget URL before rendering non-form widgets", async () => {
|
|
273
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
284
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
274
285
|
|
|
275
286
|
const props = {
|
|
276
287
|
...baseProps,
|
|
@@ -296,7 +307,7 @@ describe("SoluCXWidget bootstrap flow", () => {
|
|
|
296
307
|
expect(webview.props.source.uri).toBe(bootstrappedWidgetUrl);
|
|
297
308
|
});
|
|
298
309
|
|
|
299
|
-
it("keeps widget hidden when
|
|
310
|
+
it("keeps widget hidden when preflight fetch fails", async () => {
|
|
300
311
|
mockRequestWidgetUrl.mockRejectedValue(new Error("network"));
|
|
301
312
|
|
|
302
313
|
const props = {
|
|
@@ -313,15 +324,16 @@ describe("SoluCXWidget bootstrap flow", () => {
|
|
|
313
324
|
expect(queryByTestId("webview")).toBeNull();
|
|
314
325
|
|
|
315
326
|
await waitFor(() => {
|
|
327
|
+
// Validation runs BEFORE preflight to reduce API load at scale
|
|
328
|
+
expect(mockShouldDisplayWidget).toHaveBeenCalledTimes(1);
|
|
316
329
|
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
317
|
-
expect(mockShouldDisplayWidget).not.toHaveBeenCalled();
|
|
318
330
|
expect(queryByTestId("webview")).toBeNull();
|
|
319
331
|
});
|
|
320
332
|
});
|
|
321
333
|
|
|
322
|
-
it("keeps widget hidden when validation fails
|
|
323
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
324
|
-
mockShouldDisplayWidget.mockResolvedValue({ canDisplay: false, blockReason: "
|
|
334
|
+
it("keeps widget hidden when validation fails before preflight", async () => {
|
|
335
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
336
|
+
mockShouldDisplayWidget.mockResolvedValue({ canDisplay: false, blockReason: "BLOCKED_BY_MAX_ATTEMPTS" });
|
|
325
337
|
|
|
326
338
|
const props = {
|
|
327
339
|
...baseProps,
|
|
@@ -337,12 +349,38 @@ describe("SoluCXWidget bootstrap flow", () => {
|
|
|
337
349
|
expect(queryByTestId("webview")).toBeNull();
|
|
338
350
|
|
|
339
351
|
await waitFor(() => {
|
|
340
|
-
|
|
352
|
+
// Validation blocks BEFORE preflight — preflight is never called
|
|
341
353
|
expect(mockShouldDisplayWidget).toHaveBeenCalledTimes(1);
|
|
354
|
+
expect(mockRequestWidgetUrl).not.toHaveBeenCalled();
|
|
342
355
|
expect(mockOpen).not.toHaveBeenCalled();
|
|
343
356
|
expect(queryByTestId("webview")).toBeNull();
|
|
344
357
|
});
|
|
345
358
|
});
|
|
359
|
+
|
|
360
|
+
it("keeps widget hidden and skips all fetches when transaction was already answered", async () => {
|
|
361
|
+
mockShouldDisplayForTransaction.mockResolvedValue({
|
|
362
|
+
canDisplay: false,
|
|
363
|
+
blockReason: "BLOCKED_BY_TRANSACTION_ALREADY_ANSWERED",
|
|
364
|
+
});
|
|
365
|
+
const onBlocked = jest.fn();
|
|
366
|
+
|
|
367
|
+
const props = {
|
|
368
|
+
...baseProps,
|
|
369
|
+
type: "bottom" as const,
|
|
370
|
+
callbacks: { onBlocked },
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const { queryByTestId } = render(<SoluCXWidget {...props} />);
|
|
374
|
+
|
|
375
|
+
await waitFor(() => {
|
|
376
|
+
expect(mockShouldDisplayForTransaction).toHaveBeenCalledWith('txn-999');
|
|
377
|
+
expect(mockShouldDisplayWidget).not.toHaveBeenCalled();
|
|
378
|
+
expect(mockRequestWidgetOptions).not.toHaveBeenCalled();
|
|
379
|
+
expect(mockRequestWidgetUrl).not.toHaveBeenCalled();
|
|
380
|
+
expect(onBlocked).toHaveBeenCalledWith('BLOCKED_BY_TRANSACTION_ALREADY_ANSWERED');
|
|
381
|
+
expect(queryByTestId("webview")).toBeNull();
|
|
382
|
+
});
|
|
383
|
+
});
|
|
346
384
|
});
|
|
347
385
|
|
|
348
386
|
describe("SoluCXWidget callbacks", () => {
|
|
@@ -356,7 +394,7 @@ describe("SoluCXWidget callbacks", () => {
|
|
|
356
394
|
|
|
357
395
|
render(<SoluCXWidget {...props} type="modal" />);
|
|
358
396
|
|
|
359
|
-
expect(onError).
|
|
397
|
+
expect(onError).toHaveBeenCalled();
|
|
360
398
|
expect(onError).toHaveBeenCalledWith("Widget data is required but was not provided");
|
|
361
399
|
});
|
|
362
400
|
|
|
@@ -379,7 +417,7 @@ describe("SoluCXWidget callbacks", () => {
|
|
|
379
417
|
await waitFor(() => {
|
|
380
418
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
381
419
|
expect(onError).toHaveBeenCalledWith("Network timeout");
|
|
382
|
-
expect(
|
|
420
|
+
expect(mockHide).toHaveBeenCalledTimes(1);
|
|
383
421
|
});
|
|
384
422
|
});
|
|
385
423
|
|
|
@@ -449,13 +487,13 @@ describe("SoluCXWidget callbacks", () => {
|
|
|
449
487
|
});
|
|
450
488
|
});
|
|
451
489
|
|
|
452
|
-
it("calls
|
|
453
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
490
|
+
it("calls onBlocked when validation fails", async () => {
|
|
491
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
454
492
|
mockShouldDisplayWidget.mockResolvedValue({
|
|
455
493
|
canDisplay: false,
|
|
456
|
-
blockReason: "
|
|
494
|
+
blockReason: "BLOCKED_BY_MAX_ATTEMPTS",
|
|
457
495
|
});
|
|
458
|
-
const
|
|
496
|
+
const onBlocked = jest.fn();
|
|
459
497
|
|
|
460
498
|
const props = {
|
|
461
499
|
...baseProps,
|
|
@@ -464,20 +502,20 @@ describe("SoluCXWidget callbacks", () => {
|
|
|
464
502
|
journey: "sac",
|
|
465
503
|
},
|
|
466
504
|
type: "bottom" as const,
|
|
467
|
-
callbacks: {
|
|
505
|
+
callbacks: { onBlocked },
|
|
468
506
|
};
|
|
469
507
|
|
|
470
508
|
render(<SoluCXWidget {...props} />);
|
|
471
509
|
|
|
472
510
|
await waitFor(() => {
|
|
473
|
-
expect(
|
|
474
|
-
expect(
|
|
475
|
-
expect(
|
|
511
|
+
expect(onBlocked).toHaveBeenCalledTimes(1);
|
|
512
|
+
expect(onBlocked).toHaveBeenCalledWith("BLOCKED_BY_MAX_ATTEMPTS");
|
|
513
|
+
expect(mockHide).toHaveBeenCalledTimes(1);
|
|
476
514
|
});
|
|
477
515
|
});
|
|
478
516
|
|
|
479
517
|
it("calls onPreOpen, onOpened callbacks for successful bootstrap", async () => {
|
|
480
|
-
mockRequestWidgetUrl.mockResolvedValue(bootstrappedWidgetUrl);
|
|
518
|
+
mockRequestWidgetUrl.mockResolvedValue({ available: true, url: bootstrappedWidgetUrl });
|
|
481
519
|
mockShouldDisplayWidget.mockResolvedValue({ canDisplay: true });
|
|
482
520
|
const onPreOpen = jest.fn();
|
|
483
521
|
const onOpened = jest.fn();
|
|
@@ -520,7 +558,7 @@ describe("SoluCXWidget error handling from requestWidgetUrl", () => {
|
|
|
520
558
|
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
521
559
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
522
560
|
expect(onError).toHaveBeenCalledWith("Failed to get error response: 404 Not Found");
|
|
523
|
-
expect(
|
|
561
|
+
expect(mockHide).toHaveBeenCalledTimes(1);
|
|
524
562
|
expect(queryByTestId("webview")).toBeNull();
|
|
525
563
|
});
|
|
526
564
|
});
|
|
@@ -541,7 +579,7 @@ describe("SoluCXWidget error handling from requestWidgetUrl", () => {
|
|
|
541
579
|
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
542
580
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
543
581
|
expect(onError).toHaveBeenCalledWith("Failed to get error response: 500 Internal Server Error");
|
|
544
|
-
expect(
|
|
582
|
+
expect(mockHide).toHaveBeenCalledTimes(1);
|
|
545
583
|
expect(queryByTestId("webview")).toBeNull();
|
|
546
584
|
});
|
|
547
585
|
});
|
|
@@ -562,7 +600,7 @@ describe("SoluCXWidget error handling from requestWidgetUrl", () => {
|
|
|
562
600
|
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
563
601
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
564
602
|
expect(onError).toHaveBeenCalledWith("Fetch is not available");
|
|
565
|
-
expect(
|
|
603
|
+
expect(mockHide).toHaveBeenCalledTimes(1);
|
|
566
604
|
expect(queryByTestId("webview")).toBeNull();
|
|
567
605
|
});
|
|
568
606
|
});
|
|
@@ -583,12 +621,12 @@ describe("SoluCXWidget error handling from requestWidgetUrl", () => {
|
|
|
583
621
|
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
584
622
|
expect(onError).toHaveBeenCalledTimes(1);
|
|
585
623
|
expect(onError).toHaveBeenCalledWith("Failed to get the widget api response.");
|
|
586
|
-
expect(
|
|
624
|
+
expect(mockHide).toHaveBeenCalledTimes(1);
|
|
587
625
|
expect(queryByTestId("webview")).toBeNull();
|
|
588
626
|
});
|
|
589
627
|
});
|
|
590
628
|
|
|
591
|
-
it("
|
|
629
|
+
it("calls shouldDisplayWidget before requestWidgetUrl and handles preflight failure", async () => {
|
|
592
630
|
mockRequestWidgetUrl.mockRejectedValue(new Error("Failed to fetch widget URL: 403 Forbidden"));
|
|
593
631
|
const onError = jest.fn();
|
|
594
632
|
|
|
@@ -601,8 +639,9 @@ describe("SoluCXWidget error handling from requestWidgetUrl", () => {
|
|
|
601
639
|
render(<SoluCXWidget {...props} />);
|
|
602
640
|
|
|
603
641
|
await waitFor(() => {
|
|
642
|
+
// Validation runs BEFORE preflight to avoid unnecessary API calls
|
|
643
|
+
expect(mockShouldDisplayWidget).toHaveBeenCalled();
|
|
604
644
|
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
605
|
-
expect(mockShouldDisplayWidget).not.toHaveBeenCalled();
|
|
606
645
|
expect(mockOpen).not.toHaveBeenCalled();
|
|
607
646
|
expect(onError).toHaveBeenCalledWith("Failed to fetch widget URL: 403 Forbidden");
|
|
608
647
|
});
|
|
@@ -623,6 +662,8 @@ describe("SoluCXWidget error handling from requestWidgetUrl", () => {
|
|
|
623
662
|
render(<SoluCXWidget {...props} />);
|
|
624
663
|
|
|
625
664
|
await waitFor(() => {
|
|
665
|
+
// Validation runs first, then preflight fails
|
|
666
|
+
expect(mockShouldDisplayWidget).toHaveBeenCalled();
|
|
626
667
|
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
627
668
|
expect(onPreOpen).not.toHaveBeenCalled();
|
|
628
669
|
expect(onOpened).not.toHaveBeenCalled();
|
|
@@ -644,11 +685,13 @@ describe("SoluCXWidget error handling from requestWidgetUrl", () => {
|
|
|
644
685
|
expect(queryByTestId("webview")).toBeNull();
|
|
645
686
|
|
|
646
687
|
await waitFor(() => {
|
|
688
|
+
// Validation runs first, then preflight fails
|
|
689
|
+
expect(mockShouldDisplayWidget).toHaveBeenCalled();
|
|
647
690
|
expect(mockRequestWidgetUrl).toHaveBeenCalledTimes(1);
|
|
648
691
|
});
|
|
649
692
|
|
|
650
693
|
// Should remain null after failure
|
|
651
694
|
expect(queryByTestId("webview")).toBeNull();
|
|
652
|
-
expect(
|
|
695
|
+
expect(mockHide).toHaveBeenCalledTimes(1);
|
|
653
696
|
});
|
|
654
697
|
});
|