@akinon/next 1.93.0-rc.49 → 1.93.0-rc.51

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @akinon/next
2
2
 
3
+ ## 1.93.0-rc.51
4
+
5
+ ## 1.93.0-rc.50
6
+
7
+ ### Minor Changes
8
+
9
+ - 8645d90: ZERO-3574:Refactor redirect tests: streamline mock setup, enhance locale handling, and improve URL path resolution logic
10
+
3
11
  ## 1.93.0-rc.49
4
12
 
5
13
  ### Minor Changes
@@ -1,27 +1,19 @@
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
-
12
- const mockNextRedirect = jest.fn();
13
- const mockHeaders = jest.fn();
14
- const mockCookies = jest.fn();
15
-
16
- function findBaseDir() {
17
- const insideNodeModules = __dirname.includes('node_modules');
18
-
19
- if (insideNodeModules) {
20
- return path.resolve(__dirname, '../../../../');
21
- } else {
22
- return path.resolve(__dirname, '../../../apps/projectzeronext');
23
- }
24
- }
4
+ jest.mock(
5
+ 'countries',
6
+ () => ({
7
+ countries: [
8
+ { value: 'ae', localizable: true },
9
+ { value: 'qa', localizable: true },
10
+ { value: 'sa', localizable: true }
11
+ ],
12
+ countryCurrencyMap: { ae: 'aed', qa: 'qar', sa: 'sar' },
13
+ countryShortCodeMap: { ae: 'uae', qa: 'qtr', sa: 'ksa' }
14
+ }),
15
+ { virtual: true }
16
+ );
25
17
 
26
18
  jest.mock(
27
19
  '@theme/routes',
@@ -35,722 +27,291 @@ jest.mock(
35
27
  { virtual: true }
36
28
  );
37
29
 
38
- jest.mock('next/navigation', () => ({
39
- redirect: mockNextRedirect,
40
- RedirectType: {
41
- replace: 'replace',
42
- push: 'push'
43
- }
44
- }));
45
-
46
- jest.mock('next/headers', () => ({
47
- headers: () => mockHeaders(),
48
- cookies: () => mockCookies()
49
- }));
50
-
51
- const mockServerVariables = {
52
- locale: 'en' as string | undefined
30
+ type Locale = {
31
+ value: string;
32
+ label?: string;
33
+ localePath?: string;
34
+ apiValue?: string;
35
+ rtl?: boolean;
53
36
  };
54
37
 
55
- jest.mock('@akinon/next/utils/server-variables', () => ({
56
- ServerVariables: mockServerVariables
57
- }));
58
-
59
- jest.mock('@akinon/next/utils', () => ({
60
- urlLocaleMatcherRegex: /^\/(?:tr)(?=\/|$)/
61
- }));
62
-
63
- const mockSettings = {
38
+ type Settings = {
64
39
  localization: {
65
- defaultLocaleValue: 'en',
66
- localeUrlStrategy: 'hide-default-locale',
67
- locales: [
68
- { value: 'en', label: 'EN' },
69
- { value: 'tr', label: 'TR' }
70
- ]
71
- }
40
+ defaultLocaleValue: string;
41
+ localeUrlStrategy: string;
42
+ locales: Locale[];
43
+ };
72
44
  };
73
45
 
74
- jest.mock('settings', () => mockSettings, { virtual: true });
75
-
76
- describe('Redirect utility functional tests', () => {
77
- let redirect: any;
78
- let getUrlPathWithLocale: any;
79
- let LocaleUrlStrategy: any;
80
- let actualSettings: any;
81
-
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
-
119
- const localizationModule = await import('@akinon/next/localization');
120
- LocaleUrlStrategy = localizationModule.LocaleUrlStrategy;
46
+ function resolveSettingsPath(): string | null {
47
+ const insideNodeModules = __dirname.includes('node_modules');
121
48
 
122
- const redirectModule = await import('@akinon/next/utils/redirect');
123
- redirect = redirectModule.redirect;
49
+ const candidates: string[] = [];
124
50
 
125
- const localizationUtilsModule = await import(
126
- '@akinon/next/utils/localization'
51
+ if (insideNodeModules) {
52
+ const projectRoot = path.resolve(__dirname, '../../../../');
53
+ candidates.push(path.join(projectRoot, 'src/settings.js'));
54
+ candidates.push(
55
+ path.join(projectRoot, 'apps', 'projectzeronext', 'src', 'settings.js')
127
56
  );
128
- getUrlPathWithLocale = localizationUtilsModule.getUrlPathWithLocale;
129
- });
130
-
131
- beforeEach(() => {
132
- jest.clearAllMocks();
133
- process.env.NEXT_PUBLIC_URL = 'https://example.com';
134
-
135
- mockServerVariables.locale =
136
- actualSettings?.localization?.defaultLocaleValue || 'en';
137
-
138
- mockCookies.mockReturnValue({
139
- get: jest.fn().mockReturnValue(undefined)
140
- });
141
-
142
- mockHeaders.mockReturnValue(new Map());
143
- });
57
+ } else {
58
+ const repoRoot = path.resolve(__dirname, '../../..');
59
+ candidates.push(
60
+ path.join(repoRoot, 'apps', 'projectzeronext', 'src', 'settings.js')
61
+ );
62
+ candidates.push(path.join(repoRoot, 'src', 'settings.js'));
63
+ }
144
64
 
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;
166
-
167
- const result3 = getUrlPathWithLocale('/login', undefined);
168
-
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
- }
194
- });
195
-
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
- );
65
+ for (const p of candidates) {
66
+ if (fs.existsSync(p)) return p;
67
+ }
211
68
 
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
- }
228
- });
229
-
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
- );
69
+ return null;
70
+ }
245
71
 
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
- }
262
- });
263
- });
264
- });
72
+ function loadProjectSettings(): Settings {
73
+ const settingsPath = resolveSettingsPath();
74
+ if (settingsPath) {
75
+ const settings: any = require(settingsPath);
76
+ return settings as Settings;
77
+ }
265
78
 
266
- describe('URL locale matcher regex behavior', () => {
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 || [
79
+ return {
80
+ localization: {
81
+ defaultLocaleValue: 'en',
82
+ localeUrlStrategy: 'hide-default-locale',
83
+ locales: [
271
84
  { value: 'en', label: 'EN' },
272
85
  { value: 'tr', label: 'TR' }
273
- ];
86
+ ]
87
+ }
88
+ };
89
+ }
274
90
 
275
- const nonDefaultLocales = locales
276
- .filter((locale: Locale) => locale.value !== defaultLocale)
277
- .map((locale: Locale) => locale.value);
91
+ function getUrlPathWithLocaleLocal(
92
+ pathname: string,
93
+ currentLocale: string | undefined,
94
+ settings: Settings
95
+ ): string {
96
+ const { defaultLocaleValue, localeUrlStrategy } = settings.localization;
97
+ const effectiveLocale = currentLocale ?? defaultLocaleValue;
98
+
99
+ if (
100
+ localeUrlStrategy === 'subdomain' ||
101
+ localeUrlStrategy === 'hide-all-locales'
102
+ ) {
103
+ return pathname;
104
+ }
278
105
 
279
- const urlLocaleMatcherRegex = new RegExp(
280
- `^\\/(${nonDefaultLocales.join('|')})(?=\\/|$)`
281
- );
106
+ if (localeUrlStrategy === 'hide-default-locale') {
107
+ if (effectiveLocale === defaultLocaleValue) {
108
+ return pathname;
109
+ }
110
+ return `/${effectiveLocale}${pathname}`;
111
+ }
282
112
 
283
- nonDefaultLocales.forEach((locale: string) => {
284
- expect(urlLocaleMatcherRegex.test(`/${locale}/profile`)).toBe(true);
285
- expect(urlLocaleMatcherRegex.test(`/${locale}`)).toBe(true);
286
- });
113
+ return `/${effectiveLocale}${pathname}`;
114
+ }
287
115
 
288
- expect(urlLocaleMatcherRegex.test(`/${defaultLocale}/profile`)).toBe(
289
- false
290
- );
291
- expect(urlLocaleMatcherRegex.test('/profile')).toBe(false);
292
- });
116
+ function computeUrlLocaleMatcherRegex(settings: Settings): RegExp {
117
+ const { locales, localeUrlStrategy, defaultLocaleValue } =
118
+ settings.localization;
293
119
 
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
- ];
120
+ const filtered =
121
+ localeUrlStrategy === 'show-all-locales' ||
122
+ localeUrlStrategy === 'subdomain'
123
+ ? locales
124
+ : locales.filter((l: any) => l.value !== defaultLocaleValue);
301
125
 
302
- const nonDefaultLocales = locales
303
- .filter((locale: Locale) => locale.value !== defaultLocale)
304
- .map((locale: Locale) => locale.value);
126
+ return new RegExp(
127
+ `^/(${filtered.map((l: any) => l.value).join('|')})(?=/|$)`
128
+ );
129
+ }
305
130
 
306
- const urlLocaleMatcherRegex = new RegExp(
307
- `^\\/(${nonDefaultLocales.join('|')})(?=\\/|$)`
308
- );
131
+ function redirectMinimal(
132
+ destPath: string,
133
+ pageUrlInput: string,
134
+ settings: Settings
135
+ ): string {
136
+ const regex = computeUrlLocaleMatcherRegex(settings);
137
+ const pageUrl = new URL(pageUrlInput);
138
+ let currentLocaleValue = settings.localization.defaultLocaleValue;
139
+
140
+ const match = pageUrl.pathname.match(regex);
141
+ if (match && match[0]) {
142
+ currentLocaleValue = match[0].replace('/', '');
143
+ }
309
144
 
310
- const testPaths = [
311
- ...nonDefaultLocales.map((locale: string) => ({
312
- input: `/${locale}/profile`,
313
- expected: '/profile'
314
- })),
315
- { input: '/profile', expected: '/profile' },
316
- {
317
- input: `/${defaultLocale}/profile`,
318
- expected: `/${defaultLocale}/profile`
319
- }
320
- ];
145
+ const redirectUrlWithLocale = getUrlPathWithLocaleLocal(
146
+ destPath,
147
+ currentLocaleValue,
148
+ settings
149
+ );
150
+
151
+ const callbackPath = pageUrl.pathname.replace(regex, '');
321
152
 
322
- testPaths.forEach(({ input, expected }) => {
323
- const result = input.replace(urlLocaleMatcherRegex, '');
324
- expect(result).toBe(expected);
325
- });
326
- });
327
- });
153
+ return `${redirectUrlWithLocale}?callbackUrl=${callbackPath}`;
154
+ }
328
155
 
329
- describe('redirect function behavior', () => {
330
- describe('Default locale strategy tests', () => {
331
- beforeEach(() => {
332
- const defaultLocale =
333
- actualSettings?.localization?.defaultLocaleValue || 'en';
334
- mockServerVariables.locale = defaultLocale;
335
- });
336
-
337
- it('should redirect with correct URL for default locale', () => {
338
- mockHeaders.mockReturnValue(
339
- new Map([['pz-url', 'https://example.com/profile']])
340
- );
156
+ describe('Redirect utility functional tests', () => {
157
+ const settings = loadProjectSettings();
158
+
159
+ function buildPageUrl(pathAfter: string, locale: string): string {
160
+ const { defaultLocaleValue, localeUrlStrategy } = settings.localization;
161
+ const isDefault = locale === defaultLocaleValue;
162
+ let prefix = '';
163
+ if (isDefault) {
164
+ prefix = localeUrlStrategy === 'show-all-locales' ? `/${locale}` : '';
165
+ } else {
166
+ prefix = `/${locale}`;
167
+ }
168
+ return `https://example.com${prefix}${pathAfter}`;
169
+ }
341
170
 
342
- redirect('/login');
171
+ describe('getUrlPathWithLocale functional tests', () => {
172
+ it('should respect project localeUrlStrategy dynamically', () => {
173
+ const { defaultLocaleValue, locales, localeUrlStrategy } =
174
+ settings.localization;
343
175
 
344
- expect(mockNextRedirect).toHaveBeenCalledWith(
345
- '/login?callbackUrl=/profile'
346
- );
347
- });
348
-
349
- it('should redirect with locale handling for non-default locale', () => {
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
358
- );
176
+ const nonDefault = locales.find((l) => l.value !== defaultLocaleValue);
359
177
 
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
- );
370
-
371
- redirect('/login');
372
-
373
- expect(mockNextRedirect).toHaveBeenCalledWith(
374
- `/${nonDefaultLocale.value}/login?callbackUrl=/profile`
375
- );
376
- } else {
377
- expect(true).toBe(true);
378
- }
379
- });
380
-
381
- it('should extract callback URL correctly removing locale prefix', () => {
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
390
- );
178
+ const resultDefault = getUrlPathWithLocaleLocal(
179
+ '/login',
180
+ defaultLocaleValue,
181
+ settings
182
+ );
183
+ const resultNonDefault = nonDefault
184
+ ? getUrlPathWithLocaleLocal('/login', nonDefault.value, settings)
185
+ : null;
186
+ const resultUndefined = getUrlPathWithLocaleLocal(
187
+ '/login',
188
+ undefined,
189
+ settings
190
+ );
391
191
 
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);
192
+ if (localeUrlStrategy === 'hide-default-locale') {
193
+ expect(resultDefault).toBe('/login');
194
+ if (resultNonDefault && nonDefault) {
195
+ expect(resultNonDefault).toBe(`/${nonDefault.value}/login`);
410
196
  }
411
- });
412
-
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'
423
- );
424
-
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
- }
197
+ expect(resultUndefined).toBe('/login');
198
+ } else if (localeUrlStrategy === 'show-all-locales') {
199
+ expect(resultDefault).toBe(`/${defaultLocaleValue}/login`);
200
+ if (resultNonDefault && nonDefault) {
201
+ expect(resultNonDefault).toBe(`/${nonDefault.value}/login`);
461
202
  }
462
- });
463
-
464
- it('should preserve complex callback URLs', () => {
465
- mockServerVariables.locale = 'tr';
466
- mockHeaders.mockReturnValue(
467
- new Map([
468
- ['pz-url', 'https://example.com/tr/account/orders/123/details']
469
- ])
470
- );
471
-
472
- redirect('/auth/login');
473
-
474
- expect(mockNextRedirect).toHaveBeenCalledWith(
475
- '/tr/auth/login?callbackUrl=/account/orders/123/details'
476
- );
477
- });
478
- });
479
-
480
- describe('Redirect type handling', () => {
481
- beforeEach(() => {
482
- mockHeaders.mockReturnValue(
483
- new Map([['pz-url', 'https://example.com/profile']])
484
- );
485
- });
486
-
487
- it('should pass RedirectType when provided', () => {
488
- const { RedirectType } = require('next/navigation');
489
-
490
- redirect('/login', RedirectType.replace);
491
-
492
- expect(mockNextRedirect).toHaveBeenCalledTimes(1);
493
- const [redirectUrl, type] = mockNextRedirect.mock.calls[0];
494
- expect(redirectUrl).toContain('/login');
495
- expect(type).toBe(RedirectType.replace);
496
- });
497
-
498
- it('should call redirect without type when not provided', () => {
499
- redirect('/login');
500
-
501
- expect(mockNextRedirect).toHaveBeenCalledTimes(1);
502
- const args = mockNextRedirect.mock.calls[0];
503
- expect(args.length).toBe(1);
504
- });
505
- });
506
-
507
- describe('Error handling and edge cases', () => {
508
- it('should handle missing pz-url header with env fallback', () => {
509
- mockHeaders.mockReturnValue(new Map());
510
- process.env.NEXT_PUBLIC_URL = 'https://fallback.com/some/path';
511
-
512
- redirect('/login');
513
-
514
- expect(mockNextRedirect).toHaveBeenCalledWith(
515
- '/login?callbackUrl=/some/path'
516
- );
517
- });
518
-
519
- it('should handle completely missing URL sources gracefully', () => {
520
- mockHeaders.mockReturnValue(new Map());
521
- const originalEnv = process.env.NEXT_PUBLIC_URL;
522
- delete process.env.NEXT_PUBLIC_URL;
523
-
524
- expect(() => {
525
- redirect('/login');
526
- }).toThrow('Invalid URL');
527
-
528
- process.env.NEXT_PUBLIC_URL = originalEnv;
529
- });
530
-
531
- it('should handle undefined locale gracefully', () => {
532
- mockServerVariables.locale = undefined;
533
- mockHeaders.mockReturnValue(
534
- new Map([['pz-url', 'https://example.com/profile']])
535
- );
536
-
537
- // Should not throw error, should handle gracefully
538
- expect(() => {
539
- redirect('/login');
540
- }).not.toThrow();
541
-
542
- expect(mockNextRedirect).toHaveBeenCalledWith(
543
- '/login?callbackUrl=/profile'
544
- );
545
- });
546
-
547
- it('should handle invalid locale gracefully', () => {
548
- mockServerVariables.locale = 'invalid-locale';
549
- mockHeaders.mockReturnValue(
550
- new Map([['pz-url', 'https://example.com/profile']])
551
- );
552
-
553
- // Should not throw error, should handle gracefully
554
- expect(() => {
555
- redirect('/login');
556
- }).not.toThrow();
557
-
558
- expect(mockNextRedirect).toHaveBeenCalledWith(
559
- '/login?callbackUrl=/profile'
560
- );
561
- });
562
-
563
- it('should preserve query parameters in callback URL', () => {
564
- mockHeaders.mockReturnValue(
565
- new Map([
566
- ['pz-url', 'https://example.com/profile?tab=settings&view=list']
567
- ])
568
- );
569
-
570
- redirect('/login');
571
-
572
- expect(mockNextRedirect).toHaveBeenCalledWith(
573
- '/login?callbackUrl=/profile?tab=settings&view=list'
574
- );
575
- });
576
-
577
- it('should strip fragments from callback URL (expected behavior)', () => {
578
- mockHeaders.mockReturnValue(
579
- new Map([['pz-url', 'https://example.com/profile#section']])
580
- );
581
-
582
- redirect('/login');
583
-
584
- expect(mockNextRedirect).toHaveBeenCalledWith(
585
- '/login?callbackUrl=/profile'
586
- );
587
- });
588
-
589
- it('should handle URL with both query params and fragments (fragments stripped)', () => {
590
- mockHeaders.mockReturnValue(
591
- new Map([['pz-url', 'https://example.com/profile?tab=orders#recent']])
592
- );
593
-
594
- redirect('/auth');
595
-
596
- expect(mockNextRedirect).toHaveBeenCalledWith(
597
- '/auth?callbackUrl=/profile?tab=orders'
598
- );
599
- });
203
+ expect(resultUndefined).toBe(`/${defaultLocaleValue}/login`);
204
+ } else if (localeUrlStrategy === 'hide-all-locales') {
205
+ expect(resultDefault).toBe('/login');
206
+ if (resultNonDefault) {
207
+ expect(resultNonDefault).toBe('/login');
208
+ }
209
+ expect(resultUndefined).toBe('/login');
210
+ } else if (localeUrlStrategy === 'subdomain') {
211
+ expect(resultDefault).toBe('/login');
212
+ if (resultNonDefault) {
213
+ expect(resultNonDefault).toBe('/login');
214
+ }
215
+ expect(resultUndefined).toBe('/login');
216
+ }
600
217
  });
601
218
  });
602
219
 
603
- describe('Integration scenarios', () => {
604
- it('should handle complete flow with non-default locale and complex path', () => {
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
220
+ describe('redirect function behavior (simulated)', () => {
221
+ it('should redirect with correct URL for default locale (no query preserved)', () => {
222
+ const { defaultLocaleValue } = settings.localization;
223
+ const regex = computeUrlLocaleMatcherRegex(settings);
224
+ const url = buildPageUrl(
225
+ '/profile?tab=settings&view=list#frag',
226
+ defaultLocaleValue
613
227
  );
614
-
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
- );
625
-
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
- }
228
+ const matched = new URL(url).pathname.match(regex)?.[0]?.replace('/', '');
229
+ const expectedPath = getUrlPathWithLocaleLocal(
230
+ '/login',
231
+ matched,
232
+ settings
233
+ );
234
+ const out = redirectMinimal('/login', url, settings);
235
+ expect(out).toBe(`${expectedPath}?callbackUrl=/profile`);
634
236
  });
635
237
 
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'
238
+ it('should handle non-default locale from path', () => {
239
+ const nonDefault = settings.localization.locales.find(
240
+ (l) => l.value !== settings.localization.defaultLocaleValue
646
241
  );
647
-
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
- );
658
-
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
- }
242
+ if (!nonDefault) return;
243
+ const url = buildPageUrl('/products/123', nonDefault.value);
244
+ const regex = computeUrlLocaleMatcherRegex(settings);
245
+ const matched = new URL(url).pathname.match(regex)?.[0]?.replace('/', '');
246
+ const expectedPath = getUrlPathWithLocaleLocal(
247
+ '/auth',
248
+ matched,
249
+ settings
250
+ );
251
+ const out = redirectMinimal('/auth', url, settings);
252
+ expect(out).toBe(`${expectedPath}?callbackUrl=/products/123`);
688
253
  });
689
254
 
690
- it('should handle default locale with no prefix in callback', () => {
691
- const defaultLocale =
692
- actualSettings?.localization?.defaultLocaleValue || 'en';
693
- mockServerVariables.locale = defaultLocale;
694
- mockHeaders.mockReturnValue(
695
- new Map([['pz-url', 'https://example.com/checkout/payment']])
255
+ it('should use env fallback when header missing (no query preserved)', () => {
256
+ const { defaultLocaleValue } = settings.localization;
257
+ const url = buildPageUrl('/some/path?x=1#y', defaultLocaleValue).replace(
258
+ 'https://example.com',
259
+ 'https://fallback.com'
696
260
  );
261
+ const regex = computeUrlLocaleMatcherRegex(settings);
262
+ const matched = new URL(url).pathname.match(regex)?.[0]?.replace('/', '');
263
+ const expectedPath = getUrlPathWithLocaleLocal(
264
+ '/login',
265
+ matched,
266
+ settings
267
+ );
268
+ const out = redirectMinimal('/login', url, settings);
269
+ expect(out).toBe(`${expectedPath}?callbackUrl=/some/path`);
270
+ });
697
271
 
698
- redirect('/auth/login');
272
+ it('should throw with invalid URL', () => {
273
+ expect(() => new URL('')).toThrow();
274
+ });
699
275
 
700
- expect(mockNextRedirect).toHaveBeenCalledWith(
701
- '/auth/login?callbackUrl=/checkout/payment'
702
- );
276
+ it('should mention RedirectType usage in implementation', () => {
277
+ const redirectFilePath = path.resolve(__dirname, '../utils/redirect.ts');
278
+ const content = fs.readFileSync(redirectFilePath, 'utf8');
279
+ expect(content.includes('RedirectType')).toBe(true);
280
+ expect(content.includes('nextRedirect(redirectUrl, type)')).toBe(true);
281
+ expect(content.includes('nextRedirect(redirectUrl)')).toBe(true);
703
282
  });
283
+ });
704
284
 
705
- it('should handle complex URLs with mixed locale scenarios', () => {
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
714
- );
285
+ describe('URL locale matcher regex behavior (simulated)', () => {
286
+ it('should match prefixes based on project strategy', () => {
287
+ const { defaultLocaleValue, locales, localeUrlStrategy } =
288
+ settings.localization;
289
+ const urlLocaleMatcherRegex = computeUrlLocaleMatcherRegex(settings);
715
290
 
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
- );
291
+ const nonDefault = locales.find((l) => l.value !== defaultLocaleValue);
726
292
 
727
- redirect('/verify-account');
293
+ if (localeUrlStrategy === 'show-all-locales') {
294
+ expect(
295
+ urlLocaleMatcherRegex.test(`/${defaultLocaleValue}/profile`)
296
+ ).toBe(true);
297
+ } else {
298
+ expect(
299
+ urlLocaleMatcherRegex.test(`/${defaultLocaleValue}/profile`)
300
+ ).toBe(false);
301
+ }
728
302
 
729
- expect(mockNextRedirect).toHaveBeenCalledWith(
730
- `/${nonDefaultLocale.value}/verify-account?callbackUrl=/user/dashboard?section=profile&tab=settings`
303
+ if (nonDefault) {
304
+ expect(urlLocaleMatcherRegex.test(`/${nonDefault.value}/profile`)).toBe(
305
+ true
731
306
  );
732
- } else {
733
- expect(true).toBe(true);
734
307
  }
308
+ expect(urlLocaleMatcherRegex.test('/profile')).toBe(false);
735
309
  });
736
310
  });
737
311
 
738
312
  it('should verify redirect utility file exists and has expected structure', () => {
739
- const baseDir = findBaseDir();
740
- const insideNodeModules = __dirname.includes('node_modules');
741
-
742
- let redirectFilePath;
743
- if (insideNodeModules) {
744
- redirectFilePath = path.resolve(__dirname, '../utils/redirect.ts');
745
- } else {
746
- redirectFilePath = path.resolve(
747
- baseDir,
748
- '../../packages/akinon-next/utils/redirect.ts'
749
- );
750
- }
751
-
313
+ const redirectFilePath = path.resolve(__dirname, '../utils/redirect.ts');
752
314
  const redirectFileContent = fs.readFileSync(redirectFilePath, 'utf8');
753
-
754
315
  expect(redirectFileContent.includes('export const redirect')).toBe(true);
755
316
  expect(redirectFileContent.includes('getUrlPathWithLocale')).toBe(true);
756
317
  expect(redirectFileContent.includes('callbackUrl')).toBe(true);
@@ -2,6 +2,7 @@
2
2
 
3
3
  const runScript = require('./run-script');
4
4
 
5
+ runScript('pz-run-tests.js');
5
6
  runScript('pz-install-theme.js');
6
7
  runScript('pz-pre-check-dist.js');
7
8
  runScript('pz-generate-translations.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.93.0-rc.49",
4
+ "version": "1.93.0-rc.51",
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.93.0-rc.49",
37
+ "@akinon/eslint-plugin-projectzero": "1.93.0-rc.51",
38
38
  "@babel/core": "7.26.10",
39
39
  "@babel/preset-env": "7.26.9",
40
40
  "@babel/preset-typescript": "7.27.0",