@tagadapay/plugin-sdk 3.1.12 → 3.1.24

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.
Files changed (144) hide show
  1. package/build-cdn.js +397 -11
  2. package/dist/data/iso3166.d.ts +23 -33
  3. package/dist/data/iso3166.js +134 -198
  4. package/dist/data/languages.d.ts +5 -64
  5. package/dist/data/languages.js +23 -143
  6. package/dist/external-tracker.js +623 -3426
  7. package/dist/external-tracker.min.js +2 -25
  8. package/dist/external-tracker.min.js.map +4 -4
  9. package/dist/react/config/payment.d.ts +14 -4
  10. package/dist/react/config/payment.js +47 -9
  11. package/dist/react/hooks/useCheckout.d.ts +3 -0
  12. package/dist/react/hooks/useCheckout.js +4 -1
  13. package/dist/react/hooks/useISOData.js +1 -1
  14. package/dist/react/hooks/usePaymentPolling.d.ts +3 -3
  15. package/dist/react/hooks/usePluginConfig.js +9 -10
  16. package/dist/react/providers/TagadaProvider.js +1 -1
  17. package/dist/tagada-react-sdk-minimal.min.js +36 -0
  18. package/dist/tagada-react-sdk-minimal.min.js.map +7 -0
  19. package/dist/tagada-react-sdk.js +37821 -0
  20. package/dist/tagada-react-sdk.min.js +78 -0
  21. package/dist/tagada-react-sdk.min.js.map +7 -0
  22. package/dist/tagada-sdk.js +16044 -0
  23. package/dist/tagada-sdk.min.js +32 -0
  24. package/dist/tagada-sdk.min.js.map +7 -0
  25. package/dist/v2/cdn-react-minimal.d.ts +23 -0
  26. package/dist/v2/cdn-react-minimal.js +26 -0
  27. package/dist/v2/core/client.d.ts +4 -2
  28. package/dist/v2/core/client.js +5 -4
  29. package/dist/v2/core/config/environment.js +2 -1
  30. package/dist/v2/core/errors.d.ts +75 -0
  31. package/dist/v2/core/errors.js +104 -0
  32. package/dist/v2/core/funnelClient.d.ts +100 -10
  33. package/dist/v2/core/funnelClient.js +121 -27
  34. package/dist/v2/core/isoData.d.ts +4 -4
  35. package/dist/v2/core/isoData.js +7 -7
  36. package/dist/v2/core/pixelMapping.d.ts +49 -0
  37. package/dist/v2/core/pixelMapping.js +363 -0
  38. package/dist/v2/core/resources/apiClient.d.ts +2 -0
  39. package/dist/v2/core/resources/apiClient.js +52 -9
  40. package/dist/v2/core/resources/checkout.d.ts +99 -30
  41. package/dist/v2/core/resources/checkout.js +14 -0
  42. package/dist/v2/core/resources/customer.d.ts +20 -19
  43. package/dist/v2/core/resources/expressPaymentMethods.d.ts +1 -0
  44. package/dist/v2/core/resources/funnel.d.ts +17 -17
  45. package/dist/v2/core/resources/payments.d.ts +89 -13
  46. package/dist/v2/core/resources/payments.js +27 -9
  47. package/dist/v2/core/resources/postPurchases.d.ts +17 -0
  48. package/dist/v2/core/resources/postPurchases.js +20 -0
  49. package/dist/v2/core/types.d.ts +50 -12
  50. package/dist/v2/core/types.js +0 -3
  51. package/dist/v2/core/utils/checkout.d.ts +2 -2
  52. package/dist/v2/core/utils/checkout.js +7 -2
  53. package/dist/v2/core/utils/currency.d.ts +14 -0
  54. package/dist/v2/core/utils/currency.js +40 -0
  55. package/dist/v2/core/utils/deviceInfo.d.ts +0 -10
  56. package/dist/v2/core/utils/deviceInfo.js +152 -76
  57. package/dist/v2/core/utils/index.d.ts +1 -0
  58. package/dist/v2/core/utils/index.js +2 -0
  59. package/dist/v2/core/utils/order.d.ts +13 -9
  60. package/dist/v2/core/utils/pluginConfig.d.ts +8 -0
  61. package/dist/v2/core/utils/pluginConfig.js +36 -12
  62. package/dist/v2/index.d.ts +6 -3
  63. package/dist/v2/index.js +4 -2
  64. package/dist/v2/react/components/FunnelScriptInjector.js +166 -77
  65. package/dist/v2/react/components/StripeExpressButton.d.ts +13 -0
  66. package/dist/v2/react/components/StripeExpressButton.js +171 -0
  67. package/dist/v2/react/components/WhopCheckout.d.ts +24 -0
  68. package/dist/v2/react/components/WhopCheckout.js +237 -0
  69. package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +1 -1
  70. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.d.ts +14 -0
  71. package/dist/v2/react/hooks/payment-actions/useAirwallexRadarAction.js +181 -0
  72. package/dist/v2/react/hooks/payment-actions/useErrorAction.d.ts +9 -0
  73. package/dist/v2/react/hooks/payment-actions/useErrorAction.js +21 -0
  74. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.d.ts +14 -0
  75. package/dist/v2/react/hooks/payment-actions/useFinixRadarAction.js +187 -0
  76. package/dist/v2/react/hooks/payment-actions/useKessPayAction.d.ts +11 -0
  77. package/dist/v2/react/hooks/payment-actions/useKessPayAction.js +91 -0
  78. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.d.ts +24 -0
  79. package/dist/v2/react/hooks/payment-actions/useMasterCardAction.js +221 -0
  80. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.d.ts +15 -0
  81. package/dist/v2/react/hooks/payment-actions/usePaymentActionHandler.js +142 -0
  82. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.d.ts +3 -0
  83. package/dist/v2/react/hooks/payment-actions/useProcessorAuthAction.js +31 -0
  84. package/dist/v2/react/hooks/payment-actions/useRedirectAction.d.ts +10 -0
  85. package/dist/v2/react/hooks/payment-actions/useRedirectAction.js +35 -0
  86. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.d.ts +14 -0
  87. package/dist/v2/react/hooks/payment-actions/useStripeRadarAction.js +192 -0
  88. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.d.ts +14 -0
  89. package/dist/v2/react/hooks/payment-actions/useThreedsAuthAction.js +81 -0
  90. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.d.ts +11 -0
  91. package/dist/v2/react/hooks/payment-actions/useTrustFlowAction.js +84 -0
  92. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.d.ts +14 -0
  93. package/dist/v2/react/hooks/payment-processing/usePaymentInstruments.js +36 -0
  94. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.d.ts +31 -0
  95. package/dist/v2/react/hooks/payment-processing/usePaymentProcessors.js +212 -0
  96. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.d.ts +14 -0
  97. package/dist/v2/react/hooks/payment-redirect/useAirwallex3dsReturn.js +207 -0
  98. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.d.ts +12 -0
  99. package/dist/v2/react/hooks/payment-redirect/useGenericPaymentReturn.js +101 -0
  100. package/dist/v2/react/hooks/useApplePayCheckout.js +8 -8
  101. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +16 -0
  102. package/dist/v2/react/hooks/useCheckoutQuery.js +63 -10
  103. package/dist/v2/react/hooks/useFunnel.d.ts +15 -4
  104. package/dist/v2/react/hooks/useFunnel.js +8 -4
  105. package/dist/v2/react/hooks/useGeoLocation.d.ts +2 -1
  106. package/dist/v2/react/hooks/useGeoLocation.js +4 -2
  107. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +2 -0
  108. package/dist/v2/react/hooks/useGoogleAutocomplete.js +29 -15
  109. package/dist/v2/react/hooks/useISOData.d.ts +2 -5
  110. package/dist/v2/react/hooks/useISOData.js +26 -27
  111. package/dist/v2/react/hooks/usePaymentPolling.d.ts +3 -3
  112. package/dist/v2/react/hooks/usePaymentQuery.d.ts +18 -5
  113. package/dist/v2/react/hooks/usePaymentQuery.js +63 -1015
  114. package/dist/v2/react/hooks/usePaymentRetrieve.d.ts +3 -2
  115. package/dist/v2/react/hooks/usePaymentRetrieve.js +3 -1
  116. package/dist/v2/react/hooks/usePixelTracking.d.ts +5 -48
  117. package/dist/v2/react/hooks/usePixelTracking.js +283 -504
  118. package/dist/v2/react/hooks/usePostPurchasesQuery.js +34 -2
  119. package/dist/v2/react/hooks/useRemappableParams.d.ts +2 -6
  120. package/dist/v2/react/hooks/useRemappableParams.js +23 -23
  121. package/dist/v2/react/hooks/useSetPaymentMethod.d.ts +16 -0
  122. package/dist/v2/react/hooks/useSetPaymentMethod.js +33 -0
  123. package/dist/v2/react/hooks/useShippingRatesQuery.js +13 -5
  124. package/dist/v2/react/hooks/useStepConfig.d.ts +23 -6
  125. package/dist/v2/react/hooks/useStepConfig.js +14 -7
  126. package/dist/v2/react/hooks/useTranslation.js +23 -8
  127. package/dist/v2/react/hooks/useWhopPaymentPolling.d.ts +30 -0
  128. package/dist/v2/react/hooks/useWhopPaymentPolling.js +61 -0
  129. package/dist/v2/react/index.d.ts +15 -1
  130. package/dist/v2/react/index.js +7 -0
  131. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.d.ts +3 -1
  132. package/dist/v2/react/providers/ExpressPaymentMethodsProvider.js +12 -2
  133. package/dist/v2/react/providers/TagadaProvider.js +74 -5
  134. package/dist/v2/standalone/external-tracker.d.ts +52 -46
  135. package/dist/v2/standalone/external-tracker.js +205 -98
  136. package/dist/v2/standalone/index.d.ts +40 -0
  137. package/dist/v2/standalone/index.js +148 -1
  138. package/dist/v2/standalone/payment-service.d.ts +134 -0
  139. package/dist/v2/standalone/payment-service.js +928 -0
  140. package/package.json +6 -4
  141. package/dist/react/utils/__tests__/urlUtils.test.d.ts +0 -1
  142. package/dist/react/utils/__tests__/urlUtils.test.js +0 -189
  143. package/dist/v2/core/__tests__/pathRemapping.test.d.ts +0 -11
  144. package/dist/v2/core/__tests__/pathRemapping.test.js +0 -776
@@ -1,776 +0,0 @@
1
- /**
2
- * Path Remapping Tests
3
- *
4
- * Comprehensive test suite for path remapping functionality including:
5
- * - Exact path matching
6
- * - Parameter matching with different names
7
- * - Wildcards and optional segments
8
- * - Edge cases and error handling
9
- * - SDK functions: shouldMatchRoute, getInternalPath, getPathInfo
10
- */
11
- import { match } from 'path-to-regexp';
12
- // Mock browser environment
13
- const mockWindow = {
14
- location: {
15
- pathname: '/',
16
- hostname: 'localhost',
17
- search: '',
18
- href: 'http://localhost:5173/',
19
- },
20
- addEventListener: jest.fn(),
21
- removeEventListener: jest.fn(),
22
- dispatchEvent: jest.fn()
23
- };
24
- const mockDocument = {
25
- querySelector: jest.fn()
26
- };
27
- const mockLocalStorage = {
28
- getItem: jest.fn(),
29
- setItem: jest.fn(),
30
- removeItem: jest.fn(),
31
- clear: jest.fn()
32
- };
33
- const mockHistory = {
34
- pushState: jest.fn(),
35
- replaceState: jest.fn(),
36
- back: jest.fn(),
37
- forward: jest.fn(),
38
- go: jest.fn(),
39
- length: 0,
40
- scrollRestoration: 'auto',
41
- state: null
42
- };
43
- // Setup global mocks
44
- global.window = mockWindow;
45
- global.document = mockDocument;
46
- global.localStorage = mockLocalStorage;
47
- global.history = mockHistory;
48
- describe('Path Remapping - Pattern Matching', () => {
49
- describe('Exact Path Matching', () => {
50
- it('should match exact paths', () => {
51
- const pattern = '/hello';
52
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
53
- expect(matchFn('/hello')).not.toBe(false);
54
- expect(matchFn('/hello/')).not.toBe(false);
55
- expect(matchFn('/hello-world')).toBe(false);
56
- expect(matchFn('/helloworld')).toBe(false);
57
- });
58
- it('should NOT match paths with additional segments', () => {
59
- const pattern = '/hello';
60
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
61
- expect(matchFn('/hello/world')).toBe(false);
62
- expect(matchFn('/hello/123')).toBe(false);
63
- });
64
- });
65
- describe('Parameter Matching - Same Names', () => {
66
- it('should match paths with single parameter', () => {
67
- const pattern = '/products/:id';
68
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
69
- const result1 = matchFn('/products/123');
70
- const result2 = matchFn('/products/abc-xyz');
71
- expect(result1).not.toBe(false);
72
- expect(result2).not.toBe(false);
73
- if (result1 !== false) {
74
- expect(result1.params).toEqual({ id: '123' });
75
- }
76
- if (result2 !== false) {
77
- expect(result2.params).toEqual({ id: 'abc-xyz' });
78
- }
79
- });
80
- it('should match paths with multiple parameters', () => {
81
- const pattern = '/users/:userId/posts/:postId';
82
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
83
- const result = matchFn('/users/john/posts/456');
84
- expect(result).not.toBe(false);
85
- if (result !== false) {
86
- expect(result.params).toEqual({ userId: 'john', postId: '456' });
87
- }
88
- });
89
- });
90
- describe('Parameter Matching - Different Names', () => {
91
- it('should match external pattern with different parameter names than internal', () => {
92
- // External: /myremap/:param
93
- // Internal: /hello-with-param/:myparam
94
- // The matching only cares about the external pattern structure
95
- const externalPattern = '/myremap/:param';
96
- const matchFn = match(externalPattern, { decode: decodeURIComponent, end: true });
97
- const result = matchFn('/myremap/test123');
98
- expect(result).not.toBe(false);
99
- if (result !== false) {
100
- expect(result.params).toEqual({ param: 'test123' });
101
- // The internal path would be /hello-with-param/:myparam
102
- // The param 'test123' would be available as :myparam in the internal route
103
- }
104
- });
105
- it('should match complex remapping scenario', () => {
106
- // External: /products/:productId/reviews/:reviewId
107
- // Internal: /items/:id/comments/:commentId
108
- const externalPattern = '/products/:productId/reviews/:reviewId';
109
- const matchFn = match(externalPattern, { decode: decodeURIComponent, end: true });
110
- const result = matchFn('/products/abc123/reviews/xyz789');
111
- expect(result).not.toBe(false);
112
- if (result !== false) {
113
- expect(result.params).toEqual({
114
- productId: 'abc123',
115
- reviewId: 'xyz789'
116
- });
117
- }
118
- });
119
- });
120
- describe('Special Characters and Encoding', () => {
121
- it('should handle URL-encoded characters', () => {
122
- const pattern = '/products/:id';
123
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
124
- const result = matchFn('/products/hello%20world');
125
- expect(result).not.toBe(false);
126
- if (result !== false) {
127
- expect(result.params).toEqual({ id: 'hello world' });
128
- }
129
- });
130
- it('should handle special characters in parameters', () => {
131
- const pattern = '/users/:username';
132
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
133
- const result1 = matchFn('/users/john-doe');
134
- const result2 = matchFn('/users/user_123');
135
- const result3 = matchFn('/users/user.name');
136
- expect(result1).not.toBe(false);
137
- expect(result2).not.toBe(false);
138
- expect(result3).not.toBe(false);
139
- });
140
- });
141
- describe('Optional Segments', () => {
142
- it('should match paths with optional segments', () => {
143
- const pattern = '/users{/:id}';
144
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
145
- const result1 = matchFn('/users');
146
- const result2 = matchFn('/users/123');
147
- expect(result1).not.toBe(false);
148
- expect(result2).not.toBe(false);
149
- if (result2 !== false) {
150
- expect(result2.params).toEqual({ id: '123' });
151
- }
152
- });
153
- it('should match paths with multiple optional segments', () => {
154
- const pattern = '/api{/:version}{/:endpoint}';
155
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
156
- expect(matchFn('/api')).not.toBe(false);
157
- expect(matchFn('/api/v1')).not.toBe(false);
158
- expect(matchFn('/api/v1/users')).not.toBe(false);
159
- });
160
- });
161
- describe('Wildcard Matching', () => {
162
- it('should match paths with wildcards', () => {
163
- const pattern = '/files/*path';
164
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
165
- const result1 = matchFn('/files/docs');
166
- const result2 = matchFn('/files/docs/readme.md');
167
- const result3 = matchFn('/files/images/logo.png');
168
- expect(result1).not.toBe(false);
169
- expect(result2).not.toBe(false);
170
- expect(result3).not.toBe(false);
171
- if (result2 !== false) {
172
- expect(result2.params.path).toEqual(['docs', 'readme.md']);
173
- }
174
- });
175
- });
176
- describe('Edge Cases', () => {
177
- it('should NOT match when path has trailing segments', () => {
178
- const pattern = '/hello';
179
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
180
- expect(matchFn('/hello/extra')).toBe(false);
181
- expect(matchFn('/hello/extra/segments')).toBe(false);
182
- });
183
- it('should handle root path', () => {
184
- const pattern = '/';
185
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
186
- expect(matchFn('/')).not.toBe(false);
187
- expect(matchFn('/hello')).toBe(false);
188
- });
189
- it('should handle empty parameter values', () => {
190
- const pattern = '/users/:id';
191
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
192
- // Empty parameter should not match
193
- expect(matchFn('/users/')).toBe(false);
194
- expect(matchFn('/users')).toBe(false);
195
- });
196
- it('should handle numeric parameters', () => {
197
- const pattern = '/items/:id';
198
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true });
199
- const result = matchFn('/items/12345');
200
- expect(result).not.toBe(false);
201
- if (result !== false) {
202
- // Note: params are always strings
203
- expect(result.params).toEqual({ id: '12345' });
204
- expect(typeof result.params.id).toBe('string');
205
- }
206
- });
207
- });
208
- describe('Case Sensitivity', () => {
209
- it('should be case-sensitive when option is enabled', () => {
210
- const pattern = '/Hello';
211
- const matchFn = match(pattern, { decode: decodeURIComponent, end: true, sensitive: true });
212
- expect(matchFn('/Hello')).not.toBe(false);
213
- expect(matchFn('/hello')).toBe(false);
214
- expect(matchFn('/HELLO')).toBe(false);
215
- });
216
- });
217
- describe('Real-World Scenarios', () => {
218
- it('should handle e-commerce product remapping', () => {
219
- // External: /p/:productSlug
220
- // Internal: /products/:id
221
- const externalPattern = '/p/:productSlug';
222
- const matchFn = match(externalPattern, { decode: decodeURIComponent, end: true });
223
- const result = matchFn('/p/awesome-product-name');
224
- expect(result).not.toBe(false);
225
- if (result !== false) {
226
- expect(result.params).toEqual({ productSlug: 'awesome-product-name' });
227
- }
228
- });
229
- it('should handle checkout flow remapping', () => {
230
- // External: /checkout/:step
231
- // Internal: /payment/:stepId
232
- const externalPattern = '/checkout/:step';
233
- const matchFn = match(externalPattern, { decode: decodeURIComponent, end: true });
234
- const result1 = matchFn('/checkout/shipping');
235
- const result2 = matchFn('/checkout/payment');
236
- const result3 = matchFn('/checkout/confirmation');
237
- expect(result1).not.toBe(false);
238
- expect(result2).not.toBe(false);
239
- expect(result3).not.toBe(false);
240
- });
241
- it('should handle thank you page with order ID remapping', () => {
242
- // External: /order-success/:orderId
243
- // Internal: /thankyou/:id
244
- const externalPattern = '/order-success/:orderId';
245
- const matchFn = match(externalPattern, { decode: decodeURIComponent, end: true });
246
- const result = matchFn('/order-success/ORD-123-ABC');
247
- expect(result).not.toBe(false);
248
- if (result !== false) {
249
- expect(result.params).toEqual({ orderId: 'ORD-123-ABC' });
250
- }
251
- });
252
- it('should NOT match wrong paths', () => {
253
- const externalPattern = '/myremap/:param';
254
- const matchFn = match(externalPattern, { decode: decodeURIComponent, end: true });
255
- // These should NOT match
256
- expect(matchFn('/other-page')).toBe(false);
257
- expect(matchFn('/myremap')).toBe(false); // Missing parameter
258
- expect(matchFn('/myremapped/test')).toBe(false); // Wrong path
259
- expect(matchFn('/myremap/test/extra')).toBe(false); // Extra segment
260
- });
261
- });
262
- describe('Parameter Name Mapping Logic', () => {
263
- it('should demonstrate parameter name independence', () => {
264
- // The key insight: External and internal paths can have different param names
265
- // The actual URL is matched against the EXTERNAL pattern
266
- // The resulting params are then mapped to the INTERNAL route
267
- const externalPattern = '/custom/:myParam';
268
- const internalPath = '/hello/:differentParam';
269
- const matchFn = match(externalPattern, { decode: decodeURIComponent, end: true });
270
- const result = matchFn('/custom/value123');
271
- expect(result).not.toBe(false);
272
- if (result !== false) {
273
- // External pattern matched, param is 'myParam'
274
- expect(result.params).toEqual({ myParam: 'value123' });
275
- // In the actual implementation, React Router will handle the internal route
276
- // The param value 'value123' will be available as 'differentParam'
277
- // This works because both routes expect a single param in the same position
278
- }
279
- });
280
- it('should work with multiple params in different order', () => {
281
- // External: /users/:userId/posts/:postId
282
- // Internal: /members/:id/articles/:articleId
283
- const externalPattern = '/users/:userId/posts/:postId';
284
- const matchFn = match(externalPattern, { decode: decodeURIComponent, end: true });
285
- const result = matchFn('/users/john/posts/article-123');
286
- expect(result).not.toBe(false);
287
- if (result !== false) {
288
- expect(result.params).toEqual({
289
- userId: 'john',
290
- postId: 'article-123'
291
- });
292
- // React Router will map these positions to the internal route params
293
- }
294
- });
295
- });
296
- });
297
- describe('Path Remapping - Integration Tests', () => {
298
- describe('Complete Remapping Flow', () => {
299
- it('should simulate complete remapping from external to internal', () => {
300
- const remapConfig = {
301
- externalPath: '/myremap/:param',
302
- internalPath: '/hello-with-param/:myparam'
303
- };
304
- const currentPath = '/myremap/test123';
305
- // Step 1: Check if current path matches external pattern
306
- const matchFn = match(remapConfig.externalPath, {
307
- decode: decodeURIComponent,
308
- end: true
309
- });
310
- const result = matchFn(currentPath);
311
- // Step 2: If match, use internal path
312
- expect(result).not.toBe(false);
313
- if (result !== false) {
314
- const shouldUseInternalPath = remapConfig.internalPath;
315
- expect(shouldUseInternalPath).toBe('/hello-with-param/:myparam');
316
- }
317
- });
318
- it('should handle no-match scenario correctly', () => {
319
- const remapConfig = {
320
- externalPath: '/myremap/:param',
321
- internalPath: '/hello-with-param/:myparam'
322
- };
323
- const currentPath = '/different-path/test';
324
- const matchFn = match(remapConfig.externalPath, {
325
- decode: decodeURIComponent,
326
- end: true
327
- });
328
- const result = matchFn(currentPath);
329
- // Should NOT match
330
- expect(result).toBe(false);
331
- // Therefore, no remapping should occur
332
- });
333
- });
334
- describe('Multiple Remap Rules (Future Feature)', () => {
335
- it('should handle multiple remap configurations', () => {
336
- const remapRules = [
337
- { externalPath: '/products/:id', internalPath: '/items/:itemId' },
338
- { externalPath: '/checkout/:step', internalPath: '/payment/:stepId' },
339
- { externalPath: '/order/:orderId', internalPath: '/thankyou/:id' }
340
- ];
341
- const testCases = [
342
- { url: '/products/123', shouldMatch: 0 },
343
- { url: '/checkout/shipping', shouldMatch: 1 },
344
- { url: '/order/ABC-123', shouldMatch: 2 },
345
- { url: '/random/path', shouldMatch: -1 }
346
- ];
347
- testCases.forEach(testCase => {
348
- let matchedRuleIndex = -1;
349
- for (let i = 0; i < remapRules.length; i++) {
350
- const rule = remapRules[i];
351
- const matchFn = match(rule.externalPath, {
352
- decode: decodeURIComponent,
353
- end: true
354
- });
355
- const result = matchFn(testCase.url);
356
- if (result !== false) {
357
- matchedRuleIndex = i;
358
- break;
359
- }
360
- }
361
- expect(matchedRuleIndex).toBe(testCase.shouldMatch);
362
- });
363
- });
364
- });
365
- });
366
- describe('SDK Functions - shouldMatchRoute, getInternalPath, getPathInfo', () => {
367
- // Import SDK functions
368
- let shouldMatchRoute;
369
- let getInternalPath;
370
- let getPathInfo;
371
- beforeEach(() => {
372
- // Reset mocks
373
- jest.resetModules();
374
- jest.clearAllMocks();
375
- // Reset window location
376
- mockWindow.location.pathname = '/';
377
- mockWindow.location.hostname = 'localhost';
378
- mockWindow.location.search = '';
379
- mockWindow.location.href = 'http://localhost:5173/';
380
- // Reset localStorage mock
381
- mockLocalStorage.getItem.mockReturnValue(null);
382
- mockDocument.querySelector.mockReturnValue(null);
383
- // Dynamically import to get fresh instances
384
- const pathRemapping = require('../pathRemapping');
385
- shouldMatchRoute = pathRemapping.shouldMatchRoute;
386
- getInternalPath = pathRemapping.getInternalPath;
387
- getPathInfo = pathRemapping.getPathInfo;
388
- });
389
- describe('shouldMatchRoute() - No Remapping', () => {
390
- it('should match exact path without remapping', () => {
391
- mockWindow.location.pathname = '/hello';
392
- mockLocalStorage.getItem.mockReturnValue(null);
393
- mockDocument.querySelector.mockReturnValue(null);
394
- const result = shouldMatchRoute('/hello');
395
- expect(result).toBe(true);
396
- });
397
- it('should match path with parameters', () => {
398
- mockWindow.location.pathname = '/hello-with-param/test123';
399
- mockLocalStorage.getItem.mockReturnValue(null);
400
- mockDocument.querySelector.mockReturnValue(null);
401
- const result = shouldMatchRoute('/hello-with-param/:myparam');
402
- expect(result).toBe(true);
403
- });
404
- it('should NOT match different paths', () => {
405
- mockWindow.location.pathname = '/other-page';
406
- mockLocalStorage.getItem.mockReturnValue(null);
407
- mockDocument.querySelector.mockReturnValue(null);
408
- const result = shouldMatchRoute('/hello');
409
- expect(result).toBe(false);
410
- });
411
- });
412
- describe('shouldMatchRoute() - With Remapping', () => {
413
- it('should match when current URL matches external pattern', () => {
414
- mockWindow.location.pathname = '/myremap/test123';
415
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
416
- externalPath: '/myremap/:param',
417
- internalPath: '/hello-with-param/:myparam'
418
- }));
419
- mockDocument.querySelector.mockReturnValue(null);
420
- const result = shouldMatchRoute('/hello-with-param/:myparam');
421
- expect(result).toBe(true);
422
- });
423
- it('should NOT match when URL does not match external pattern', () => {
424
- mockWindow.location.pathname = '/other-page';
425
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
426
- externalPath: '/myremap/:param',
427
- internalPath: '/hello-with-param/:myparam'
428
- }));
429
- mockDocument.querySelector.mockReturnValue(null);
430
- const result = shouldMatchRoute('/hello-with-param/:myparam');
431
- expect(result).toBe(false);
432
- });
433
- });
434
- describe('Real-World Scenarios', () => {
435
- it('E-commerce: Product page remapping', () => {
436
- mockWindow.location.pathname = '/p/awesome-product-slug';
437
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
438
- externalPath: '/p/:slug',
439
- internalPath: '/products/:id'
440
- }));
441
- expect(getInternalPath()).toBe('/products/:id');
442
- expect(shouldMatchRoute('/products/:id')).toBe(true);
443
- expect(shouldMatchRoute('/hello')).toBe(false);
444
- });
445
- it('Checkout: Step remapping', () => {
446
- mockWindow.location.pathname = '/checkout/shipping';
447
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
448
- externalPath: '/checkout/:step',
449
- internalPath: '/payment/:stepId'
450
- }));
451
- expect(getInternalPath()).toBe('/payment/:stepId');
452
- expect(shouldMatchRoute('/payment/:stepId')).toBe(true);
453
- });
454
- });
455
- });
456
- describe('SDK Functions - matchRoute() with Parameter Extraction', () => {
457
- let matchRoute;
458
- beforeEach(() => {
459
- // Reset mocks
460
- jest.resetModules();
461
- jest.clearAllMocks();
462
- // Reset window location
463
- mockWindow.location.pathname = '/';
464
- mockWindow.location.hostname = 'localhost';
465
- mockWindow.location.search = '';
466
- mockWindow.location.href = 'http://localhost:5173/';
467
- // Reset localStorage mock
468
- mockLocalStorage.getItem.mockReturnValue(null);
469
- mockDocument.querySelector.mockReturnValue(null);
470
- // Dynamically import to get fresh instances
471
- const pathRemapping = require('../pathRemapping');
472
- matchRoute = pathRemapping.matchRoute;
473
- });
474
- describe('Non-Remapped Paths - Parameter Extraction', () => {
475
- it('should match static path and return empty params', () => {
476
- mockWindow.location.pathname = '/hello';
477
- mockLocalStorage.getItem.mockReturnValue(null);
478
- const result = matchRoute('/hello');
479
- expect(result.matched).toBe(true);
480
- expect(result.params).toEqual({});
481
- });
482
- it('should extract single parameter from URL', () => {
483
- mockWindow.location.pathname = '/hello-with-param/test123';
484
- mockLocalStorage.getItem.mockReturnValue(null);
485
- const result = matchRoute('/hello-with-param/:myparam');
486
- expect(result.matched).toBe(true);
487
- expect(result.params).toEqual({ myparam: 'test123' });
488
- });
489
- it('should extract multiple parameters from URL', () => {
490
- mockWindow.location.pathname = '/users/john/posts/456';
491
- mockLocalStorage.getItem.mockReturnValue(null);
492
- const result = matchRoute('/users/:userId/posts/:postId');
493
- expect(result.matched).toBe(true);
494
- expect(result.params).toEqual({
495
- userId: 'john',
496
- postId: '456'
497
- });
498
- });
499
- it('should return no match for different path', () => {
500
- mockWindow.location.pathname = '/other-page';
501
- mockLocalStorage.getItem.mockReturnValue(null);
502
- const result = matchRoute('/hello');
503
- expect(result.matched).toBe(false);
504
- expect(result.params).toEqual({});
505
- });
506
- it('should handle URL-encoded parameters', () => {
507
- mockWindow.location.pathname = '/products/hello%20world';
508
- mockLocalStorage.getItem.mockReturnValue(null);
509
- const result = matchRoute('/products/:slug');
510
- expect(result.matched).toBe(true);
511
- expect(result.params).toEqual({ slug: 'hello world' });
512
- });
513
- });
514
- describe('Remapped Paths - Parameter Extraction (Matching Names)', () => {
515
- it('should extract params from external URL with matching parameter names', () => {
516
- mockWindow.location.pathname = '/myremap/test123';
517
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
518
- externalPath: '/myremap/:myparam',
519
- internalPath: '/hello-with-param/:myparam'
520
- }));
521
- const result = matchRoute('/hello-with-param/:myparam');
522
- expect(result.matched).toBe(true);
523
- expect(result.params).toEqual({ myparam: 'test123' });
524
- });
525
- it('should extract multiple params from external URL with matching names', () => {
526
- mockWindow.location.pathname = '/u/john/p/456';
527
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
528
- externalPath: '/u/:userId/p/:postId',
529
- internalPath: '/users/:userId/posts/:postId'
530
- }));
531
- const result = matchRoute('/users/:userId/posts/:postId');
532
- expect(result.matched).toBe(true);
533
- expect(result.params).toEqual({
534
- userId: 'john',
535
- postId: '456'
536
- });
537
- });
538
- it('should handle complex slugs with hyphens and numbers', () => {
539
- mockWindow.location.pathname = '/custom/awesome-product-123';
540
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
541
- externalPath: '/custom/:slug',
542
- internalPath: '/products/:slug'
543
- }));
544
- const result = matchRoute('/products/:slug');
545
- expect(result.matched).toBe(true);
546
- expect(result.params).toEqual({ slug: 'awesome-product-123' });
547
- });
548
- it('should return no match when external URL does not match pattern', () => {
549
- mockWindow.location.pathname = '/wrong-path/test';
550
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
551
- externalPath: '/myremap/:param',
552
- internalPath: '/hello-with-param/:myparam'
553
- }));
554
- const result = matchRoute('/hello-with-param/:myparam');
555
- expect(result.matched).toBe(false);
556
- expect(result.params).toEqual({});
557
- });
558
- });
559
- describe('Remapped Paths - Production Meta Tag', () => {
560
- it('should work with simple meta tag (current production format)', () => {
561
- // Note: In production, meta tag only provides the internal path, not the full remap config
562
- // The remapping logic would be handled by the CRM/server-side
563
- mockWindow.location.pathname = '/custom/test456';
564
- mockLocalStorage.getItem.mockReturnValue(null);
565
- // Mock the x-plugin-internal-path meta tag (current production format)
566
- const mockMetaTag = {
567
- getAttribute: jest.fn().mockReturnValue('/hello-with-param/:myparam')
568
- };
569
- mockDocument.querySelector.mockReturnValue(mockMetaTag);
570
- const result = matchRoute('/hello-with-param/:myparam');
571
- // Without localStorage config, the meta tag alone doesn't provide external pattern
572
- // so params can't be extracted from the external URL
573
- // This test confirms the current behavior
574
- expect(result.matched).toBe(true);
575
- // Params will be empty or extracted from trying to match against internal pattern
576
- expect(result.params).toBeDefined();
577
- });
578
- it('should prioritize localStorage over meta tag', () => {
579
- mockWindow.location.pathname = '/local/test789';
580
- // localStorage config
581
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
582
- externalPath: '/local/:localParam',
583
- internalPath: '/hello-with-param/:myparam'
584
- }));
585
- // Meta tag config (should be ignored)
586
- const mockMetaTag = {
587
- getAttribute: jest.fn().mockReturnValue(JSON.stringify({
588
- external: '/meta/:metaParam',
589
- internal: '/hello-with-param/:myparam'
590
- }))
591
- };
592
- mockDocument.querySelector.mockReturnValue(mockMetaTag);
593
- const result = matchRoute('/hello-with-param/:myparam');
594
- expect(result.matched).toBe(true);
595
- expect(result.params).toEqual({ localParam: 'test789' });
596
- });
597
- });
598
- describe('Edge Cases and Error Handling', () => {
599
- it('should handle empty parameter values', () => {
600
- mockWindow.location.pathname = '/hello-with-param/';
601
- mockLocalStorage.getItem.mockReturnValue(null);
602
- const result = matchRoute('/hello-with-param/:myparam');
603
- // path-to-regexp should handle this
604
- expect(result.matched).toBe(false);
605
- });
606
- it('should handle special characters in parameters', () => {
607
- mockWindow.location.pathname = '/products/test-product_123';
608
- mockLocalStorage.getItem.mockReturnValue(null);
609
- const result = matchRoute('/products/:id');
610
- expect(result.matched).toBe(true);
611
- expect(result.params).toEqual({ id: 'test-product_123' });
612
- });
613
- it('should return no match for malformed paths', () => {
614
- mockWindow.location.pathname = '/hello//double-slash';
615
- mockLocalStorage.getItem.mockReturnValue(null);
616
- const result = matchRoute('/hello/:param');
617
- expect(result.matched).toBe(false);
618
- });
619
- it('should handle root path', () => {
620
- mockWindow.location.pathname = '/';
621
- mockLocalStorage.getItem.mockReturnValue(null);
622
- const result = matchRoute('/');
623
- expect(result.matched).toBe(true);
624
- expect(result.params).toEqual({});
625
- });
626
- it('should throw error for invalid internal path (no leading slash)', () => {
627
- mockWindow.location.pathname = '/hello';
628
- mockLocalStorage.getItem.mockReturnValue(null);
629
- expect(() => {
630
- matchRoute('hello');
631
- }).toThrow('internalPath must start with /');
632
- });
633
- it('should throw error for empty internal path', () => {
634
- mockWindow.location.pathname = '/hello';
635
- mockLocalStorage.getItem.mockReturnValue(null);
636
- expect(() => {
637
- matchRoute('');
638
- }).toThrow('internalPath cannot be empty');
639
- });
640
- });
641
- describe('Real-World E-Commerce Scenarios', () => {
642
- it('Product page: Custom SEO-friendly URL → Internal product ID', () => {
643
- mockWindow.location.pathname = '/p/awesome-wireless-headphones';
644
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
645
- externalPath: '/p/:slug',
646
- internalPath: '/products/:slug'
647
- }));
648
- const result = matchRoute('/products/:slug');
649
- expect(result.matched).toBe(true);
650
- expect(result.params).toEqual({ slug: 'awesome-wireless-headphones' });
651
- });
652
- it('Checkout flow: Custom steps → Internal payment flow', () => {
653
- mockWindow.location.pathname = '/checkout/billing-info';
654
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
655
- externalPath: '/checkout/:step',
656
- internalPath: '/payment/:step'
657
- }));
658
- const result = matchRoute('/payment/:step');
659
- expect(result.matched).toBe(true);
660
- expect(result.params).toEqual({ step: 'billing-info' });
661
- });
662
- it('Thank you page: Order confirmation with custom URL', () => {
663
- mockWindow.location.pathname = '/order/ORD-2024-12345';
664
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
665
- externalPath: '/order/:orderId',
666
- internalPath: '/thankyou/:orderId'
667
- }));
668
- const result = matchRoute('/thankyou/:orderId');
669
- expect(result.matched).toBe(true);
670
- expect(result.params).toEqual({ orderId: 'ORD-2024-12345' });
671
- });
672
- it('User profile: Username in URL', () => {
673
- mockWindow.location.pathname = '/user/john_doe_123';
674
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
675
- externalPath: '/user/:username',
676
- internalPath: '/profile/:username'
677
- }));
678
- const result = matchRoute('/profile/:username');
679
- expect(result.matched).toBe(true);
680
- expect(result.params).toEqual({ username: 'john_doe_123' });
681
- });
682
- it('Blog post: SEO slug → Internal post ID structure', () => {
683
- mockWindow.location.pathname = '/blog/how-to-use-path-remapping';
684
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
685
- externalPath: '/blog/:slug',
686
- internalPath: '/posts/:slug'
687
- }));
688
- const result = matchRoute('/posts/:slug');
689
- expect(result.matched).toBe(true);
690
- expect(result.params).toEqual({ slug: 'how-to-use-path-remapping' });
691
- });
692
- });
693
- describe('Complex Path Patterns', () => {
694
- it('should handle nested paths with multiple segments', () => {
695
- mockWindow.location.pathname = '/users/john/posts/456/comments';
696
- mockLocalStorage.getItem.mockReturnValue(null);
697
- const result = matchRoute('/users/:userId/posts/:postId/comments');
698
- expect(result.matched).toBe(true);
699
- expect(result.params).toEqual({
700
- userId: 'john',
701
- postId: '456'
702
- });
703
- });
704
- it('should handle paths with trailing slashes', () => {
705
- mockWindow.location.pathname = '/hello-with-param/test123/';
706
- mockLocalStorage.getItem.mockReturnValue(null);
707
- const result = matchRoute('/hello-with-param/:myparam');
708
- expect(result.matched).toBe(true);
709
- expect(result.params).toEqual({ myparam: 'test123' });
710
- });
711
- it('should handle numeric IDs', () => {
712
- mockWindow.location.pathname = '/products/12345';
713
- mockLocalStorage.getItem.mockReturnValue(null);
714
- const result = matchRoute('/products/:id');
715
- expect(result.matched).toBe(true);
716
- expect(result.params).toEqual({ id: '12345' });
717
- });
718
- it('should handle UUID-style IDs', () => {
719
- mockWindow.location.pathname = '/orders/550e8400-e29b-41d4-a716-446655440000';
720
- mockLocalStorage.getItem.mockReturnValue(null);
721
- const result = matchRoute('/orders/:orderId');
722
- expect(result.matched).toBe(true);
723
- expect(result.params).toEqual({ orderId: '550e8400-e29b-41d4-a716-446655440000' });
724
- });
725
- });
726
- describe('Concurrent Requests - Cache Behavior', () => {
727
- it('should return consistent results for same path', () => {
728
- mockWindow.location.pathname = '/hello-with-param/test123';
729
- mockLocalStorage.getItem.mockReturnValue(null);
730
- const result1 = matchRoute('/hello-with-param/:myparam');
731
- const result2 = matchRoute('/hello-with-param/:myparam');
732
- expect(result1).toEqual(result2);
733
- expect(result1.params).toEqual({ myparam: 'test123' });
734
- });
735
- it('should handle different paths in same session', () => {
736
- mockLocalStorage.getItem.mockReturnValue(null);
737
- // First path
738
- mockWindow.location.pathname = '/products/123';
739
- const result1 = matchRoute('/products/:id');
740
- // Second path (simulating navigation)
741
- mockWindow.location.pathname = '/users/john';
742
- const result2 = matchRoute('/users/:username');
743
- expect(result1.matched).toBe(true);
744
- expect(result1.params).toEqual({ id: '123' });
745
- expect(result2.matched).toBe(true);
746
- expect(result2.params).toEqual({ username: 'john' });
747
- });
748
- });
749
- describe('Integration with CRM Validation Rules', () => {
750
- it('should work with enforced matching parameter names', () => {
751
- // CRM ensures external and internal params have same names
752
- mockWindow.location.pathname = '/custom/test123';
753
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
754
- externalPath: '/custom/:myparam',
755
- internalPath: '/hello-with-param/:myparam'
756
- }));
757
- const result = matchRoute('/hello-with-param/:myparam');
758
- expect(result.matched).toBe(true);
759
- // Parameter names match, so extraction is straightforward
760
- expect(result.params).toEqual({ myparam: 'test123' });
761
- });
762
- it('should handle multiple params with enforced naming', () => {
763
- mockWindow.location.pathname = '/c/electronics/p/laptop-123';
764
- mockLocalStorage.getItem.mockReturnValue(JSON.stringify({
765
- externalPath: '/c/:category/p/:productId',
766
- internalPath: '/shop/:category/item/:productId'
767
- }));
768
- const result = matchRoute('/shop/:category/item/:productId');
769
- expect(result.matched).toBe(true);
770
- expect(result.params).toEqual({
771
- category: 'electronics',
772
- productId: 'laptop-123'
773
- });
774
- });
775
- });
776
- });