@envive-ai/react-hooks 0.3.0 → 0.3.2
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/application/commerce-api.cjs +4 -4
- package/dist/application/commerce-api.js +4 -4
- package/dist/application/models/api/response.d.cts +1 -1
- package/dist/application/models/api/response.d.ts +1 -1
- package/dist/application/models/frontendConfig.d.cts +1 -1
- package/dist/application/models/frontendConfig.d.ts +1 -1
- package/dist/application/models/guards/api/isApiFormResponse.cjs +1 -1
- package/dist/application/models/guards/api/isApiFormResponse.js +1 -1
- package/dist/application/models/guards/api/isApiFormSubmittedResponseAttributes.cjs +1 -1
- package/dist/application/models/guards/api/isApiFormSubmittedResponseAttributes.js +1 -1
- package/dist/application/models/guards/api/isApiOrderResponseAttributes.cjs +2 -2
- package/dist/application/models/guards/api/isApiOrderResponseAttributes.js +2 -2
- package/dist/application/models/guards/api/isApiProductResponseAttributes.cjs +1 -1
- package/dist/application/models/guards/api/isApiProductResponseAttributes.js +1 -1
- package/dist/application/models/guards/api/isApiResponse.cjs +1 -1
- package/dist/application/models/guards/api/isApiResponse.js +1 -1
- package/dist/application/models/guards/api/isApiSearchEventAttributes.cjs +2 -2
- package/dist/application/models/guards/api/isApiSearchEventAttributes.js +2 -2
- package/dist/application/models/message.cjs +1 -1
- package/dist/application/models/message.d.cts +1 -1
- package/dist/application/models/message.d.ts +1 -1
- package/dist/application/models/message.js +1 -1
- package/dist/application/models/validators/validateGraphQLColorsConfig.cjs +2 -1
- package/dist/application/models/validators/validateGraphQLColorsConfig.js +2 -1
- package/dist/application/models/validators/validateGraphQLFrontendConfig.cjs +1 -1
- package/dist/application/models/validators/validateGraphQLFrontendConfig.d.cts +1 -1
- package/dist/application/models/validators/validateGraphQLFrontendConfig.d.ts +1 -1
- package/dist/application/models/validators/validateGraphQLFrontendConfig.js +1 -1
- package/dist/application/utils/analyticsUtils.cjs +2 -2
- package/dist/application/utils/analyticsUtils.js +2 -2
- package/dist/application/utils/domObserver.cjs +1 -1
- package/dist/application/utils/domObserver.js +1 -1
- package/dist/application/utils/elementObserver.cjs +1 -1
- package/dist/application/utils/elementObserver.d.cts +2 -2
- package/dist/application/utils/elementObserver.js +1 -1
- package/dist/application/utils/mutationHelper.cjs +2 -2
- package/dist/application/utils/mutationHelper.js +2 -2
- package/dist/application/utils/nodeSelector.cjs +5 -2
- package/dist/application/utils/nodeSelector.js +5 -2
- package/dist/application/utils/stringUtils.cjs +1 -1
- package/dist/application/utils/stringUtils.js +1 -1
- package/dist/atoms/app/index.cjs +1 -1
- package/dist/atoms/app/index.d.cts +1 -1
- package/dist/atoms/app/index.js +1 -1
- package/dist/atoms/app/variant.cjs +1 -1
- package/dist/atoms/app/variant.d.cts +3 -3
- package/dist/atoms/app/variant.d.ts +3 -3
- package/dist/atoms/app/variant.js +1 -1
- package/dist/atoms/chat/chatState.d.cts +15 -15
- package/dist/atoms/chat/chatState.d.ts +15 -15
- package/dist/atoms/chat/form.d.cts +2 -2
- package/dist/atoms/chat/form.d.ts +2 -2
- package/dist/atoms/chat/index.cjs +1 -1
- package/dist/atoms/chat/index.d.cts +3 -3
- package/dist/atoms/chat/index.d.ts +2 -2
- package/dist/atoms/chat/index.js +1 -1
- package/dist/atoms/chat/lastMessage.d.cts +2 -2
- package/dist/atoms/chat/lastMessage.d.ts +2 -2
- package/dist/atoms/chat/messageQueue.cjs +1 -1
- package/dist/atoms/chat/messageQueue.js +1 -1
- package/dist/atoms/chat/performanceMetrics.d.cts +6 -6
- package/dist/atoms/chat/performanceMetrics.d.ts +6 -6
- package/dist/atoms/chat/renderedWidgetRefs.d.cts +2 -2
- package/dist/atoms/chat/renderedWidgetRefs.d.ts +2 -2
- package/dist/atoms/chat/replies.cjs +2 -2
- package/dist/atoms/chat/replies.d.cts +2 -2
- package/dist/atoms/chat/replies.d.ts +2 -2
- package/dist/atoms/chat/replies.js +2 -2
- package/dist/atoms/chat/suggestions.d.cts +2 -2
- package/dist/atoms/chat/suggestions.d.ts +2 -2
- package/dist/atoms/globalSearch/globalSearch.cjs +3 -1
- package/dist/atoms/globalSearch/globalSearch.d.cts +10 -7
- package/dist/atoms/globalSearch/globalSearch.d.ts +10 -7
- package/dist/atoms/globalSearch/globalSearch.js +3 -2
- package/dist/atoms/globalSearch/index.cjs +1 -0
- package/dist/atoms/globalSearch/index.d.cts +2 -2
- package/dist/atoms/globalSearch/index.d.ts +2 -2
- package/dist/atoms/globalSearch/index.js +2 -2
- package/dist/atoms/org/customerService.d.cts +7 -7
- package/dist/atoms/org/graphqlConfig.cjs +1 -1
- package/dist/atoms/org/graphqlConfig.d.cts +6 -6
- package/dist/atoms/org/graphqlConfig.d.ts +5 -5
- package/dist/atoms/org/graphqlConfig.js +1 -1
- package/dist/atoms/org/newOrgConfigAtom.d.cts +2 -2
- package/dist/atoms/org/newOrgConfigAtom.d.ts +2 -2
- package/dist/atoms/org/orgAnalyticsConfig.cjs +1 -1
- package/dist/atoms/org/orgAnalyticsConfig.d.cts +6 -6
- package/dist/atoms/org/orgAnalyticsConfig.d.ts +6 -6
- package/dist/atoms/org/orgAnalyticsConfig.js +1 -1
- package/dist/atoms/search/chatSearch.d.cts +17 -17
- package/dist/atoms/search/chatSearch.d.ts +17 -17
- package/dist/atoms/search/searchAPI.cjs +1 -1
- package/dist/atoms/search/searchAPI.d.cts +14 -14
- package/dist/atoms/search/searchAPI.d.ts +14 -14
- package/dist/atoms/search/searchAPI.js +1 -1
- package/dist/atoms/search/searchServiceAdapter.cjs +1 -1
- package/dist/atoms/search/searchServiceAdapter.js +1 -1
- package/dist/atoms/search/types.d.cts +1 -1
- package/dist/contexts/amplitudeContext/amplitudeContext.cjs +6 -6
- package/dist/contexts/amplitudeContext/amplitudeContext.js +6 -6
- package/dist/contexts/cdnContext/cdnContext.cjs +1 -1
- package/dist/contexts/cdnContext/cdnContext.js +1 -1
- package/dist/contexts/chatContext/chatContext.cjs +4 -4
- package/dist/contexts/chatContext/chatContext.d.cts +2 -2
- package/dist/contexts/chatContext/chatContext.js +4 -4
- package/dist/contexts/enviveConfigContext/enviveConfigContext.cjs +2 -3
- package/dist/contexts/enviveConfigContext/enviveConfigContext.js +2 -3
- package/dist/contexts/enviveContext/enviveContext.cjs +52 -0
- package/dist/contexts/enviveContext/enviveContext.d.cts +31 -0
- package/dist/contexts/enviveContext/enviveContext.d.ts +31 -0
- package/dist/contexts/enviveContext/enviveContext.js +51 -0
- package/dist/contexts/enviveContext/index.cjs +5 -0
- package/dist/contexts/enviveContext/index.d.cts +3 -0
- package/dist/contexts/enviveContext/index.d.ts +3 -0
- package/dist/contexts/enviveContext/index.js +4 -0
- package/dist/contexts/enviveContext/types.cjs +11 -0
- package/dist/contexts/enviveContext/types.d.cts +8 -0
- package/dist/contexts/enviveContext/types.d.ts +8 -0
- package/dist/contexts/enviveContext/types.js +10 -0
- package/dist/contexts/featureFlagContext/featureFlagContext.cjs +2 -2
- package/dist/contexts/featureFlagContext/featureFlagContext.js +2 -2
- package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.cjs +2 -2
- package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.js +2 -2
- package/dist/contexts/graphqlContext/graphqlContext.cjs +3 -3
- package/dist/contexts/graphqlContext/graphqlContext.js +3 -3
- package/dist/contexts/localStorageContext/localStorageContext.cjs +1 -1
- package/dist/contexts/localStorageContext/localStorageContext.js +1 -1
- package/dist/contexts/newOrgConfigContext/newOrgConfigContext.cjs +1 -1
- package/dist/contexts/newOrgConfigContext/newOrgConfigContext.js +1 -1
- package/dist/contexts/searchContext/searchContext.cjs +1 -1
- package/dist/contexts/searchContext/searchContext.js +1 -1
- package/dist/contexts/sessionStorageContext/sessionStorageContext.cjs +1 -1
- package/dist/contexts/sessionStorageContext/sessionStorageContext.js +1 -1
- package/dist/contexts/systemSettingsContext/systemSettingsContext.d.cts +2 -2
- package/dist/contexts/systemSettingsContext/systemSettingsContext.d.ts +2 -2
- package/dist/contexts/types.cjs +1 -1
- package/dist/contexts/types.d.cts +1 -1
- package/dist/contexts/types.d.ts +1 -1
- package/dist/contexts/types.js +1 -1
- package/dist/contexts/userIdentityContext/userIdentityContext.cjs +1 -1
- package/dist/contexts/userIdentityContext/userIdentityContext.js +1 -1
- package/dist/events/index.cjs +2 -4
- package/dist/events/index.js +2 -4
- package/dist/hooks/AmplitudeOperations/useAmplitudeOperations.cjs +1 -1
- package/dist/hooks/AmplitudeOperations/useAmplitudeOperations.js +1 -1
- package/dist/hooks/Debounce/useDebounce.cjs +1 -1
- package/dist/hooks/Debounce/useDebounce.js +1 -1
- package/dist/hooks/ElementObserver/useElementObserver.cjs +2 -2
- package/dist/hooks/ElementObserver/useElementObserver.js +2 -2
- package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.cts +2 -2
- package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.ts +2 -2
- package/dist/hooks/IdentifyUser/useIdentifyUser.cjs +1 -1
- package/dist/hooks/IdentifyUser/useIdentifyUser.js +1 -1
- package/dist/hooks/ImageResolver/useImageResolver.cjs +2 -2
- package/dist/hooks/ImageResolver/useImageResolver.js +2 -2
- package/dist/hooks/IsSmallScreen/useIsSmallScreen.cjs +1 -1
- package/dist/hooks/IsSmallScreen/useIsSmallScreen.js +1 -1
- package/dist/hooks/LocalStorageOperations/useLocalStorageOperations.cjs +11 -3
- package/dist/hooks/LocalStorageOperations/useLocalStorageOperations.js +11 -3
- package/dist/hooks/Search/useRecommendedProducts.cjs +1 -1
- package/dist/hooks/Search/useRecommendedProducts.js +1 -1
- package/dist/hooks/Search/useSearch.cjs +6 -3
- package/dist/hooks/Search/useSearch.d.cts +3 -1
- package/dist/hooks/Search/useSearch.d.ts +3 -1
- package/dist/hooks/Search/useSearch.js +7 -4
- package/dist/hooks/Search/useSearchInput.cjs +1 -1
- package/dist/hooks/Search/useSearchInput.js +1 -1
- package/dist/hooks/SearchOperations/useSearchOperations.cjs +1 -1
- package/dist/hooks/SearchOperations/useSearchOperations.d.cts +1 -1
- package/dist/hooks/SearchOperations/useSearchOperations.d.ts +1 -1
- package/dist/hooks/SearchOperations/useSearchOperations.js +1 -1
- package/dist/hooks/SessionStorageOperations/useSessionStorageOperations.cjs +1 -1
- package/dist/hooks/SessionStorageOperations/useSessionStorageOperations.js +1 -1
- package/dist/hooks/SystemSettingsContext/useSystemSettingsContext.d.cts +2 -2
- package/dist/hooks/SystemSettingsContext/useSystemSettingsContext.d.ts +2 -2
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.cjs +1 -1
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.js +1 -1
- package/dist/hooks/utils.cjs +3 -3
- package/dist/hooks/utils.d.cts +1 -1
- package/dist/hooks/utils.d.ts +1 -1
- package/dist/hooks/utils.js +3 -3
- package/package.json +25 -6
- package/src/application/commerce-api.ts +12 -10
- package/src/application/models/api/nextMessageRequest.ts +2 -0
- package/src/application/models/api/response.ts +4 -4
- package/src/application/models/api/search.ts +1 -2
- package/src/application/models/api/userEvent.ts +1 -2
- package/src/application/models/frontendConfig.ts +2 -2
- package/src/application/models/guards/api/isApiOrderResponseAttributes.ts +8 -4
- package/src/application/models/guards/api/isApiSearchEventAttributes.ts +1 -1
- package/src/application/models/index.ts +1 -0
- package/src/application/models/message.ts +5 -5
- package/src/application/models/utilityTypes/delimiterCase.ts +1 -0
- package/src/application/models/validators/validateGraphQLColorsConfig.ts +2 -1
- package/src/application/models/validators/validateGraphQLFrontendConfig.ts +1 -1
- package/src/application/models/variantInfo/variantInfo.ts +3 -0
- package/src/application/utils/analyticsUtils.ts +3 -3
- package/src/application/utils/domObserver.ts +1 -0
- package/src/application/utils/elementObserver.ts +1 -0
- package/src/application/utils/mutationHelper.ts +3 -1
- package/src/application/utils/nodeSelector.ts +7 -1
- package/src/application/utils/stringUtils.ts +1 -0
- package/src/atoms/app/index.ts +17 -6
- package/src/atoms/app/variant.ts +11 -13
- package/src/atoms/chat/__tests__/messageQueue.test.spec.ts +61 -0
- package/src/atoms/chat/index.ts +8 -8
- package/src/atoms/chat/messageQueue.ts +2 -48
- package/src/atoms/chat/replies.ts +3 -3
- package/src/atoms/org/graphqlConfig.ts +0 -1
- package/src/atoms/org/orgAnalyticsConfig.ts +1 -1
- package/src/atoms/search/searchAPI.ts +1 -1
- package/src/atoms/search/searchServiceAdapter.ts +1 -1
- package/src/contexts/amplitudeContext/__tests__/amplitudeContext.test.tsx +4 -3
- package/src/contexts/amplitudeContext/amplitudeContext.tsx +5 -5
- package/src/contexts/cdnContext/cdnContext.tsx +1 -1
- package/src/contexts/enviveConfigContext/__tests__/enviveConfigContext.test.tsx +2 -3
- package/src/contexts/enviveConfigContext/enviveConfigContext.tsx +3 -3
- package/src/contexts/enviveContext/enviveContext.tsx +89 -0
- package/src/contexts/enviveContext/index.ts +2 -0
- package/src/contexts/enviveContext/types.ts +4 -0
- package/src/contexts/featureFlagContext/featureFlagContext.tsx +1 -1
- package/src/contexts/featureFlagServiceContext/featureFlagServiceContext.tsx +1 -1
- package/src/contexts/graphqlContext/__tests__/graphqlContext.test.tsx +7 -5
- package/src/contexts/graphqlContext/graphqlContext.tsx +1 -5
- package/src/contexts/hardcopyContext/hardcopyContext.tsx +70 -0
- package/src/contexts/hardcopyContext/index.ts +1 -0
- package/src/contexts/localStorageContext/__tests__/localStorageContext.test.tsx +9 -3
- package/src/contexts/localStorageContext/localStorageContext.tsx +1 -1
- package/src/contexts/newOrgConfigContext/__tests__/newOrgConfigContext.test.tsx +2 -3
- package/src/contexts/newOrgConfigContext/newOrgConfigContext.tsx +1 -1
- package/src/contexts/pageContext/__tests__/pageContext.test.tsx +666 -0
- package/src/contexts/pageContext/index.ts +2 -0
- package/src/contexts/pageContext/mapping.ts +38 -0
- package/src/contexts/pageContext/pageContext.tsx +78 -0
- package/src/contexts/pageContext/types.ts +27 -0
- package/src/contexts/salesAgentContext/chatAPI.ts +75 -0
- package/src/contexts/salesAgentContext/index.ts +3 -0
- package/src/contexts/salesAgentContext/salesAgentContext.tsx +128 -0
- package/src/contexts/salesAgentContext/salesAgentService.ts +197 -0
- package/src/contexts/searchContext/__tests__/searchContext.test.tsx +14 -15
- package/src/contexts/searchContext/searchContext.tsx +1 -1
- package/src/contexts/sessionStorageContext/sessionStorageContext.tsx +1 -1
- package/src/contexts/systemSettingsContext/__tests__/systemSettingsContext.test.tsx +7 -6
- package/src/contexts/types.ts +20 -22
- package/src/contexts/userIdentityContext/__tests__/userIdentityContext.test.tsx +3 -8
- package/src/contexts/userIdentityContext/userIdentityContext.tsx +1 -1
- package/src/events/index.ts +5 -7
- package/src/hooks/AmplitudeOperations/useAmplitudeOperations.ts +1 -1
- package/src/hooks/Debounce/useDebounce.ts +1 -1
- package/src/hooks/ElementObserver/useElementObserver.ts +2 -1
- package/src/hooks/GraphQLConfig/useGraphQLConfig.ts +4 -2
- package/src/hooks/IdentifyUser/useIdentifyUser.ts +1 -1
- package/src/hooks/ImageResolver/useImageResolver.ts +2 -0
- package/src/hooks/IsSmallScreen/useIsSmallScreen.ts +1 -1
- package/src/hooks/LocalStorageOperations/useLocalStorageOperations.ts +20 -4
- package/src/hooks/Search/__tests__/useSearch.test.tsx +15 -10
- package/src/hooks/Search/useRecommendedProducts.ts +2 -2
- package/src/hooks/Search/useSearchInput.ts +1 -1
- package/src/hooks/SearchOperations/useSearchOperations.ts +2 -2
- package/src/hooks/SessionStorageOperations/useSessionStorageOperations.ts +1 -1
- package/src/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.ts +2 -2
- package/src/hooks/utils.ts +3 -2
- package/src/contexts/chatContext/chatContext.tsx +0 -463
- package/src/contexts/chatContext/index.ts +0 -1
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { act, render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import { Provider, useAtom } from 'jotai';
|
|
4
|
+
import { PageVisitCategory, UserEvent } from '@spiffy-ai/commerce-api-client';
|
|
5
|
+
import { urlResolverAtom, UrlResolverResponse } from 'src/atoms/app/variant';
|
|
6
|
+
import Logger from 'src/application/logging/logger';
|
|
7
|
+
import CommerceApiClient from 'src/application/commerce-api';
|
|
8
|
+
import { VariantTypeEnum } from 'src/application/models';
|
|
9
|
+
import { PageProvider, usePage } from '../pageContext';
|
|
10
|
+
import { mapUrlResolverResponseToVariantInfo } from '../mapping';
|
|
11
|
+
|
|
12
|
+
// Mock the Logger to avoid console output in tests
|
|
13
|
+
vi.spyOn(Logger, 'logInfo').mockImplementation(() => {});
|
|
14
|
+
vi.spyOn(Logger, 'logWarn').mockImplementation(() => {});
|
|
15
|
+
vi.spyOn(Logger, 'logError').mockImplementation(() => {});
|
|
16
|
+
|
|
17
|
+
// Mock CommerceApiClient
|
|
18
|
+
const mockResolveUrl = vi.fn();
|
|
19
|
+
vi.mock('src/application/commerce-api', () => ({
|
|
20
|
+
default: {
|
|
21
|
+
resolveUrl: vi.fn(),
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
// Component that uses the usePage hook
|
|
26
|
+
const MockComponent: React.FC = () => {
|
|
27
|
+
const context = usePage();
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div data-testid="mock-component">
|
|
31
|
+
<div data-testid="page-url">{context.pageUrl || 'undefined'}</div>
|
|
32
|
+
<div data-testid="variant-info">
|
|
33
|
+
{context.variantInfo ? JSON.stringify(context.variantInfo) : 'undefined'}
|
|
34
|
+
</div>
|
|
35
|
+
<div data-testid="user-event">
|
|
36
|
+
{context.userEvent ? JSON.stringify(context.userEvent) : 'undefined'}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Component that directly reads from the urlResolverAtom to verify it's set
|
|
43
|
+
const AtomReaderComponent: React.FC = () => {
|
|
44
|
+
const [urlResolverResponse] = useAtom(urlResolverAtom);
|
|
45
|
+
const cacheString = JSON.stringify(urlResolverResponse);
|
|
46
|
+
|
|
47
|
+
return <div data-testid="atom-reader">{cacheString}</div>;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
describe('PageProvider', () => {
|
|
51
|
+
const originalLocation = window.location;
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
vi.clearAllMocks();
|
|
55
|
+
mockResolveUrl.mockClear();
|
|
56
|
+
(CommerceApiClient.resolveUrl as ReturnType<typeof vi.fn>).mockImplementation(mockResolveUrl);
|
|
57
|
+
|
|
58
|
+
// Reset window.location
|
|
59
|
+
delete (window as { location?: Location }).location;
|
|
60
|
+
// @ts-expect-error - window.location is a string & Location
|
|
61
|
+
window.location = {
|
|
62
|
+
...originalLocation,
|
|
63
|
+
href: 'https://example.com/test-page',
|
|
64
|
+
} as unknown as Location;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
// @ts-expect-error - window.location is a string & Location
|
|
69
|
+
window.location = originalLocation;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const renderWithProviders = (children: React.ReactNode) => {
|
|
73
|
+
return render(
|
|
74
|
+
<Provider>
|
|
75
|
+
<PageProvider>{children}</PageProvider>
|
|
76
|
+
</Provider>,
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
describe('PageProvider Initialization', () => {
|
|
81
|
+
it('should initialize with pageUrl from window.location.href', async () => {
|
|
82
|
+
window.location.href = 'https://example.com/initial-page';
|
|
83
|
+
|
|
84
|
+
renderWithProviders(<MockComponent />);
|
|
85
|
+
|
|
86
|
+
await waitFor(() => {
|
|
87
|
+
expect(screen.getByTestId('page-url').textContent).toBe('https://example.com/initial-page');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should handle missing window.location gracefully', async () => {
|
|
92
|
+
// This test verifies that the component handles edge cases
|
|
93
|
+
// In a real browser environment, window.location is always available
|
|
94
|
+
// This test ensures the component doesn't crash in unusual test scenarios
|
|
95
|
+
const originalLocation = window.location;
|
|
96
|
+
|
|
97
|
+
// Temporarily remove location
|
|
98
|
+
delete (window as { location?: Location }).location;
|
|
99
|
+
|
|
100
|
+
// Restore it immediately since the component needs it
|
|
101
|
+
// @ts-expect-error - window.location is a string & Location
|
|
102
|
+
window.location = originalLocation;
|
|
103
|
+
|
|
104
|
+
renderWithProviders(<MockComponent />);
|
|
105
|
+
|
|
106
|
+
// Should render successfully
|
|
107
|
+
expect(screen.getByTestId('mock-component')).toBeInTheDocument();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('URL Resolution', () => {
|
|
112
|
+
it('should call CommerceApiClient.resolveUrl when pageUrl is set', async () => {
|
|
113
|
+
const mockResponse: UrlResolverResponse = {
|
|
114
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
115
|
+
specific_details: {},
|
|
116
|
+
ready: true,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
120
|
+
|
|
121
|
+
window.location.href = 'https://example.com/test';
|
|
122
|
+
|
|
123
|
+
renderWithProviders(<MockComponent />);
|
|
124
|
+
|
|
125
|
+
await waitFor(() => {
|
|
126
|
+
expect(mockResolveUrl).toHaveBeenCalledWith('https://example.com/test');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should normalize URL to lowercase and trim before resolving', async () => {
|
|
131
|
+
const mockResponse: UrlResolverResponse = {
|
|
132
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
133
|
+
specific_details: {},
|
|
134
|
+
ready: true,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
138
|
+
|
|
139
|
+
window.location.href = ' HTTPS://EXAMPLE.COM/TEST ';
|
|
140
|
+
|
|
141
|
+
renderWithProviders(<MockComponent />);
|
|
142
|
+
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(mockResolveUrl).toHaveBeenCalledWith('https://example.com/test');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should use cached response from urlResolverAtom if available', async () => {
|
|
149
|
+
const cachedResponse: UrlResolverResponse = {
|
|
150
|
+
variant_type: VariantTypeEnum.Pdp,
|
|
151
|
+
specific_details: {
|
|
152
|
+
pdp_attributes: {
|
|
153
|
+
product_id: 'cached-product-123',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
ready: true,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
window.location.href = 'https://example.com/cached-page';
|
|
160
|
+
|
|
161
|
+
// Set up the atom with cached data before rendering
|
|
162
|
+
const TestWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
163
|
+
const [, setUrlResolver] = useAtom(urlResolverAtom);
|
|
164
|
+
|
|
165
|
+
React.useEffect(() => {
|
|
166
|
+
setUrlResolver({
|
|
167
|
+
url: 'https://example.com/cached-page',
|
|
168
|
+
response: cachedResponse,
|
|
169
|
+
});
|
|
170
|
+
}, [setUrlResolver]);
|
|
171
|
+
|
|
172
|
+
return <PageProvider>{children}</PageProvider>;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
render(
|
|
176
|
+
<Provider>
|
|
177
|
+
<TestWrapper>
|
|
178
|
+
<MockComponent />
|
|
179
|
+
</TestWrapper>
|
|
180
|
+
</Provider>,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// Should not call resolveUrl since it's cached
|
|
184
|
+
await waitFor(() => {
|
|
185
|
+
expect(mockResolveUrl).not.toHaveBeenCalled();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Should use cached variant info
|
|
189
|
+
await waitFor(() => {
|
|
190
|
+
const variantInfo = screen.getByTestId('variant-info').textContent;
|
|
191
|
+
expect(variantInfo).toContain('cached-product-123');
|
|
192
|
+
expect(variantInfo).toContain(VariantTypeEnum.Pdp);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should not resolve URL when pageUrl is empty string', async () => {
|
|
197
|
+
// Test with an empty URL string
|
|
198
|
+
window.location.href = '';
|
|
199
|
+
|
|
200
|
+
renderWithProviders(<MockComponent />);
|
|
201
|
+
|
|
202
|
+
// Wait for initial render
|
|
203
|
+
await waitFor(() => {
|
|
204
|
+
expect(screen.getByTestId('mock-component')).toBeInTheDocument();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Empty string is falsy, so resolveUrl should not be called
|
|
208
|
+
// The component checks `if (!pageUrl)` which treats empty string as falsy
|
|
209
|
+
await act(async () => {
|
|
210
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
expect(mockResolveUrl).not.toHaveBeenCalled();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('Variant Info Mapping', () => {
|
|
218
|
+
it('should map PDP variant response correctly', async () => {
|
|
219
|
+
const mockResponse: UrlResolverResponse = {
|
|
220
|
+
variant_type: VariantTypeEnum.Pdp,
|
|
221
|
+
specific_details: {
|
|
222
|
+
pdp_attributes: {
|
|
223
|
+
product_id: 'product-123',
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
ready: true,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
230
|
+
window.location.href = 'https://example.com/product';
|
|
231
|
+
|
|
232
|
+
renderWithProviders(<MockComponent />);
|
|
233
|
+
|
|
234
|
+
await waitFor(() => {
|
|
235
|
+
const variantInfo = screen.getByTestId('variant-info').textContent;
|
|
236
|
+
expect(variantInfo).toContain('product-123');
|
|
237
|
+
expect(variantInfo).toContain(VariantTypeEnum.Pdp);
|
|
238
|
+
expect(variantInfo).toContain('https://example.com/product');
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should map PLP variant response correctly', async () => {
|
|
243
|
+
const mockResponse: UrlResolverResponse = {
|
|
244
|
+
variant_type: VariantTypeEnum.Plp,
|
|
245
|
+
specific_details: {
|
|
246
|
+
plp_attributes: {
|
|
247
|
+
attributes: {
|
|
248
|
+
id: 'plp-456',
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
ready: true,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
256
|
+
window.location.href = 'https://example.com/category';
|
|
257
|
+
|
|
258
|
+
renderWithProviders(<MockComponent />);
|
|
259
|
+
|
|
260
|
+
await waitFor(() => {
|
|
261
|
+
const variantInfo = screen.getByTestId('variant-info').textContent;
|
|
262
|
+
expect(variantInfo).toContain('plp-456');
|
|
263
|
+
expect(variantInfo).toContain(VariantTypeEnum.Plp);
|
|
264
|
+
expect(variantInfo).toContain('https://example.com/category');
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should map PageVisit variant response correctly', async () => {
|
|
269
|
+
const mockResponse: UrlResolverResponse = {
|
|
270
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
271
|
+
specific_details: {},
|
|
272
|
+
ready: true,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
276
|
+
window.location.href = 'https://example.com/page';
|
|
277
|
+
|
|
278
|
+
renderWithProviders(<MockComponent />);
|
|
279
|
+
|
|
280
|
+
await waitFor(() => {
|
|
281
|
+
const variantInfo = screen.getByTestId('variant-info').textContent;
|
|
282
|
+
expect(variantInfo).toContain(VariantTypeEnum.PageVisit);
|
|
283
|
+
expect(variantInfo).toContain('https://example.com/page');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should handle PDP variant with missing product_id', async () => {
|
|
288
|
+
const mockResponse: UrlResolverResponse = {
|
|
289
|
+
variant_type: VariantTypeEnum.Pdp,
|
|
290
|
+
specific_details: {
|
|
291
|
+
pdp_attributes: {},
|
|
292
|
+
},
|
|
293
|
+
ready: true,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
297
|
+
window.location.href = 'https://example.com/product';
|
|
298
|
+
|
|
299
|
+
renderWithProviders(<MockComponent />);
|
|
300
|
+
|
|
301
|
+
await waitFor(() => {
|
|
302
|
+
const variantInfo = screen.getByTestId('variant-info').textContent;
|
|
303
|
+
expect(variantInfo).toContain(VariantTypeEnum.Pdp);
|
|
304
|
+
expect(variantInfo).toContain('""'); // Empty string for productId
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should handle PLP variant with missing plp_id', async () => {
|
|
309
|
+
const mockResponse: UrlResolverResponse = {
|
|
310
|
+
variant_type: VariantTypeEnum.Plp,
|
|
311
|
+
specific_details: {
|
|
312
|
+
plp_attributes: {},
|
|
313
|
+
},
|
|
314
|
+
ready: true,
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
318
|
+
window.location.href = 'https://example.com/category';
|
|
319
|
+
|
|
320
|
+
renderWithProviders(<MockComponent />);
|
|
321
|
+
|
|
322
|
+
await waitFor(() => {
|
|
323
|
+
const variantInfo = screen.getByTestId('variant-info').textContent;
|
|
324
|
+
expect(variantInfo).toContain(VariantTypeEnum.Plp);
|
|
325
|
+
expect(variantInfo).toContain('""'); // Empty string for plpId
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe('User Event Handling', () => {
|
|
331
|
+
it('should set userEvent when provided in response', async () => {
|
|
332
|
+
const mockUserEvent = {
|
|
333
|
+
event_id: 'event-123',
|
|
334
|
+
event_type: PageVisitCategory.Other,
|
|
335
|
+
} as unknown as UserEvent;
|
|
336
|
+
|
|
337
|
+
const mockResponse: UrlResolverResponse = {
|
|
338
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
339
|
+
specific_details: {},
|
|
340
|
+
ready: true,
|
|
341
|
+
user_event: mockUserEvent,
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
345
|
+
window.location.href = 'https://example.com/page';
|
|
346
|
+
|
|
347
|
+
renderWithProviders(<MockComponent />);
|
|
348
|
+
|
|
349
|
+
await waitFor(() => {
|
|
350
|
+
const userEvent = screen.getByTestId('user-event').textContent;
|
|
351
|
+
expect(userEvent).toContain('event-123');
|
|
352
|
+
expect(userEvent).toContain(PageVisitCategory.Other);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should set userEvent to undefined when not provided in response', async () => {
|
|
357
|
+
const mockResponse: UrlResolverResponse = {
|
|
358
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
359
|
+
specific_details: {},
|
|
360
|
+
ready: true,
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
364
|
+
window.location.href = 'https://example.com/page';
|
|
365
|
+
|
|
366
|
+
renderWithProviders(<MockComponent />);
|
|
367
|
+
|
|
368
|
+
await waitFor(() => {
|
|
369
|
+
const userEvent = screen.getByTestId('user-event').textContent;
|
|
370
|
+
expect(userEvent).toBe('undefined');
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
describe('Error Handling', () => {
|
|
376
|
+
it('should log error and not crash when resolveUrl fails', async () => {
|
|
377
|
+
const errorSpy = vi.spyOn(Logger, 'logError');
|
|
378
|
+
const error = new Error('Network error');
|
|
379
|
+
mockResolveUrl.mockRejectedValue(error);
|
|
380
|
+
|
|
381
|
+
window.location.href = 'https://example.com/error-page';
|
|
382
|
+
|
|
383
|
+
renderWithProviders(<MockComponent />);
|
|
384
|
+
|
|
385
|
+
await waitFor(() => {
|
|
386
|
+
expect(errorSpy).toHaveBeenCalledWith('Failed to resolve page URL', error, {
|
|
387
|
+
pageUrl: 'https://example.com/error-page',
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Component should still render
|
|
392
|
+
expect(screen.getByTestId('mock-component')).toBeInTheDocument();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('should handle invalid variant type gracefully', async () => {
|
|
396
|
+
// This test verifies that the mapping function throws an error for invalid types
|
|
397
|
+
// The error should be caught and logged
|
|
398
|
+
const errorSpy = vi.spyOn(Logger, 'logError');
|
|
399
|
+
|
|
400
|
+
const invalidResponse = {
|
|
401
|
+
variant_type: 'invalid_type',
|
|
402
|
+
specific_details: {},
|
|
403
|
+
ready: true,
|
|
404
|
+
} as unknown as UrlResolverResponse;
|
|
405
|
+
|
|
406
|
+
mockResolveUrl.mockResolvedValue(invalidResponse);
|
|
407
|
+
window.location.href = 'https://example.com/invalid';
|
|
408
|
+
|
|
409
|
+
renderWithProviders(<MockComponent />);
|
|
410
|
+
|
|
411
|
+
// The mapping function should throw, which will be caught in the useEffect
|
|
412
|
+
await waitFor(() => {
|
|
413
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
describe('usePage Hook', () => {
|
|
419
|
+
it('should throw error when used outside of PageProvider', () => {
|
|
420
|
+
// Suppress console.error for this test
|
|
421
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
422
|
+
|
|
423
|
+
expect(() => {
|
|
424
|
+
render(
|
|
425
|
+
<Provider>
|
|
426
|
+
<MockComponent />
|
|
427
|
+
</Provider>,
|
|
428
|
+
);
|
|
429
|
+
}).toThrow('usePage must be used within a PageProvider');
|
|
430
|
+
|
|
431
|
+
consoleSpy.mockRestore();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('should return context values when used within PageProvider', async () => {
|
|
435
|
+
const mockResponse: UrlResolverResponse = {
|
|
436
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
437
|
+
specific_details: {},
|
|
438
|
+
ready: true,
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
442
|
+
window.location.href = 'https://example.com/test';
|
|
443
|
+
|
|
444
|
+
renderWithProviders(<MockComponent />);
|
|
445
|
+
|
|
446
|
+
await waitFor(() => {
|
|
447
|
+
expect(screen.getByTestId('page-url').textContent).toBe('https://example.com/test');
|
|
448
|
+
expect(screen.getByTestId('variant-info').textContent).not.toBe('undefined');
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('should update context when pageUrl changes', async () => {
|
|
453
|
+
const mockResponse1: UrlResolverResponse = {
|
|
454
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
455
|
+
specific_details: {},
|
|
456
|
+
ready: true,
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const mockResponse2: UrlResolverResponse = {
|
|
460
|
+
variant_type: VariantTypeEnum.Pdp,
|
|
461
|
+
specific_details: {
|
|
462
|
+
pdp_attributes: {
|
|
463
|
+
product_id: 'product-789',
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
ready: true,
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
mockResolveUrl.mockResolvedValueOnce(mockResponse1).mockResolvedValueOnce(mockResponse2);
|
|
470
|
+
|
|
471
|
+
window.location.href = 'https://example.com/page1';
|
|
472
|
+
|
|
473
|
+
const { rerender } = renderWithProviders(<MockComponent />);
|
|
474
|
+
|
|
475
|
+
await waitFor(() => {
|
|
476
|
+
expect(screen.getByTestId('page-url').textContent).toBe('https://example.com/page1');
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Change the URL
|
|
480
|
+
window.location.href = 'https://example.com/page2';
|
|
481
|
+
|
|
482
|
+
// Note: In a real SPA, the URL change would trigger the useEffect
|
|
483
|
+
// For testing, we need to manually trigger or test the effect differently
|
|
484
|
+
// This test demonstrates the expected behavior
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
describe('Context Value Memoization', () => {
|
|
489
|
+
it('should memoize context value to prevent unnecessary re-renders', async () => {
|
|
490
|
+
const mockResponse: UrlResolverResponse = {
|
|
491
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
492
|
+
specific_details: {},
|
|
493
|
+
ready: true,
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
497
|
+
window.location.href = 'https://example.com/memo-test';
|
|
498
|
+
|
|
499
|
+
let renderCount = 0;
|
|
500
|
+
const CountingComponent: React.FC = () => {
|
|
501
|
+
renderCount++;
|
|
502
|
+
const context = usePage();
|
|
503
|
+
return <div data-testid="count">{renderCount}</div>;
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
renderWithProviders(<CountingComponent />);
|
|
507
|
+
|
|
508
|
+
await waitFor(() => {
|
|
509
|
+
expect(screen.getByTestId('count')).toBeInTheDocument();
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
// The component should render, and the context value should be stable
|
|
513
|
+
const initialCount = renderCount;
|
|
514
|
+
|
|
515
|
+
// Trigger a re-render (simulating parent re-render)
|
|
516
|
+
await act(async () => {
|
|
517
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
// The context value should be memoized, so additional renders
|
|
521
|
+
// should be minimal (only from initial setup)
|
|
522
|
+
expect(renderCount).toBeGreaterThanOrEqual(initialCount);
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
describe('Integration with urlResolverAtom', () => {
|
|
527
|
+
it('should update urlResolverAtom when resolving new URL', async () => {
|
|
528
|
+
const mockResponse: UrlResolverResponse = {
|
|
529
|
+
variant_type: VariantTypeEnum.Pdp,
|
|
530
|
+
specific_details: {
|
|
531
|
+
pdp_attributes: {
|
|
532
|
+
product_id: 'product-999',
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
ready: true,
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
mockResolveUrl.mockResolvedValue(mockResponse);
|
|
539
|
+
window.location.href = 'https://example.com/new-page';
|
|
540
|
+
|
|
541
|
+
render(
|
|
542
|
+
<Provider>
|
|
543
|
+
<PageProvider>
|
|
544
|
+
<MockComponent />
|
|
545
|
+
<AtomReaderComponent />
|
|
546
|
+
</PageProvider>
|
|
547
|
+
</Provider>,
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
await waitFor(() => {
|
|
551
|
+
expect(mockResolveUrl).toHaveBeenCalled();
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
// The atom should eventually contain the cached response
|
|
555
|
+
// Note: The actual caching happens in the atom setter, which may
|
|
556
|
+
// require additional setup to test directly
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
describe('mapUrlResolverResponseToVariantInfo', () => {
|
|
562
|
+
it('should map PDP variant correctly', () => {
|
|
563
|
+
const response: UrlResolverResponse = {
|
|
564
|
+
variant_type: VariantTypeEnum.Pdp,
|
|
565
|
+
specific_details: {
|
|
566
|
+
pdp_attributes: {
|
|
567
|
+
product_id: 'test-product-123',
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
ready: true,
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
const result = mapUrlResolverResponseToVariantInfo('https://example.com/product', response);
|
|
574
|
+
|
|
575
|
+
expect(result).toEqual({
|
|
576
|
+
variantType: VariantTypeEnum.Pdp,
|
|
577
|
+
productId: 'test-product-123',
|
|
578
|
+
url: 'https://example.com/product',
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should map PLP variant correctly', () => {
|
|
583
|
+
const response: UrlResolverResponse = {
|
|
584
|
+
variant_type: VariantTypeEnum.Plp,
|
|
585
|
+
specific_details: {
|
|
586
|
+
plp_attributes: {
|
|
587
|
+
attributes: {
|
|
588
|
+
id: 'test-plp-456',
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
ready: true,
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const result = mapUrlResolverResponseToVariantInfo('https://example.com/category', response);
|
|
596
|
+
|
|
597
|
+
expect(result).toEqual({
|
|
598
|
+
variantType: VariantTypeEnum.Plp,
|
|
599
|
+
plpId: 'test-plp-456',
|
|
600
|
+
url: 'https://example.com/category',
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it('should map PageVisit variant correctly', () => {
|
|
605
|
+
const response: UrlResolverResponse = {
|
|
606
|
+
variant_type: VariantTypeEnum.PageVisit,
|
|
607
|
+
specific_details: {},
|
|
608
|
+
ready: true,
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
const result = mapUrlResolverResponseToVariantInfo('https://example.com/page', response);
|
|
612
|
+
|
|
613
|
+
expect(result).toEqual({
|
|
614
|
+
variantType: VariantTypeEnum.PageVisit,
|
|
615
|
+
url: 'https://example.com/page',
|
|
616
|
+
});
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('should handle PDP with missing product_id', () => {
|
|
620
|
+
const response: UrlResolverResponse = {
|
|
621
|
+
variant_type: VariantTypeEnum.Pdp,
|
|
622
|
+
specific_details: {
|
|
623
|
+
pdp_attributes: {},
|
|
624
|
+
},
|
|
625
|
+
ready: true,
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
const result = mapUrlResolverResponseToVariantInfo('https://example.com/product', response);
|
|
629
|
+
|
|
630
|
+
expect(result).toEqual({
|
|
631
|
+
variantType: VariantTypeEnum.Pdp,
|
|
632
|
+
productId: '',
|
|
633
|
+
url: 'https://example.com/product',
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it('should handle PLP with missing plp_id', () => {
|
|
638
|
+
const response: UrlResolverResponse = {
|
|
639
|
+
variant_type: VariantTypeEnum.Plp,
|
|
640
|
+
specific_details: {
|
|
641
|
+
plp_attributes: {},
|
|
642
|
+
},
|
|
643
|
+
ready: true,
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
const result = mapUrlResolverResponseToVariantInfo('https://example.com/category', response);
|
|
647
|
+
|
|
648
|
+
expect(result).toEqual({
|
|
649
|
+
variantType: VariantTypeEnum.Plp,
|
|
650
|
+
plpId: '',
|
|
651
|
+
url: 'https://example.com/category',
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it('should throw error for invalid variant type', () => {
|
|
656
|
+
const response = {
|
|
657
|
+
variant_type: 'invalid_type',
|
|
658
|
+
specific_details: {},
|
|
659
|
+
ready: true,
|
|
660
|
+
} as unknown as UrlResolverResponse;
|
|
661
|
+
|
|
662
|
+
expect(() => {
|
|
663
|
+
mapUrlResolverResponseToVariantInfo('https://example.com/page', response);
|
|
664
|
+
}).toThrow('Invalid variant type');
|
|
665
|
+
});
|
|
666
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { UrlResolverResponse } from 'src/atoms/app/variant';
|
|
2
|
+
|
|
3
|
+
import { VariantTypeEnum } from 'src/application/models';
|
|
4
|
+
|
|
5
|
+
import { PageVariantInfo, VisitPageVariantInfo } from './types';
|
|
6
|
+
|
|
7
|
+
export const mapUrlResolverResponseToVariantInfo = (
|
|
8
|
+
url: string,
|
|
9
|
+
response: UrlResolverResponse,
|
|
10
|
+
): PageVariantInfo => {
|
|
11
|
+
if (
|
|
12
|
+
response.variant_type === VariantTypeEnum.Pdp &&
|
|
13
|
+
'pdp_attributes' in response.specific_details
|
|
14
|
+
) {
|
|
15
|
+
return {
|
|
16
|
+
variantType: VariantTypeEnum.Pdp,
|
|
17
|
+
productId: response.specific_details.pdp_attributes.product_id ?? '',
|
|
18
|
+
url,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
if (
|
|
22
|
+
response.variant_type === VariantTypeEnum.Plp &&
|
|
23
|
+
'plp_attributes' in response.specific_details
|
|
24
|
+
) {
|
|
25
|
+
return {
|
|
26
|
+
variantType: VariantTypeEnum.Plp,
|
|
27
|
+
plpId: response.specific_details.plp_attributes?.attributes?.id ?? '',
|
|
28
|
+
url,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (response.variant_type === VariantTypeEnum.PageVisit) {
|
|
32
|
+
return {
|
|
33
|
+
variantType: VariantTypeEnum.PageVisit,
|
|
34
|
+
url,
|
|
35
|
+
} as VisitPageVariantInfo;
|
|
36
|
+
}
|
|
37
|
+
throw new Error('Invalid variant type');
|
|
38
|
+
};
|