@equinor/fusion-framework-module-navigation 7.0.9-next.0 → 7.0.10

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 (55) hide show
  1. package/dist/esm/version.js +1 -1
  2. package/dist/esm/version.js.map +1 -1
  3. package/dist/tsconfig.tsbuildinfo +1 -1
  4. package/dist/types/version.d.ts +1 -1
  5. package/package.json +10 -7
  6. package/CHANGELOG.md +0 -600
  7. package/src/NavigateEvent.ts +0 -47
  8. package/src/NavigatedEvent.ts +0 -35
  9. package/src/NavigationConfigurator.interface.ts +0 -41
  10. package/src/NavigationConfigurator.ts +0 -226
  11. package/src/NavigationProvider.interface.ts +0 -99
  12. package/src/NavigationProvider.ts +0 -436
  13. package/src/__tests__/BrowserHistory.test.ts +0 -151
  14. package/src/__tests__/HashHistory.test.ts +0 -121
  15. package/src/__tests__/MemoryHistory.test.ts +0 -185
  16. package/src/__tests__/NavigationProvider.test.ts +0 -302
  17. package/src/__tests__/ProxyHistory.test.ts +0 -149
  18. package/src/__tests__/setup.ts +0 -8
  19. package/src/enable-navigation.ts +0 -60
  20. package/src/index.ts +0 -51
  21. package/src/lib/BaseHistory.ts +0 -255
  22. package/src/lib/BrowserHistory.ts +0 -124
  23. package/src/lib/BrowserHistoryHashStack.ts +0 -57
  24. package/src/lib/BrowserHistoryStack.ts +0 -96
  25. package/src/lib/MemoryHistory.ts +0 -76
  26. package/src/lib/MemoryHistoryStack.ts +0 -118
  27. package/src/lib/ProxyHistory.ts +0 -145
  28. package/src/lib/create-history.ts +0 -58
  29. package/src/lib/index.ts +0 -33
  30. package/src/lib/state/actions.ts +0 -108
  31. package/src/lib/state/check-blockers.ts +0 -52
  32. package/src/lib/state/create-flow.ts +0 -36
  33. package/src/lib/state/create-history-reducer.ts +0 -77
  34. package/src/lib/state/create-store.ts +0 -39
  35. package/src/lib/state/flow-creators.ts +0 -7
  36. package/src/lib/state/go.ts +0 -20
  37. package/src/lib/state/history.state.ts +0 -14
  38. package/src/lib/state/index.ts +0 -4
  39. package/src/lib/state/navigate.ts +0 -58
  40. package/src/lib/state/pop.ts +0 -24
  41. package/src/lib/state/validate-current-location.ts +0 -33
  42. package/src/lib/types.ts +0 -190
  43. package/src/lib/utils/encode-trailing-whitespace.ts +0 -18
  44. package/src/lib/utils/has-protocol.ts +0 -21
  45. package/src/lib/utils/index.ts +0 -5
  46. package/src/lib/utils/path-to-string.ts +0 -24
  47. package/src/lib/utils/path-to-url.ts +0 -52
  48. package/src/lib/utils/resolve-browser-location.ts +0 -1
  49. package/src/lib/utils/resolve-hash-location.ts +0 -26
  50. package/src/lib/utils/resolve-path.ts +0 -22
  51. package/src/lib/utils/resolve-window-location.ts +0 -24
  52. package/src/module.ts +0 -80
  53. package/src/version.ts +0 -2
  54. package/tsconfig.json +0 -24
  55. package/vitest.config.ts +0 -14
@@ -1,121 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { firstValueFrom, skip } from 'rxjs';
3
- import { createHistory } from '../lib/create-history';
4
- import type { History } from '../lib/types';
5
-
6
- describe('HashHistory', () => {
7
- let history: History;
8
-
9
- beforeEach(() => {
10
- history = createHistory('hash');
11
- });
12
-
13
- afterEach(() => {
14
- history[Symbol.dispose]();
15
- });
16
-
17
- describe('hash-based navigation', () => {
18
- it('should update hash instead of pathname on push', async () => {
19
- const initialPathname = window.location.pathname;
20
-
21
- const updatePromise = firstValueFrom(history.state$.pipe(skip(1)));
22
- history.push('/about', { test: 'test' });
23
- await updatePromise;
24
-
25
- expect(history.location.pathname).toBe('/about');
26
- expect(history.action).toBe('PUSH');
27
- expect(history.location.state).toEqual({ test: 'test' });
28
- // Pathname should remain unchanged
29
- expect(window.location.pathname).toBe(initialPathname);
30
- // Hash should contain the route
31
- expect(window.location.hash).toBe('#/about');
32
- });
33
-
34
- it('should update hash instead of pathname on replace', async () => {
35
- const initialPathname = window.location.pathname;
36
-
37
- const updatePromise = firstValueFrom(history.state$.pipe(skip(1)));
38
- history.replace('/profile', { userId: 123 });
39
- await updatePromise;
40
-
41
- expect(history.location.pathname).toBe('/profile');
42
- expect(history.action).toBe('REPLACE');
43
- expect(history.location.state).toEqual({ userId: 123 });
44
- // Pathname should remain unchanged
45
- expect(window.location.pathname).toBe(initialPathname);
46
- // Hash should contain the route
47
- expect(window.location.hash).toBe('#/profile');
48
- });
49
-
50
- it('should resolve location from hash', async () => {
51
- const updatePromise = firstValueFrom(history.state$.pipe(skip(1)));
52
- history.push('/test?query=1#section');
53
- await updatePromise;
54
-
55
- const location = history.location;
56
- expect(location.pathname).toBe('/test');
57
- expect(location.search).toBe('?query=1');
58
- expect(location.hash).toBe('#section');
59
- expect(window.location.hash).toBe('#/test?query=1#section');
60
- });
61
-
62
- it('should handle hashchange events for back/forward', async () => {
63
- const initialPathname = window.location.pathname;
64
-
65
- // Push multiple entries
66
- history.push('/page1', { page: 1 });
67
- await firstValueFrom(history.state$.pipe(skip(1)));
68
- expect(window.location.hash).toBe('#/page1');
69
-
70
- history.push('/page2', { page: 2 });
71
- await firstValueFrom(history.state$.pipe(skip(1)));
72
- expect(window.location.hash).toBe('#/page2');
73
-
74
- // Go back - should trigger hashchange event
75
- const backPromise = firstValueFrom(history.state$.pipe(skip(1)));
76
- window.history.back();
77
- await backPromise;
78
- expect(history.location.pathname).toBe('/page1');
79
- expect(history.action).toBe('POP');
80
- expect(history.location.state).toEqual({ page: 1 });
81
- expect(window.location.hash).toBe('#/page1');
82
- expect(window.location.pathname).toBe(initialPathname);
83
-
84
- // Go forward
85
- const forwardPromise = firstValueFrom(history.state$.pipe(skip(1)));
86
- window.history.forward();
87
- await forwardPromise;
88
- expect(history.location.pathname).toBe('/page2');
89
- expect(history.action).toBe('POP');
90
- expect(history.location.state).toEqual({ page: 2 });
91
- expect(window.location.hash).toBe('#/page2');
92
- expect(window.location.pathname).toBe(initialPathname);
93
- });
94
-
95
- it('should handle hash with search and hash fragments', async () => {
96
- const initialPathname = window.location.pathname;
97
-
98
- const updatePromise = firstValueFrom(history.state$.pipe(skip(1)));
99
- history.push('/users?filter=active#list', { filter: 'active' });
100
- await updatePromise;
101
-
102
- expect(history.location.pathname).toBe('/users');
103
- expect(history.location.search).toBe('?filter=active');
104
- expect(history.location.hash).toBe('#list');
105
- // Hash should contain full path with search and hash
106
- expect(window.location.hash).toBe('#/users?filter=active#list');
107
- expect(window.location.pathname).toBe(initialPathname);
108
- });
109
- });
110
-
111
- describe('state$ observable', () => {
112
- it('should emit state updates on navigation', async () => {
113
- const updatePromise = firstValueFrom(history.state$.pipe(skip(1)));
114
- history.push('/test');
115
- const update = await updatePromise;
116
-
117
- expect(update.action).toBe('PUSH');
118
- expect(update.location.pathname).toBe('/test');
119
- });
120
- });
121
- });
@@ -1,185 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { firstValueFrom, skip } from 'rxjs';
3
- import { MemoryHistory } from '../lib/MemoryHistory';
4
-
5
- describe('MemoryHistory', () => {
6
- let history: MemoryHistory;
7
-
8
- beforeEach(() => {
9
- history = new MemoryHistory();
10
- });
11
-
12
- afterEach(() => {
13
- history[Symbol.dispose]();
14
- });
15
-
16
- describe('initialization', () => {
17
- it('should initialize with default location', () => {
18
- expect(history.location.pathname).toBe('/');
19
- expect(history.action).toBe('POP');
20
- });
21
-
22
- it('should initialize with custom initial location', () => {
23
- const customHistory = new MemoryHistory({
24
- initialLocation: {
25
- action: 'POP',
26
- location: {
27
- pathname: '/custom',
28
- search: '',
29
- hash: '',
30
- key: 'custom-key',
31
- state: { test: 'value' },
32
- },
33
- },
34
- });
35
-
36
- expect(customHistory.location.pathname).toBe('/custom');
37
- expect(customHistory.location.state).toEqual({ test: 'value' });
38
- expect(customHistory.action).toBe('POP');
39
-
40
- customHistory[Symbol.dispose]();
41
- });
42
- });
43
-
44
- describe('in-memory navigation', () => {
45
- it('should push without affecting browser', async () => {
46
- const initialPathname = window.location.pathname;
47
- const initialHash = window.location.hash;
48
-
49
- const updatePromise = firstValueFrom(history.state$.pipe(skip(1)));
50
- history.push('/about', { test: 'test' });
51
- await updatePromise;
52
-
53
- expect(history.location.pathname).toBe('/about');
54
- expect(history.action).toBe('PUSH');
55
- expect(history.location.state).toEqual({ test: 'test' });
56
- // Browser should not be affected
57
- expect(window.location.pathname).toBe(initialPathname);
58
- expect(window.location.hash).toBe(initialHash);
59
- });
60
-
61
- it('should replace without affecting browser', async () => {
62
- const initialPathname = window.location.pathname;
63
- const initialHash = window.location.hash;
64
-
65
- const updatePromise = firstValueFrom(history.state$.pipe(skip(1)));
66
- history.replace('/profile', { userId: 123 });
67
- await updatePromise;
68
-
69
- expect(history.location.pathname).toBe('/profile');
70
- expect(history.action).toBe('REPLACE');
71
- expect(history.location.state).toEqual({ userId: 123 });
72
- // Browser should not be affected
73
- expect(window.location.pathname).toBe(initialPathname);
74
- expect(window.location.hash).toBe(initialHash);
75
- });
76
-
77
- it('should maintain in-memory history stack', async () => {
78
- // Push multiple entries
79
- history.push('/page1', { page: 1 });
80
- await firstValueFrom(history.state$.pipe(skip(1)));
81
- expect(history.location.pathname).toBe('/page1');
82
-
83
- history.push('/page2', { page: 2 });
84
- await firstValueFrom(history.state$.pipe(skip(1)));
85
- expect(history.location.pathname).toBe('/page2');
86
-
87
- history.push('/page3', { page: 3 });
88
- await firstValueFrom(history.state$.pipe(skip(1)));
89
- expect(history.location.pathname).toBe('/page3');
90
-
91
- // Go back
92
- const goBackPromise1 = firstValueFrom(history.state$.pipe(skip(1)));
93
- history.go(-1);
94
- await goBackPromise1;
95
- expect(history.location.pathname).toBe('/page2');
96
- expect(history.action).toBe('POP');
97
- expect(history.location.state).toEqual({ page: 2 });
98
-
99
- // Go back again
100
- const goBackPromise2 = firstValueFrom(history.state$.pipe(skip(1)));
101
- history.go(-1);
102
- await goBackPromise2;
103
- expect(history.location.pathname).toBe('/page1');
104
- expect(history.action).toBe('POP');
105
- expect(history.location.state).toEqual({ page: 1 });
106
-
107
- // Go forward
108
- const goForwardPromise = firstValueFrom(history.state$.pipe(skip(1)));
109
- history.go(1);
110
- await goForwardPromise;
111
- expect(history.location.pathname).toBe('/page2');
112
- expect(history.action).toBe('POP');
113
- expect(history.location.state).toEqual({ page: 2 });
114
- });
115
-
116
- it('should clamp go() to history bounds', async () => {
117
- history.push('/page1');
118
- await firstValueFrom(history.state$.pipe(skip(1)));
119
- expect(history.location.pathname).toBe('/page1');
120
-
121
- // Try to go back beyond start
122
- const goBackPromise = firstValueFrom(history.state$.pipe(skip(1)));
123
- history.go(-10);
124
- await goBackPromise;
125
- // Should clamp to first entry
126
- expect(history.location.pathname).toBe('/');
127
- expect(history.action).toBe('POP');
128
-
129
- // Try to go forward beyond end
130
- const goForwardPromise = firstValueFrom(history.state$.pipe(skip(1)));
131
- history.go(10);
132
- await goForwardPromise;
133
- // Should clamp to last entry
134
- expect(history.location.pathname).toBe('/page1');
135
- expect(history.action).toBe('POP');
136
- });
137
- });
138
-
139
- describe('createURL', () => {
140
- it('should create URLs with memory:// protocol', () => {
141
- const url = history.createURL('/test');
142
- expect(url.protocol).toBe('memory:');
143
- expect(url.pathname).toBe('/test');
144
- expect(url.href).toContain('memory://');
145
- });
146
-
147
- it('should create URLs with search and hash', () => {
148
- const url = history.createURL('/test?query=1#section');
149
- expect(url.protocol).toBe('memory:');
150
- expect(url.pathname).toBe('/test');
151
- expect(url.search).toBe('?query=1');
152
- expect(url.hash).toBe('#section');
153
- expect(url.href).toContain('memory://');
154
- });
155
- });
156
-
157
- describe('state$ observable', () => {
158
- it('should emit state updates on navigation', async () => {
159
- const updatePromise = firstValueFrom(history.state$.pipe(skip(1)));
160
- history.push('/test', { data: 'test' });
161
- const update = await updatePromise;
162
-
163
- expect(update.action).toBe('PUSH');
164
- expect(update.location.pathname).toBe('/test');
165
- expect(update.location.state).toEqual({ data: 'test' });
166
- });
167
-
168
- it('should emit state updates on go()', async () => {
169
- history.push('/page1');
170
- await firstValueFrom(history.state$.pipe(skip(1)));
171
- expect(history.location.pathname).toBe('/page1');
172
-
173
- history.push('/page2');
174
- await firstValueFrom(history.state$.pipe(skip(1)));
175
- expect(history.location.pathname).toBe('/page2');
176
-
177
- const goPromise = firstValueFrom(history.state$.pipe(skip(1)));
178
- history.go(-1);
179
- const update = await goPromise;
180
-
181
- expect(update.action).toBe('POP');
182
- expect(update.location.pathname).toBe('/page1');
183
- });
184
- });
185
- });
@@ -1,302 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { NavigationProvider } from '../NavigationProvider';
3
- import { MemoryHistory } from '../lib/MemoryHistory';
4
- import type { INavigationConfigurator } from '../NavigationConfigurator.interface';
5
- import type { History } from '../lib/types';
6
-
7
- describe('NavigationProvider', () => {
8
- let history: History;
9
- let provider: NavigationProvider;
10
- let config: INavigationConfigurator;
11
-
12
- beforeEach(() => {
13
- history = new MemoryHistory();
14
- config = { history };
15
- provider = new NavigationProvider({
16
- version: '1.0.0',
17
- config,
18
- });
19
- });
20
-
21
- afterEach(() => {
22
- provider?.dispose();
23
- history[Symbol.dispose]();
24
- });
25
-
26
- it('should have version', () => {
27
- expect(provider.version).toBeDefined();
28
- expect(provider.version.toString()).toBe('1.0.0');
29
- });
30
-
31
- it('should have history property', () => {
32
- expect(provider.history).toBe(history);
33
- });
34
-
35
- it('should have path property', () => {
36
- expect(provider.path).toBeDefined();
37
- expect(provider.path.pathname).toBeDefined();
38
- });
39
-
40
- it('should have state$ observable', () => {
41
- expect(provider.state$).toBeDefined();
42
- expect(typeof provider.state$.subscribe).toBe('function');
43
- });
44
-
45
- it('should throw error if no history provided', () => {
46
- config = {};
47
- expect(() => {
48
- new NavigationProvider({
49
- version: '1.0.0',
50
- config,
51
- });
52
- }).toThrow('no history provided!');
53
- });
54
-
55
- it('should dispose correctly', () => {
56
- const disposeSpy = vi.spyOn(history as { [Symbol.dispose]: () => void }, Symbol.dispose);
57
- provider.dispose();
58
- expect(disposeSpy).toHaveBeenCalled();
59
- });
60
-
61
- it('should normalize root basename "/" to empty string', () => {
62
- const providerWithRootBasename = new NavigationProvider({
63
- version: '1.0.0',
64
- config: { history, basename: '/' },
65
- });
66
-
67
- // Root basename '/' should be treated as "no basename"
68
- expect(providerWithRootBasename.basename).toBe('');
69
-
70
- providerWithRootBasename.dispose();
71
- });
72
-
73
- describe('basename normalization', () => {
74
- it('should strip trailing slashes from basename', () => {
75
- const provider1 = new NavigationProvider({
76
- version: '1.0.0',
77
- config: { history, basename: '/apps/my-app/' },
78
- });
79
- expect(provider1.basename).toBe('/apps/my-app');
80
- provider1.dispose();
81
-
82
- const provider2 = new NavigationProvider({
83
- version: '1.0.0',
84
- config: { history, basename: '/apps/my-app///' },
85
- });
86
- expect(provider2.basename).toBe('/apps/my-app');
87
- provider2.dispose();
88
- });
89
-
90
- it('should collapse consecutive slashes in basename', () => {
91
- const provider = new NavigationProvider({
92
- version: '1.0.0',
93
- config: { history, basename: '/apps//my-app' },
94
- });
95
- expect(provider.basename).toBe('/apps/my-app');
96
- provider.dispose();
97
- });
98
-
99
- it('should handle pathological input efficiently (ReDoS protection)', () => {
100
- // Create a string with many consecutive slashes to test performance
101
- // This would cause ReDoS with certain regex patterns
102
- const manySlashes = `/apps/${'/'.repeat(10000)}my-app`;
103
-
104
- const startTime = Date.now();
105
- const provider = new NavigationProvider({
106
- version: '1.0.0',
107
- config: { history, basename: manySlashes },
108
- });
109
- const endTime = Date.now();
110
-
111
- // Should complete in reasonable time (< 100ms for 10k slashes)
112
- expect(endTime - startTime).toBeLessThan(100);
113
- expect(provider.basename).toBe('/apps/my-app');
114
- provider.dispose();
115
- });
116
-
117
- it('should handle pathological trailing slashes efficiently (ReDoS protection)', () => {
118
- // Create a string with many trailing slashes
119
- // The /\/+$/ regex pattern would cause ReDoS with this input
120
- const manyTrailingSlashes = `/apps/my-app${'/'.repeat(10000)}`;
121
-
122
- const startTime = Date.now();
123
- const provider = new NavigationProvider({
124
- version: '1.0.0',
125
- config: { history, basename: manyTrailingSlashes },
126
- });
127
- const endTime = Date.now();
128
-
129
- // Should complete in reasonable time (< 100ms for 10k trailing slashes)
130
- expect(endTime - startTime).toBeLessThan(100);
131
- expect(provider.basename).toBe('/apps/my-app');
132
- provider.dispose();
133
- });
134
-
135
- it('should handle undefined basename', () => {
136
- const provider = new NavigationProvider({
137
- version: '1.0.0',
138
- config: { history, basename: undefined },
139
- });
140
- expect(provider.basename).toBe('');
141
- provider.dispose();
142
- });
143
- });
144
-
145
- describe('_isWithinBasenameScope', () => {
146
- it('should allow all paths when basename is "/" (no basename)', () => {
147
- const provider = new NavigationProvider({
148
- version: '1.0.0',
149
- config: { history, basename: '/' },
150
- });
151
-
152
- // All paths should be in scope
153
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
154
- expect((provider as any)._isWithinBasenameScope('/')).toBe(true);
155
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
156
- expect((provider as any)._isWithinBasenameScope('/apps')).toBe(true);
157
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
158
- expect((provider as any)._isWithinBasenameScope('/apps/my-app')).toBe(true);
159
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
160
- expect((provider as any)._isWithinBasenameScope('/apps/my-app/users')).toBe(true);
161
-
162
- provider.dispose();
163
- });
164
-
165
- it('should check path boundaries correctly with basename', () => {
166
- const provider = new NavigationProvider({
167
- version: '1.0.0',
168
- config: { history, basename: '/apps/my-app' },
169
- });
170
-
171
- // Exact match should be in scope
172
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
173
- expect((provider as any)._isWithinBasenameScope('/apps/my-app')).toBe(true);
174
-
175
- // Paths starting with basename/ should be in scope
176
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
177
- expect((provider as any)._isWithinBasenameScope('/apps/my-app/')).toBe(true);
178
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
179
- expect((provider as any)._isWithinBasenameScope('/apps/my-app/users')).toBe(true);
180
-
181
- // Similar but different path should NOT be in scope (path boundary check)
182
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
183
- expect((provider as any)._isWithinBasenameScope('/apps/my-app-other')).toBe(false);
184
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
185
- expect((provider as any)._isWithinBasenameScope('/apps/my-app-other/users')).toBe(false);
186
-
187
- // Completely different paths should NOT be in scope
188
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
189
- expect((provider as any)._isWithinBasenameScope('/other')).toBe(false);
190
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
191
- expect((provider as any)._isWithinBasenameScope('/')).toBe(false);
192
-
193
- provider.dispose();
194
- });
195
-
196
- it('should handle consecutive slashes in pathname', () => {
197
- const provider = new NavigationProvider({
198
- version: '1.0.0',
199
- config: { history, basename: '/apps/my-app' },
200
- });
201
-
202
- // Pathname with consecutive slashes should be normalized
203
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
204
- expect((provider as any)._isWithinBasenameScope('/apps//my-app')).toBe(true);
205
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
206
- expect((provider as any)._isWithinBasenameScope('/apps/my-app//users')).toBe(true);
207
-
208
- provider.dispose();
209
- });
210
- });
211
-
212
- describe('_localizePath', () => {
213
- it('should strip basename from paths on boundary', () => {
214
- const provider = new NavigationProvider({
215
- version: '1.0.0',
216
- config: { history, basename: '/apps/my-app' },
217
- });
218
-
219
- // Exact match should become '/'
220
- expect(
221
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
222
- (provider as any)._localizePath({ pathname: '/apps/my-app', search: '', hash: '' }),
223
- ).toEqual({ pathname: '/', search: '', hash: '' });
224
-
225
- // Path with basename prefix should have it stripped
226
- expect(
227
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
228
- (provider as any)._localizePath({
229
- pathname: '/apps/my-app/users',
230
- search: '?q=test',
231
- hash: '#section',
232
- }),
233
- ).toEqual({ pathname: '/users', search: '?q=test', hash: '#section' });
234
-
235
- provider.dispose();
236
- });
237
-
238
- it('should not strip similar paths without boundary match', () => {
239
- const provider = new NavigationProvider({
240
- version: '1.0.0',
241
- config: { history, basename: '/apps/my-app' },
242
- });
243
-
244
- // Path that starts similarly but isn't a real match should not be stripped
245
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
246
- const result = (provider as any)._localizePath({
247
- pathname: '/apps/my-app-other/users',
248
- search: '',
249
- hash: '',
250
- });
251
-
252
- // Should not strip anything since it doesn't match on path boundary
253
- expect(result.pathname).toBe('/apps/my-app-other/users');
254
-
255
- provider.dispose();
256
- });
257
-
258
- it('should handle root basename "/" correctly', () => {
259
- const provider = new NavigationProvider({
260
- version: '1.0.0',
261
- config: { history, basename: '/' },
262
- });
263
-
264
- // With no basename, paths should be returned normalized
265
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
266
- expect((provider as any)._localizePath({ pathname: '/', search: '', hash: '' })).toEqual({
267
- pathname: '/',
268
- search: '',
269
- hash: '',
270
- });
271
-
272
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
273
- expect((provider as any)._localizePath({ pathname: '/apps', search: '', hash: '' })).toEqual({
274
- pathname: '/apps',
275
- search: '',
276
- hash: '',
277
- });
278
-
279
- expect(
280
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
281
- (provider as any)._localizePath({ pathname: '/apps/my-app', search: '', hash: '' }),
282
- ).toEqual({ pathname: '/apps/my-app', search: '', hash: '' });
283
-
284
- provider.dispose();
285
- });
286
-
287
- it('should normalize consecutive slashes', () => {
288
- const provider = new NavigationProvider({
289
- version: '1.0.0',
290
- config: { history, basename: '/apps/my-app' },
291
- });
292
-
293
- // Consecutive slashes should be collapsed
294
- expect(
295
- // biome-ignore lint/suspicious/noExplicitAny: Allow usage of 'any' in test files for mocking purposes
296
- (provider as any)._localizePath({ pathname: '/apps//my-app//users', search: '', hash: '' }),
297
- ).toEqual({ pathname: '/users', search: '', hash: '' });
298
-
299
- provider.dispose();
300
- });
301
- });
302
- });