@akinon/next 1.92.0-rc.38 → 1.92.0-rc.40
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/CHANGELOG.md +8 -0
- package/__tests__/redirect.test.ts +443 -117
- package/data/client/checkout.ts +4 -2
- package/jest.config.js +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
|
|
4
|
+
interface Locale {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
localePath?: string;
|
|
8
|
+
apiValue?: string;
|
|
9
|
+
rtl?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
const mockNextRedirect = jest.fn();
|
|
5
13
|
const mockHeaders = jest.fn();
|
|
14
|
+
const mockCookies = jest.fn();
|
|
6
15
|
|
|
7
16
|
function findBaseDir() {
|
|
8
17
|
const insideNodeModules = __dirname.includes('node_modules');
|
|
@@ -35,7 +44,8 @@ jest.mock('next/navigation', () => ({
|
|
|
35
44
|
}));
|
|
36
45
|
|
|
37
46
|
jest.mock('next/headers', () => ({
|
|
38
|
-
headers: () => mockHeaders()
|
|
47
|
+
headers: () => mockHeaders(),
|
|
48
|
+
cookies: () => mockCookies()
|
|
39
49
|
}));
|
|
40
50
|
|
|
41
51
|
const mockServerVariables = {
|
|
@@ -47,15 +57,65 @@ jest.mock('@akinon/next/utils/server-variables', () => ({
|
|
|
47
57
|
}));
|
|
48
58
|
|
|
49
59
|
jest.mock('@akinon/next/utils', () => ({
|
|
50
|
-
urlLocaleMatcherRegex: /^\/(?:tr
|
|
60
|
+
urlLocaleMatcherRegex: /^\/(?:tr)(?=\/|$)/
|
|
51
61
|
}));
|
|
52
62
|
|
|
63
|
+
const mockSettings = {
|
|
64
|
+
localization: {
|
|
65
|
+
defaultLocaleValue: 'en',
|
|
66
|
+
localeUrlStrategy: 'hide-default-locale',
|
|
67
|
+
locales: [
|
|
68
|
+
{ value: 'en', label: 'EN' },
|
|
69
|
+
{ value: 'tr', label: 'TR' }
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
jest.mock('settings', () => mockSettings, { virtual: true });
|
|
75
|
+
|
|
53
76
|
describe('Redirect utility functional tests', () => {
|
|
54
77
|
let redirect: any;
|
|
55
78
|
let getUrlPathWithLocale: any;
|
|
56
79
|
let LocaleUrlStrategy: any;
|
|
80
|
+
let actualSettings: any;
|
|
57
81
|
|
|
58
82
|
beforeAll(async () => {
|
|
83
|
+
try {
|
|
84
|
+
const baseDir = findBaseDir();
|
|
85
|
+
const settingsPath = path.resolve(baseDir, 'src/settings.js');
|
|
86
|
+
actualSettings = require(settingsPath);
|
|
87
|
+
|
|
88
|
+
Object.assign(mockSettings.localization, actualSettings.localization);
|
|
89
|
+
|
|
90
|
+
const nonDefaultLocales = actualSettings.localization.locales
|
|
91
|
+
.filter(
|
|
92
|
+
(locale: Locale) =>
|
|
93
|
+
locale.value !== actualSettings.localization.defaultLocaleValue
|
|
94
|
+
)
|
|
95
|
+
.map((locale: Locale) => locale.value);
|
|
96
|
+
if (nonDefaultLocales.length > 0) {
|
|
97
|
+
jest.doMock('@akinon/next/utils', () => ({
|
|
98
|
+
urlLocaleMatcherRegex: new RegExp(
|
|
99
|
+
`^\\/(${nonDefaultLocales.join('|')})(?=\\/|$)`
|
|
100
|
+
)
|
|
101
|
+
}));
|
|
102
|
+
} else {
|
|
103
|
+
jest.doMock('@akinon/next/utils', () => ({
|
|
104
|
+
urlLocaleMatcherRegex: /^\/(?!.*)/
|
|
105
|
+
}));
|
|
106
|
+
console.log('No non-default locales found, using empty regex');
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.warn(
|
|
110
|
+
'Could not load actual settings, using default mock settings'
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
jest.doMock('@akinon/next/utils', () => ({
|
|
114
|
+
urlLocaleMatcherRegex: /^\/(?!.*)/
|
|
115
|
+
}));
|
|
116
|
+
console.log('Using fallback empty regex (no locale matching)');
|
|
117
|
+
}
|
|
118
|
+
|
|
59
119
|
const localizationModule = await import('@akinon/next/localization');
|
|
60
120
|
LocaleUrlStrategy = localizationModule.LocaleUrlStrategy;
|
|
61
121
|
|
|
@@ -72,80 +132,191 @@ describe('Redirect utility functional tests', () => {
|
|
|
72
132
|
jest.clearAllMocks();
|
|
73
133
|
process.env.NEXT_PUBLIC_URL = 'https://example.com';
|
|
74
134
|
|
|
75
|
-
mockServerVariables.locale =
|
|
76
|
-
|
|
135
|
+
mockServerVariables.locale =
|
|
136
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
77
137
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const result1 = getUrlPathWithLocale('/login', 'en');
|
|
82
|
-
const result2 = getUrlPathWithLocale('/login', 'tr');
|
|
83
|
-
const result3 = getUrlPathWithLocale('/login', undefined);
|
|
138
|
+
mockCookies.mockReturnValue({
|
|
139
|
+
get: jest.fn().mockReturnValue(undefined)
|
|
140
|
+
});
|
|
84
141
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
expect(result3).toBe('/login');
|
|
88
|
-
});
|
|
142
|
+
mockHeaders.mockReturnValue(new Map());
|
|
143
|
+
});
|
|
89
144
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
145
|
+
describe('getUrlPathWithLocale functional tests', () => {
|
|
146
|
+
describe('Dynamic behavior based on actual settings', () => {
|
|
147
|
+
it('should work correctly with project localeUrlStrategy', () => {
|
|
148
|
+
const defaultLocale =
|
|
149
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
150
|
+
const strategy =
|
|
151
|
+
actualSettings?.localization?.localeUrlStrategy ||
|
|
152
|
+
LocaleUrlStrategy.HideDefaultLocale;
|
|
153
|
+
const locales = actualSettings?.localization?.locales || [
|
|
154
|
+
{ value: 'en', label: 'EN' },
|
|
155
|
+
{ value: 'tr', label: 'TR' }
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
const result1 = getUrlPathWithLocale('/login', defaultLocale);
|
|
159
|
+
|
|
160
|
+
const nonDefaultLocale = locales.find(
|
|
161
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
162
|
+
);
|
|
163
|
+
const result2 = nonDefaultLocale
|
|
164
|
+
? getUrlPathWithLocale('/login', nonDefaultLocale.value)
|
|
165
|
+
: null;
|
|
94
166
|
|
|
95
|
-
|
|
96
|
-
expect(result2).toBe('/tr/account/orders/123');
|
|
97
|
-
expect(result3).toBe('/ar/account/orders/123');
|
|
98
|
-
});
|
|
167
|
+
const result3 = getUrlPathWithLocale('/login', undefined);
|
|
99
168
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
169
|
+
if (strategy === LocaleUrlStrategy.HideDefaultLocale) {
|
|
170
|
+
expect(result1).toBe('/login');
|
|
171
|
+
if (result2 && nonDefaultLocale) {
|
|
172
|
+
expect(result2).toBe(`/${nonDefaultLocale.value}/login`);
|
|
173
|
+
}
|
|
174
|
+
expect(result3).toBe('/login');
|
|
175
|
+
} else if (strategy === LocaleUrlStrategy.ShowAllLocales) {
|
|
176
|
+
expect(result1).toBe(`/${defaultLocale}/login`);
|
|
177
|
+
if (result2 && nonDefaultLocale) {
|
|
178
|
+
expect(result2).toBe(`/${nonDefaultLocale.value}/login`);
|
|
179
|
+
}
|
|
180
|
+
expect(result3).toBe(`/${defaultLocale}/login`);
|
|
181
|
+
} else if (strategy === LocaleUrlStrategy.HideAllLocales) {
|
|
182
|
+
expect(result1).toBe('/login');
|
|
183
|
+
if (result2) {
|
|
184
|
+
expect(result2).toBe('/login');
|
|
185
|
+
}
|
|
186
|
+
expect(result3).toBe('/login');
|
|
187
|
+
} else if (strategy === LocaleUrlStrategy.Subdomain) {
|
|
188
|
+
expect(result1).toBe('/login');
|
|
189
|
+
if (result2) {
|
|
190
|
+
expect(result2).toBe('/login');
|
|
191
|
+
}
|
|
192
|
+
expect(result3).toBe('/login');
|
|
193
|
+
}
|
|
103
194
|
});
|
|
104
195
|
|
|
105
|
-
it('should handle
|
|
106
|
-
const
|
|
107
|
-
|
|
196
|
+
it('should handle complex paths correctly with project settings', () => {
|
|
197
|
+
const defaultLocale =
|
|
198
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
199
|
+
const locales = actualSettings?.localization?.locales || [
|
|
200
|
+
{ value: 'en', label: 'EN' },
|
|
201
|
+
{ value: 'tr', label: 'TR' }
|
|
202
|
+
];
|
|
203
|
+
const strategy =
|
|
204
|
+
actualSettings?.localization?.localeUrlStrategy ||
|
|
205
|
+
LocaleUrlStrategy.HideDefaultLocale;
|
|
206
|
+
|
|
207
|
+
const complexPath = '/account/orders/123';
|
|
208
|
+
const nonDefaultLocale = locales.find(
|
|
209
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
210
|
+
);
|
|
108
211
|
|
|
109
|
-
|
|
110
|
-
|
|
212
|
+
const result1 = getUrlPathWithLocale(complexPath, defaultLocale);
|
|
213
|
+
const result2 = nonDefaultLocale
|
|
214
|
+
? getUrlPathWithLocale(complexPath, nonDefaultLocale.value)
|
|
215
|
+
: null;
|
|
216
|
+
|
|
217
|
+
if (strategy === LocaleUrlStrategy.HideDefaultLocale) {
|
|
218
|
+
expect(result1).toBe(complexPath);
|
|
219
|
+
if (result2 && nonDefaultLocale) {
|
|
220
|
+
expect(result2).toBe(`/${nonDefaultLocale.value}${complexPath}`);
|
|
221
|
+
}
|
|
222
|
+
} else if (strategy === LocaleUrlStrategy.ShowAllLocales) {
|
|
223
|
+
expect(result1).toBe(`/${defaultLocale}${complexPath}`);
|
|
224
|
+
if (result2 && nonDefaultLocale) {
|
|
225
|
+
expect(result2).toBe(`/${nonDefaultLocale.value}${complexPath}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
111
228
|
});
|
|
112
229
|
|
|
113
|
-
it('should handle
|
|
114
|
-
const
|
|
115
|
-
|
|
230
|
+
it('should handle query parameters correctly', () => {
|
|
231
|
+
const defaultLocale =
|
|
232
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
233
|
+
const locales = actualSettings?.localization?.locales || [
|
|
234
|
+
{ value: 'en', label: 'EN' },
|
|
235
|
+
{ value: 'tr', label: 'TR' }
|
|
236
|
+
];
|
|
237
|
+
const strategy =
|
|
238
|
+
actualSettings?.localization?.localeUrlStrategy ||
|
|
239
|
+
LocaleUrlStrategy.HideDefaultLocale;
|
|
240
|
+
|
|
241
|
+
const pathWithQuery = '/search?q=test';
|
|
242
|
+
const nonDefaultLocale = locales.find(
|
|
243
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
244
|
+
);
|
|
116
245
|
|
|
117
|
-
|
|
118
|
-
|
|
246
|
+
const result1 = getUrlPathWithLocale(pathWithQuery, defaultLocale);
|
|
247
|
+
const result2 = nonDefaultLocale
|
|
248
|
+
? getUrlPathWithLocale(pathWithQuery, nonDefaultLocale.value)
|
|
249
|
+
: null;
|
|
250
|
+
|
|
251
|
+
if (strategy === LocaleUrlStrategy.HideDefaultLocale) {
|
|
252
|
+
expect(result1).toBe(pathWithQuery);
|
|
253
|
+
if (result2 && nonDefaultLocale) {
|
|
254
|
+
expect(result2).toBe(`/${nonDefaultLocale.value}${pathWithQuery}`);
|
|
255
|
+
}
|
|
256
|
+
} else if (strategy === LocaleUrlStrategy.ShowAllLocales) {
|
|
257
|
+
expect(result1).toBe(`/${defaultLocale}${pathWithQuery}`);
|
|
258
|
+
if (result2 && nonDefaultLocale) {
|
|
259
|
+
expect(result2).toBe(`/${nonDefaultLocale.value}${pathWithQuery}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
119
262
|
});
|
|
120
263
|
});
|
|
121
264
|
});
|
|
122
265
|
|
|
123
266
|
describe('URL locale matcher regex behavior', () => {
|
|
124
|
-
|
|
267
|
+
it('should match non-default locale prefixes based on project settings', () => {
|
|
268
|
+
const defaultLocale =
|
|
269
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
270
|
+
const locales = actualSettings?.localization?.locales || [
|
|
271
|
+
{ value: 'en', label: 'EN' },
|
|
272
|
+
{ value: 'tr', label: 'TR' }
|
|
273
|
+
];
|
|
125
274
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
});
|
|
275
|
+
const nonDefaultLocales = locales
|
|
276
|
+
.filter((locale: Locale) => locale.value !== defaultLocale)
|
|
277
|
+
.map((locale: Locale) => locale.value);
|
|
130
278
|
|
|
131
|
-
|
|
132
|
-
|
|
279
|
+
const urlLocaleMatcherRegex = new RegExp(
|
|
280
|
+
`^\\/(${nonDefaultLocales.join('|')})(?=\\/|$)`
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
nonDefaultLocales.forEach((locale: string) => {
|
|
284
|
+
expect(urlLocaleMatcherRegex.test(`/${locale}/profile`)).toBe(true);
|
|
285
|
+
expect(urlLocaleMatcherRegex.test(`/${locale}`)).toBe(true);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
expect(urlLocaleMatcherRegex.test(`/${defaultLocale}/profile`)).toBe(
|
|
289
|
+
false
|
|
290
|
+
);
|
|
133
291
|
expect(urlLocaleMatcherRegex.test('/profile')).toBe(false);
|
|
134
|
-
expect(urlLocaleMatcherRegex.test('/dashboard')).toBe(false);
|
|
135
292
|
});
|
|
136
293
|
|
|
137
|
-
it('should
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
294
|
+
it('should handle callback URL extraction correctly with project locales', () => {
|
|
295
|
+
const defaultLocale =
|
|
296
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
297
|
+
const locales = actualSettings?.localization?.locales || [
|
|
298
|
+
{ value: 'en', label: 'EN' },
|
|
299
|
+
{ value: 'tr', label: 'TR' }
|
|
300
|
+
];
|
|
301
|
+
|
|
302
|
+
const nonDefaultLocales = locales
|
|
303
|
+
.filter((locale: Locale) => locale.value !== defaultLocale)
|
|
304
|
+
.map((locale: Locale) => locale.value);
|
|
305
|
+
|
|
306
|
+
const urlLocaleMatcherRegex = new RegExp(
|
|
307
|
+
`^\\/(${nonDefaultLocales.join('|')})(?=\\/|$)`
|
|
308
|
+
);
|
|
142
309
|
|
|
143
|
-
it('should handle callback URL extraction correctly', () => {
|
|
144
310
|
const testPaths = [
|
|
145
|
-
|
|
146
|
-
|
|
311
|
+
...nonDefaultLocales.map((locale: string) => ({
|
|
312
|
+
input: `/${locale}/profile`,
|
|
313
|
+
expected: '/profile'
|
|
314
|
+
})),
|
|
147
315
|
{ input: '/profile', expected: '/profile' },
|
|
148
|
-
{
|
|
316
|
+
{
|
|
317
|
+
input: `/${defaultLocale}/profile`,
|
|
318
|
+
expected: `/${defaultLocale}/profile`
|
|
319
|
+
}
|
|
149
320
|
];
|
|
150
321
|
|
|
151
322
|
testPaths.forEach(({ input, expected }) => {
|
|
@@ -158,7 +329,9 @@ describe('Redirect utility functional tests', () => {
|
|
|
158
329
|
describe('redirect function behavior', () => {
|
|
159
330
|
describe('Default locale strategy tests', () => {
|
|
160
331
|
beforeEach(() => {
|
|
161
|
-
|
|
332
|
+
const defaultLocale =
|
|
333
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
334
|
+
mockServerVariables.locale = defaultLocale;
|
|
162
335
|
});
|
|
163
336
|
|
|
164
337
|
it('should redirect with correct URL for default locale', () => {
|
|
@@ -174,42 +347,118 @@ describe('Redirect utility functional tests', () => {
|
|
|
174
347
|
});
|
|
175
348
|
|
|
176
349
|
it('should redirect with locale handling for non-default locale', () => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
350
|
+
const locales = actualSettings?.localization?.locales || [
|
|
351
|
+
{ value: 'en', label: 'EN' },
|
|
352
|
+
{ value: 'tr', label: 'TR' }
|
|
353
|
+
];
|
|
354
|
+
const defaultLocale =
|
|
355
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
356
|
+
const nonDefaultLocale = locales.find(
|
|
357
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
180
358
|
);
|
|
181
359
|
|
|
182
|
-
|
|
360
|
+
if (nonDefaultLocale) {
|
|
361
|
+
mockServerVariables.locale = nonDefaultLocale.value;
|
|
362
|
+
mockHeaders.mockReturnValue(
|
|
363
|
+
new Map([
|
|
364
|
+
[
|
|
365
|
+
'pz-url',
|
|
366
|
+
`https://example.com/${nonDefaultLocale.value}/profile`
|
|
367
|
+
]
|
|
368
|
+
])
|
|
369
|
+
);
|
|
183
370
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
371
|
+
redirect('/login');
|
|
372
|
+
|
|
373
|
+
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
374
|
+
`/${nonDefaultLocale.value}/login?callbackUrl=/profile`
|
|
375
|
+
);
|
|
376
|
+
} else {
|
|
377
|
+
expect(true).toBe(true);
|
|
378
|
+
}
|
|
187
379
|
});
|
|
188
380
|
|
|
189
381
|
it('should extract callback URL correctly removing locale prefix', () => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
382
|
+
const locales = actualSettings?.localization?.locales || [
|
|
383
|
+
{ value: 'en', label: 'EN' },
|
|
384
|
+
{ value: 'tr', label: 'TR' }
|
|
385
|
+
];
|
|
386
|
+
const defaultLocale =
|
|
387
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
388
|
+
const nonDefaultLocale = locales.find(
|
|
389
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
193
390
|
);
|
|
194
391
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
392
|
+
if (nonDefaultLocale) {
|
|
393
|
+
mockServerVariables.locale = nonDefaultLocale.value;
|
|
394
|
+
mockHeaders.mockReturnValue(
|
|
395
|
+
new Map([
|
|
396
|
+
[
|
|
397
|
+
'pz-url',
|
|
398
|
+
`https://example.com/${nonDefaultLocale.value}/dashboard/settings`
|
|
399
|
+
]
|
|
400
|
+
])
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
redirect('/auth');
|
|
404
|
+
|
|
405
|
+
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
406
|
+
`/${nonDefaultLocale.value}/auth?callbackUrl=/dashboard/settings`
|
|
407
|
+
);
|
|
408
|
+
} else {
|
|
409
|
+
expect(true).toBe(true);
|
|
410
|
+
}
|
|
200
411
|
});
|
|
201
412
|
|
|
202
|
-
it('should handle
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
413
|
+
it('should handle RTL or additional locale correctly if available', () => {
|
|
414
|
+
const locales = actualSettings?.localization?.locales || [
|
|
415
|
+
{ value: 'en', label: 'EN' },
|
|
416
|
+
{ value: 'tr', label: 'TR' }
|
|
417
|
+
];
|
|
418
|
+
const defaultLocale =
|
|
419
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
420
|
+
const rtlLocale = locales.find(
|
|
421
|
+
(locale: Locale) =>
|
|
422
|
+
locale.value !== defaultLocale && locale.value !== 'tr'
|
|
206
423
|
);
|
|
207
424
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
425
|
+
if (rtlLocale) {
|
|
426
|
+
mockServerVariables.locale = rtlLocale.value;
|
|
427
|
+
mockHeaders.mockReturnValue(
|
|
428
|
+
new Map([
|
|
429
|
+
['pz-url', `https://example.com/${rtlLocale.value}/products/123`]
|
|
430
|
+
])
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
redirect('/checkout');
|
|
434
|
+
|
|
435
|
+
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
436
|
+
`/${rtlLocale.value}/checkout?callbackUrl=/products/123`
|
|
437
|
+
);
|
|
438
|
+
} else {
|
|
439
|
+
const nonDefaultLocale = locales.find(
|
|
440
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
441
|
+
);
|
|
442
|
+
if (nonDefaultLocale) {
|
|
443
|
+
mockServerVariables.locale = nonDefaultLocale.value;
|
|
444
|
+
mockHeaders.mockReturnValue(
|
|
445
|
+
new Map([
|
|
446
|
+
[
|
|
447
|
+
'pz-url',
|
|
448
|
+
`https://example.com/${nonDefaultLocale.value}/products/123`
|
|
449
|
+
]
|
|
450
|
+
])
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
redirect('/checkout');
|
|
454
|
+
|
|
455
|
+
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
456
|
+
`/${nonDefaultLocale.value}/checkout?callbackUrl=/products/123`
|
|
457
|
+
);
|
|
458
|
+
} else {
|
|
459
|
+
expect(true).toBe(true);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
213
462
|
});
|
|
214
463
|
|
|
215
464
|
it('should preserve complex callback URLs', () => {
|
|
@@ -285,7 +534,10 @@ describe('Redirect utility functional tests', () => {
|
|
|
285
534
|
new Map([['pz-url', 'https://example.com/profile']])
|
|
286
535
|
);
|
|
287
536
|
|
|
288
|
-
|
|
537
|
+
// Should not throw error, should handle gracefully
|
|
538
|
+
expect(() => {
|
|
539
|
+
redirect('/login');
|
|
540
|
+
}).not.toThrow();
|
|
289
541
|
|
|
290
542
|
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
291
543
|
'/login?callbackUrl=/profile'
|
|
@@ -298,14 +550,17 @@ describe('Redirect utility functional tests', () => {
|
|
|
298
550
|
new Map([['pz-url', 'https://example.com/profile']])
|
|
299
551
|
);
|
|
300
552
|
|
|
301
|
-
|
|
553
|
+
// Should not throw error, should handle gracefully
|
|
554
|
+
expect(() => {
|
|
555
|
+
redirect('/login');
|
|
556
|
+
}).not.toThrow();
|
|
302
557
|
|
|
303
558
|
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
304
559
|
'/login?callbackUrl=/profile'
|
|
305
560
|
);
|
|
306
561
|
});
|
|
307
562
|
|
|
308
|
-
it('should
|
|
563
|
+
it('should preserve query parameters in callback URL', () => {
|
|
309
564
|
mockHeaders.mockReturnValue(
|
|
310
565
|
new Map([
|
|
311
566
|
['pz-url', 'https://example.com/profile?tab=settings&view=list']
|
|
@@ -315,11 +570,11 @@ describe('Redirect utility functional tests', () => {
|
|
|
315
570
|
redirect('/login');
|
|
316
571
|
|
|
317
572
|
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
318
|
-
'/login?callbackUrl=/profile'
|
|
573
|
+
'/login?callbackUrl=/profile?tab=settings&view=list'
|
|
319
574
|
);
|
|
320
575
|
});
|
|
321
576
|
|
|
322
|
-
it('should strip fragments from callback URL', () => {
|
|
577
|
+
it('should strip fragments from callback URL (expected behavior)', () => {
|
|
323
578
|
mockHeaders.mockReturnValue(
|
|
324
579
|
new Map([['pz-url', 'https://example.com/profile#section']])
|
|
325
580
|
);
|
|
@@ -331,7 +586,7 @@ describe('Redirect utility functional tests', () => {
|
|
|
331
586
|
);
|
|
332
587
|
});
|
|
333
588
|
|
|
334
|
-
it('should handle URL with both query params and fragments', () => {
|
|
589
|
+
it('should handle URL with both query params and fragments (fragments stripped)', () => {
|
|
335
590
|
mockHeaders.mockReturnValue(
|
|
336
591
|
new Map([['pz-url', 'https://example.com/profile?tab=orders#recent']])
|
|
337
592
|
);
|
|
@@ -339,7 +594,7 @@ describe('Redirect utility functional tests', () => {
|
|
|
339
594
|
redirect('/auth');
|
|
340
595
|
|
|
341
596
|
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
342
|
-
'/auth?callbackUrl=/profile'
|
|
597
|
+
'/auth?callbackUrl=/profile?tab=orders'
|
|
343
598
|
);
|
|
344
599
|
});
|
|
345
600
|
});
|
|
@@ -347,38 +602,95 @@ describe('Redirect utility functional tests', () => {
|
|
|
347
602
|
|
|
348
603
|
describe('Integration scenarios', () => {
|
|
349
604
|
it('should handle complete flow with non-default locale and complex path', () => {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
605
|
+
const locales = actualSettings?.localization?.locales || [
|
|
606
|
+
{ value: 'en', label: 'EN' },
|
|
607
|
+
{ value: 'tr', label: 'TR' }
|
|
608
|
+
];
|
|
609
|
+
const defaultLocale =
|
|
610
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
611
|
+
const nonDefaultLocale = locales.find(
|
|
612
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
353
613
|
);
|
|
354
614
|
|
|
355
|
-
|
|
615
|
+
if (nonDefaultLocale) {
|
|
616
|
+
mockServerVariables.locale = nonDefaultLocale.value;
|
|
617
|
+
mockHeaders.mockReturnValue(
|
|
618
|
+
new Map([
|
|
619
|
+
[
|
|
620
|
+
'pz-url',
|
|
621
|
+
`https://example.com/${nonDefaultLocale.value}/account/orders/123`
|
|
622
|
+
]
|
|
623
|
+
])
|
|
624
|
+
);
|
|
356
625
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
626
|
+
redirect('/auth/login');
|
|
627
|
+
|
|
628
|
+
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
629
|
+
`/${nonDefaultLocale.value}/auth/login?callbackUrl=/account/orders/123`
|
|
630
|
+
);
|
|
631
|
+
} else {
|
|
632
|
+
expect(true).toBe(true);
|
|
633
|
+
}
|
|
360
634
|
});
|
|
361
635
|
|
|
362
|
-
it('should handle
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
636
|
+
it('should handle additional locale with deep nested paths if available', () => {
|
|
637
|
+
const locales = actualSettings?.localization?.locales || [
|
|
638
|
+
{ value: 'en', label: 'EN' },
|
|
639
|
+
{ value: 'tr', label: 'TR' }
|
|
640
|
+
];
|
|
641
|
+
const defaultLocale =
|
|
642
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
643
|
+
const additionalLocale = locales.find(
|
|
644
|
+
(locale: Locale) =>
|
|
645
|
+
locale.value !== defaultLocale && locale.value !== 'tr'
|
|
371
646
|
);
|
|
372
647
|
|
|
373
|
-
|
|
648
|
+
if (additionalLocale) {
|
|
649
|
+
mockServerVariables.locale = additionalLocale.value;
|
|
650
|
+
mockHeaders.mockReturnValue(
|
|
651
|
+
new Map([
|
|
652
|
+
[
|
|
653
|
+
'pz-url',
|
|
654
|
+
`https://example.com/${additionalLocale.value}/products/category/electronics/item/456`
|
|
655
|
+
]
|
|
656
|
+
])
|
|
657
|
+
);
|
|
374
658
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
659
|
+
redirect('/auth');
|
|
660
|
+
|
|
661
|
+
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
662
|
+
`/${additionalLocale.value}/auth?callbackUrl=/products/category/electronics/item/456`
|
|
663
|
+
);
|
|
664
|
+
} else {
|
|
665
|
+
const nonDefaultLocale = locales.find(
|
|
666
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
667
|
+
);
|
|
668
|
+
if (nonDefaultLocale) {
|
|
669
|
+
mockServerVariables.locale = nonDefaultLocale.value;
|
|
670
|
+
mockHeaders.mockReturnValue(
|
|
671
|
+
new Map([
|
|
672
|
+
[
|
|
673
|
+
'pz-url',
|
|
674
|
+
`https://example.com/${nonDefaultLocale.value}/products/category/electronics/item/456`
|
|
675
|
+
]
|
|
676
|
+
])
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
redirect('/auth');
|
|
680
|
+
|
|
681
|
+
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
682
|
+
`/${nonDefaultLocale.value}/auth?callbackUrl=/products/category/electronics/item/456`
|
|
683
|
+
);
|
|
684
|
+
} else {
|
|
685
|
+
expect(true).toBe(true);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
378
688
|
});
|
|
379
689
|
|
|
380
690
|
it('should handle default locale with no prefix in callback', () => {
|
|
381
|
-
|
|
691
|
+
const defaultLocale =
|
|
692
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
693
|
+
mockServerVariables.locale = defaultLocale;
|
|
382
694
|
mockHeaders.mockReturnValue(
|
|
383
695
|
new Map([['pz-url', 'https://example.com/checkout/payment']])
|
|
384
696
|
);
|
|
@@ -391,21 +703,35 @@ describe('Redirect utility functional tests', () => {
|
|
|
391
703
|
});
|
|
392
704
|
|
|
393
705
|
it('should handle complex URLs with mixed locale scenarios', () => {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
706
|
+
const locales = actualSettings?.localization?.locales || [
|
|
707
|
+
{ value: 'en', label: 'EN' },
|
|
708
|
+
{ value: 'tr', label: 'TR' }
|
|
709
|
+
];
|
|
710
|
+
const defaultLocale =
|
|
711
|
+
actualSettings?.localization?.defaultLocaleValue || 'en';
|
|
712
|
+
const nonDefaultLocale = locales.find(
|
|
713
|
+
(locale: Locale) => locale.value !== defaultLocale
|
|
402
714
|
);
|
|
403
715
|
|
|
404
|
-
|
|
716
|
+
if (nonDefaultLocale) {
|
|
717
|
+
mockServerVariables.locale = nonDefaultLocale.value;
|
|
718
|
+
mockHeaders.mockReturnValue(
|
|
719
|
+
new Map([
|
|
720
|
+
[
|
|
721
|
+
'pz-url',
|
|
722
|
+
`https://subdomain.example.com/${nonDefaultLocale.value}/user/dashboard?section=profile&tab=settings#preferences`
|
|
723
|
+
]
|
|
724
|
+
])
|
|
725
|
+
);
|
|
405
726
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
727
|
+
redirect('/verify-account');
|
|
728
|
+
|
|
729
|
+
expect(mockNextRedirect).toHaveBeenCalledWith(
|
|
730
|
+
`/${nonDefaultLocale.value}/verify-account?callbackUrl=/user/dashboard?section=profile&tab=settings`
|
|
731
|
+
);
|
|
732
|
+
} else {
|
|
733
|
+
expect(true).toBe(true);
|
|
734
|
+
}
|
|
409
735
|
});
|
|
410
736
|
});
|
|
411
737
|
|
package/data/client/checkout.ts
CHANGED
|
@@ -35,8 +35,10 @@ import {
|
|
|
35
35
|
|
|
36
36
|
interface CheckoutResponse {
|
|
37
37
|
pre_order?: PreOrder;
|
|
38
|
-
errors
|
|
39
|
-
non_field_errors
|
|
38
|
+
errors?: {
|
|
39
|
+
non_field_errors?: string;
|
|
40
|
+
sample_products?: string[];
|
|
41
|
+
[key: string]: string | string[] | undefined;
|
|
40
42
|
};
|
|
41
43
|
context_list?: CheckoutContext[];
|
|
42
44
|
template_name?: string;
|
package/jest.config.js
CHANGED
|
@@ -7,9 +7,9 @@ module.exports = {
|
|
|
7
7
|
preset: 'ts-jest',
|
|
8
8
|
testEnvironment: 'node',
|
|
9
9
|
rootDir: path.resolve(__dirname),
|
|
10
|
-
testMatch: ['
|
|
10
|
+
testMatch: ['**/*.test.ts'],
|
|
11
11
|
testPathIgnorePatterns: [],
|
|
12
|
-
roots: [],
|
|
12
|
+
roots: [path.resolve(__dirname)],
|
|
13
13
|
transformIgnorePatterns: [],
|
|
14
14
|
moduleNameMapper: {
|
|
15
15
|
'^settings$': path.resolve(baseDir, 'src/settings.js')
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/next",
|
|
3
3
|
"description": "Core package for Project Zero Next",
|
|
4
|
-
"version": "1.92.0-rc.
|
|
4
|
+
"version": "1.92.0-rc.40",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"set-cookie-parser": "2.6.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@akinon/eslint-plugin-projectzero": "1.92.0-rc.
|
|
37
|
+
"@akinon/eslint-plugin-projectzero": "1.92.0-rc.40",
|
|
38
38
|
"@babel/core": "7.26.10",
|
|
39
39
|
"@babel/preset-env": "7.26.9",
|
|
40
40
|
"@babel/preset-typescript": "7.27.0",
|