@angular/common 21.0.0-next.9 → 21.0.0-rc.0

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v21.0.0-next.9
2
+ * @license Angular v21.0.0-rc.0
3
3
  * (c) 2010-2025 Google LLC. https://angular.dev/
4
4
  * License: MIT
5
5
  */
@@ -13,584 +13,628 @@ import { ɵFakeNavigation as _FakeNavigation } from '@angular/core/testing';
13
13
  export { ɵFakeNavigation } from '@angular/core/testing';
14
14
  import { PlatformLocation, Location, LocationStrategy as LocationStrategy$1 } from './_location-chunk.mjs';
15
15
 
16
- /**
17
- * Parser from https://tools.ietf.org/html/rfc3986#appendix-B
18
- * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
19
- * 12 3 4 5 6 7 8 9
20
- *
21
- * Example: http://www.ics.uci.edu/pub/ietf/uri/#Related
22
- *
23
- * Results in:
24
- *
25
- * $1 = http:
26
- * $2 = http
27
- * $3 = //www.ics.uci.edu
28
- * $4 = www.ics.uci.edu
29
- * $5 = /pub/ietf/uri/
30
- * $6 = <undefined>
31
- * $7 = <undefined>
32
- * $8 = #Related
33
- * $9 = Related
34
- */
35
16
  const urlParse = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
36
17
  function parseUrl(urlStr, baseHref) {
37
- const verifyProtocol = /^((http[s]?|ftp):\/\/)/;
38
- let serverBase;
39
- // URL class requires full URL. If the URL string doesn't start with protocol, we need to add
40
- // an arbitrary base URL which can be removed afterward.
41
- if (!verifyProtocol.test(urlStr)) {
42
- serverBase = 'http://empty.com/';
43
- }
44
- let parsedUrl;
45
- try {
46
- parsedUrl = new URL(urlStr, serverBase);
47
- }
48
- catch (e) {
49
- const result = urlParse.exec(serverBase || '' + urlStr);
50
- if (!result) {
51
- throw new Error(`Invalid URL: ${urlStr} with base: ${baseHref}`);
52
- }
53
- const hostSplit = result[4].split(':');
54
- parsedUrl = {
55
- protocol: result[1],
56
- hostname: hostSplit[0],
57
- port: hostSplit[1] || '',
58
- pathname: result[5],
59
- search: result[6],
60
- hash: result[8],
61
- };
62
- }
63
- if (parsedUrl.pathname && parsedUrl.pathname.indexOf(baseHref) === 0) {
64
- parsedUrl.pathname = parsedUrl.pathname.substring(baseHref.length);
65
- }
66
- return {
67
- hostname: (!serverBase && parsedUrl.hostname) || '',
68
- protocol: (!serverBase && parsedUrl.protocol) || '',
69
- port: (!serverBase && parsedUrl.port) || '',
70
- pathname: parsedUrl.pathname || '/',
71
- search: parsedUrl.search || '',
72
- hash: parsedUrl.hash || '',
18
+ const verifyProtocol = /^((http[s]?|ftp):\/\/)/;
19
+ let serverBase;
20
+ if (!verifyProtocol.test(urlStr)) {
21
+ serverBase = 'http://empty.com/';
22
+ }
23
+ let parsedUrl;
24
+ try {
25
+ parsedUrl = new URL(urlStr, serverBase);
26
+ } catch (e) {
27
+ const result = urlParse.exec(serverBase || '' + urlStr);
28
+ if (!result) {
29
+ throw new Error(`Invalid URL: ${urlStr} with base: ${baseHref}`);
30
+ }
31
+ const hostSplit = result[4].split(':');
32
+ parsedUrl = {
33
+ protocol: result[1],
34
+ hostname: hostSplit[0],
35
+ port: hostSplit[1] || '',
36
+ pathname: result[5],
37
+ search: result[6],
38
+ hash: result[8]
73
39
  };
40
+ }
41
+ if (parsedUrl.pathname && parsedUrl.pathname.indexOf(baseHref) === 0) {
42
+ parsedUrl.pathname = parsedUrl.pathname.substring(baseHref.length);
43
+ }
44
+ return {
45
+ hostname: !serverBase && parsedUrl.hostname || '',
46
+ protocol: !serverBase && parsedUrl.protocol || '',
47
+ port: !serverBase && parsedUrl.port || '',
48
+ pathname: parsedUrl.pathname || '/',
49
+ search: parsedUrl.search || '',
50
+ hash: parsedUrl.hash || ''
51
+ };
74
52
  }
75
- /**
76
- * Provider for mock platform location config
77
- *
78
- * @publicApi
79
- */
80
53
  const MOCK_PLATFORM_LOCATION_CONFIG = new InjectionToken('MOCK_PLATFORM_LOCATION_CONFIG');
81
- /**
82
- * Mock implementation of URL state.
83
- *
84
- * @publicApi
85
- */
86
54
  class MockPlatformLocation {
87
- baseHref = '';
88
- hashUpdate = new Subject();
89
- popStateSubject = new Subject();
90
- urlChangeIndex = 0;
91
- urlChanges = [{ hostname: '', protocol: '', port: '', pathname: '/', search: '', hash: '', state: null }];
92
- constructor(config) {
93
- if (config) {
94
- this.baseHref = config.appBaseHref || '';
95
- const parsedChanges = this.parseChanges(null, config.startUrl || 'http://_empty_/', this.baseHref);
96
- this.urlChanges[0] = { ...parsedChanges };
97
- }
98
- }
99
- get hostname() {
100
- return this.urlChanges[this.urlChangeIndex].hostname;
101
- }
102
- get protocol() {
103
- return this.urlChanges[this.urlChangeIndex].protocol;
104
- }
105
- get port() {
106
- return this.urlChanges[this.urlChangeIndex].port;
107
- }
108
- get pathname() {
109
- return this.urlChanges[this.urlChangeIndex].pathname;
110
- }
111
- get search() {
112
- return this.urlChanges[this.urlChangeIndex].search;
113
- }
114
- get hash() {
115
- return this.urlChanges[this.urlChangeIndex].hash;
116
- }
117
- get state() {
118
- return this.urlChanges[this.urlChangeIndex].state;
119
- }
120
- getBaseHrefFromDOM() {
121
- return this.baseHref;
122
- }
123
- onPopState(fn) {
124
- const subscription = this.popStateSubject.subscribe(fn);
125
- return () => subscription.unsubscribe();
126
- }
127
- onHashChange(fn) {
128
- const subscription = this.hashUpdate.subscribe(fn);
129
- return () => subscription.unsubscribe();
130
- }
131
- get href() {
132
- let url = `${this.protocol}//${this.hostname}${this.port ? ':' + this.port : ''}`;
133
- url += `${this.pathname === '/' ? '' : this.pathname}${this.search}${this.hash}`;
134
- return url;
135
- }
136
- get url() {
137
- return `${this.pathname}${this.search}${this.hash}`;
138
- }
139
- parseChanges(state, url, baseHref = '') {
140
- // When the `history.state` value is stored, it is always copied.
141
- state = JSON.parse(JSON.stringify(state));
142
- return { ...parseUrl(url, baseHref), state };
143
- }
144
- replaceState(state, title, newUrl) {
145
- const { pathname, search, state: parsedState, hash } = this.parseChanges(state, newUrl);
146
- this.urlChanges[this.urlChangeIndex] = {
147
- ...this.urlChanges[this.urlChangeIndex],
148
- pathname,
149
- search,
150
- hash,
151
- state: parsedState,
152
- };
153
- }
154
- pushState(state, title, newUrl) {
155
- const { pathname, search, state: parsedState, hash } = this.parseChanges(state, newUrl);
156
- if (this.urlChangeIndex > 0) {
157
- this.urlChanges.splice(this.urlChangeIndex + 1);
158
- }
159
- this.urlChanges.push({
160
- ...this.urlChanges[this.urlChangeIndex],
161
- pathname,
162
- search,
163
- hash,
164
- state: parsedState,
165
- });
166
- this.urlChangeIndex = this.urlChanges.length - 1;
167
- }
168
- forward() {
169
- const oldUrl = this.url;
170
- const oldHash = this.hash;
171
- if (this.urlChangeIndex < this.urlChanges.length) {
172
- this.urlChangeIndex++;
173
- }
174
- this.emitEvents(oldHash, oldUrl);
175
- }
176
- back() {
177
- const oldUrl = this.url;
178
- const oldHash = this.hash;
179
- if (this.urlChangeIndex > 0) {
180
- this.urlChangeIndex--;
181
- }
182
- this.emitEvents(oldHash, oldUrl);
183
- }
184
- historyGo(relativePosition = 0) {
185
- const oldUrl = this.url;
186
- const oldHash = this.hash;
187
- const nextPageIndex = this.urlChangeIndex + relativePosition;
188
- if (nextPageIndex >= 0 && nextPageIndex < this.urlChanges.length) {
189
- this.urlChangeIndex = nextPageIndex;
190
- }
191
- this.emitEvents(oldHash, oldUrl);
192
- }
193
- getState() {
194
- return this.state;
195
- }
196
- /**
197
- * Browsers are inconsistent in when they fire events and perform the state updates
198
- * The most easiest thing to do in our mock is synchronous and that happens to match
199
- * Firefox and Chrome, at least somewhat closely
200
- *
201
- * https://github.com/WICG/navigation-api#watching-for-navigations
202
- * https://docs.google.com/document/d/1Pdve-DJ1JCGilj9Yqf5HxRJyBKSel5owgOvUJqTauwU/edit#heading=h.3ye4v71wsz94
203
- * popstate is always sent before hashchange:
204
- * https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event#when_popstate_is_sent
205
- */
206
- emitEvents(oldHash, oldUrl) {
207
- this.popStateSubject.next({
208
- type: 'popstate',
209
- state: this.getState(),
210
- oldUrl,
211
- newUrl: this.url,
212
- });
213
- if (oldHash !== this.hash) {
214
- this.hashUpdate.next({
215
- type: 'hashchange',
216
- state: null,
217
- oldUrl,
218
- newUrl: this.url,
219
- });
220
- }
221
- }
222
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: MockPlatformLocation, deps: [{ token: MOCK_PLATFORM_LOCATION_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
223
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: MockPlatformLocation });
55
+ baseHref = '';
56
+ hashUpdate = new Subject();
57
+ popStateSubject = new Subject();
58
+ urlChangeIndex = 0;
59
+ urlChanges = [{
60
+ hostname: '',
61
+ protocol: '',
62
+ port: '',
63
+ pathname: '/',
64
+ search: '',
65
+ hash: '',
66
+ state: null
67
+ }];
68
+ constructor(config) {
69
+ if (config) {
70
+ this.baseHref = config.appBaseHref || '';
71
+ const parsedChanges = this.parseChanges(null, config.startUrl || 'http://_empty_/', this.baseHref);
72
+ this.urlChanges[0] = {
73
+ ...parsedChanges
74
+ };
75
+ }
76
+ }
77
+ get hostname() {
78
+ return this.urlChanges[this.urlChangeIndex].hostname;
79
+ }
80
+ get protocol() {
81
+ return this.urlChanges[this.urlChangeIndex].protocol;
82
+ }
83
+ get port() {
84
+ return this.urlChanges[this.urlChangeIndex].port;
85
+ }
86
+ get pathname() {
87
+ return this.urlChanges[this.urlChangeIndex].pathname;
88
+ }
89
+ get search() {
90
+ return this.urlChanges[this.urlChangeIndex].search;
91
+ }
92
+ get hash() {
93
+ return this.urlChanges[this.urlChangeIndex].hash;
94
+ }
95
+ get state() {
96
+ return this.urlChanges[this.urlChangeIndex].state;
97
+ }
98
+ getBaseHrefFromDOM() {
99
+ return this.baseHref;
100
+ }
101
+ onPopState(fn) {
102
+ const subscription = this.popStateSubject.subscribe(fn);
103
+ return () => subscription.unsubscribe();
104
+ }
105
+ onHashChange(fn) {
106
+ const subscription = this.hashUpdate.subscribe(fn);
107
+ return () => subscription.unsubscribe();
108
+ }
109
+ get href() {
110
+ let url = `${this.protocol}//${this.hostname}${this.port ? ':' + this.port : ''}`;
111
+ url += `${this.pathname === '/' ? '' : this.pathname}${this.search}${this.hash}`;
112
+ return url;
113
+ }
114
+ get url() {
115
+ return `${this.pathname}${this.search}${this.hash}`;
116
+ }
117
+ parseChanges(state, url, baseHref = '') {
118
+ state = JSON.parse(JSON.stringify(state));
119
+ return {
120
+ ...parseUrl(url, baseHref),
121
+ state
122
+ };
123
+ }
124
+ replaceState(state, title, newUrl) {
125
+ const {
126
+ pathname,
127
+ search,
128
+ state: parsedState,
129
+ hash
130
+ } = this.parseChanges(state, newUrl);
131
+ this.urlChanges[this.urlChangeIndex] = {
132
+ ...this.urlChanges[this.urlChangeIndex],
133
+ pathname,
134
+ search,
135
+ hash,
136
+ state: parsedState
137
+ };
138
+ }
139
+ pushState(state, title, newUrl) {
140
+ const {
141
+ pathname,
142
+ search,
143
+ state: parsedState,
144
+ hash
145
+ } = this.parseChanges(state, newUrl);
146
+ if (this.urlChangeIndex > 0) {
147
+ this.urlChanges.splice(this.urlChangeIndex + 1);
148
+ }
149
+ this.urlChanges.push({
150
+ ...this.urlChanges[this.urlChangeIndex],
151
+ pathname,
152
+ search,
153
+ hash,
154
+ state: parsedState
155
+ });
156
+ this.urlChangeIndex = this.urlChanges.length - 1;
157
+ }
158
+ forward() {
159
+ const oldUrl = this.url;
160
+ const oldHash = this.hash;
161
+ if (this.urlChangeIndex < this.urlChanges.length) {
162
+ this.urlChangeIndex++;
163
+ }
164
+ this.emitEvents(oldHash, oldUrl);
165
+ }
166
+ back() {
167
+ const oldUrl = this.url;
168
+ const oldHash = this.hash;
169
+ if (this.urlChangeIndex > 0) {
170
+ this.urlChangeIndex--;
171
+ }
172
+ this.emitEvents(oldHash, oldUrl);
173
+ }
174
+ historyGo(relativePosition = 0) {
175
+ const oldUrl = this.url;
176
+ const oldHash = this.hash;
177
+ const nextPageIndex = this.urlChangeIndex + relativePosition;
178
+ if (nextPageIndex >= 0 && nextPageIndex < this.urlChanges.length) {
179
+ this.urlChangeIndex = nextPageIndex;
180
+ }
181
+ this.emitEvents(oldHash, oldUrl);
182
+ }
183
+ getState() {
184
+ return this.state;
185
+ }
186
+ emitEvents(oldHash, oldUrl) {
187
+ this.popStateSubject.next({
188
+ type: 'popstate',
189
+ state: this.getState(),
190
+ oldUrl,
191
+ newUrl: this.url
192
+ });
193
+ if (oldHash !== this.hash) {
194
+ this.hashUpdate.next({
195
+ type: 'hashchange',
196
+ state: null,
197
+ oldUrl,
198
+ newUrl: this.url
199
+ });
200
+ }
201
+ }
202
+ static ɵfac = i0.ɵɵngDeclareFactory({
203
+ minVersion: "12.0.0",
204
+ version: "21.0.0-rc.0",
205
+ ngImport: i0,
206
+ type: MockPlatformLocation,
207
+ deps: [{
208
+ token: MOCK_PLATFORM_LOCATION_CONFIG,
209
+ optional: true
210
+ }],
211
+ target: i0.ɵɵFactoryTarget.Injectable
212
+ });
213
+ static ɵprov = i0.ɵɵngDeclareInjectable({
214
+ minVersion: "12.0.0",
215
+ version: "21.0.0-rc.0",
216
+ ngImport: i0,
217
+ type: MockPlatformLocation
218
+ });
224
219
  }
225
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: MockPlatformLocation, decorators: [{
226
- type: Injectable
227
- }], ctorParameters: () => [{ type: undefined, decorators: [{
228
- type: Inject,
229
- args: [MOCK_PLATFORM_LOCATION_CONFIG]
230
- }, {
231
- type: Optional
232
- }] }] });
233
- /**
234
- * Mock implementation of URL state.
235
- */
220
+ i0.ɵɵngDeclareClassMetadata({
221
+ minVersion: "12.0.0",
222
+ version: "21.0.0-rc.0",
223
+ ngImport: i0,
224
+ type: MockPlatformLocation,
225
+ decorators: [{
226
+ type: Injectable
227
+ }],
228
+ ctorParameters: () => [{
229
+ type: undefined,
230
+ decorators: [{
231
+ type: Inject,
232
+ args: [MOCK_PLATFORM_LOCATION_CONFIG]
233
+ }, {
234
+ type: Optional
235
+ }]
236
+ }]
237
+ });
236
238
  class FakeNavigationPlatformLocation {
237
- _platformNavigation;
238
- constructor() {
239
- const platformNavigation = inject(PlatformNavigation);
240
- if (!(platformNavigation instanceof _FakeNavigation)) {
241
- throw new Error('FakePlatformNavigation cannot be used without FakeNavigation. Use ' +
242
- '`provideFakeNavigation` to have all these services provided together.');
243
- }
244
- this._platformNavigation = platformNavigation;
245
- }
246
- config = inject(MOCK_PLATFORM_LOCATION_CONFIG, { optional: true });
247
- getBaseHrefFromDOM() {
248
- return this.config?.appBaseHref ?? '';
249
- }
250
- onPopState(fn) {
251
- this._platformNavigation.window.addEventListener('popstate', fn);
252
- return () => this._platformNavigation.window.removeEventListener('popstate', fn);
253
- }
254
- onHashChange(fn) {
255
- this._platformNavigation.window.addEventListener('hashchange', fn);
256
- return () => this._platformNavigation.window.removeEventListener('hashchange', fn);
257
- }
258
- get href() {
259
- return this._platformNavigation.currentEntry.url;
260
- }
261
- get protocol() {
262
- return new URL(this._platformNavigation.currentEntry.url).protocol;
263
- }
264
- get hostname() {
265
- return new URL(this._platformNavigation.currentEntry.url).hostname;
266
- }
267
- get port() {
268
- return new URL(this._platformNavigation.currentEntry.url).port;
269
- }
270
- get pathname() {
271
- return new URL(this._platformNavigation.currentEntry.url).pathname;
272
- }
273
- get search() {
274
- return new URL(this._platformNavigation.currentEntry.url).search;
275
- }
276
- get hash() {
277
- return new URL(this._platformNavigation.currentEntry.url).hash;
278
- }
279
- pushState(state, title, url) {
280
- this._platformNavigation.pushState(state, title, url);
281
- }
282
- replaceState(state, title, url) {
283
- this._platformNavigation.replaceState(state, title, url);
284
- }
285
- forward() {
286
- this._platformNavigation.forward();
287
- }
288
- back() {
289
- this._platformNavigation.back();
290
- }
291
- historyGo(relativePosition = 0) {
292
- this._platformNavigation.go(relativePosition);
293
- }
294
- getState() {
295
- return this._platformNavigation.currentEntry.getHistoryState();
296
- }
297
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: FakeNavigationPlatformLocation, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
298
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: FakeNavigationPlatformLocation });
239
+ _platformNavigation;
240
+ constructor() {
241
+ const platformNavigation = inject(PlatformNavigation);
242
+ if (!(platformNavigation instanceof _FakeNavigation)) {
243
+ throw new Error('FakePlatformNavigation cannot be used without FakeNavigation. Use ' + '`provideFakeNavigation` to have all these services provided together.');
244
+ }
245
+ this._platformNavigation = platformNavigation;
246
+ }
247
+ config = inject(MOCK_PLATFORM_LOCATION_CONFIG, {
248
+ optional: true
249
+ });
250
+ getBaseHrefFromDOM() {
251
+ return this.config?.appBaseHref ?? '';
252
+ }
253
+ onPopState(fn) {
254
+ this._platformNavigation.window.addEventListener('popstate', fn);
255
+ return () => this._platformNavigation.window.removeEventListener('popstate', fn);
256
+ }
257
+ onHashChange(fn) {
258
+ this._platformNavigation.window.addEventListener('hashchange', fn);
259
+ return () => this._platformNavigation.window.removeEventListener('hashchange', fn);
260
+ }
261
+ get href() {
262
+ return this._platformNavigation.currentEntry.url;
263
+ }
264
+ get protocol() {
265
+ return new URL(this._platformNavigation.currentEntry.url).protocol;
266
+ }
267
+ get hostname() {
268
+ return new URL(this._platformNavigation.currentEntry.url).hostname;
269
+ }
270
+ get port() {
271
+ return new URL(this._platformNavigation.currentEntry.url).port;
272
+ }
273
+ get pathname() {
274
+ return new URL(this._platformNavigation.currentEntry.url).pathname;
275
+ }
276
+ get search() {
277
+ return new URL(this._platformNavigation.currentEntry.url).search;
278
+ }
279
+ get hash() {
280
+ return new URL(this._platformNavigation.currentEntry.url).hash;
281
+ }
282
+ pushState(state, title, url) {
283
+ this._platformNavigation.pushState(state, title, url);
284
+ }
285
+ replaceState(state, title, url) {
286
+ this._platformNavigation.replaceState(state, title, url);
287
+ }
288
+ forward() {
289
+ this._platformNavigation.forward();
290
+ }
291
+ back() {
292
+ this._platformNavigation.back();
293
+ }
294
+ historyGo(relativePosition = 0) {
295
+ this._platformNavigation.go(relativePosition);
296
+ }
297
+ getState() {
298
+ return this._platformNavigation.currentEntry.getHistoryState();
299
+ }
300
+ static ɵfac = i0.ɵɵngDeclareFactory({
301
+ minVersion: "12.0.0",
302
+ version: "21.0.0-rc.0",
303
+ ngImport: i0,
304
+ type: FakeNavigationPlatformLocation,
305
+ deps: [],
306
+ target: i0.ɵɵFactoryTarget.Injectable
307
+ });
308
+ static ɵprov = i0.ɵɵngDeclareInjectable({
309
+ minVersion: "12.0.0",
310
+ version: "21.0.0-rc.0",
311
+ ngImport: i0,
312
+ type: FakeNavigationPlatformLocation
313
+ });
299
314
  }
300
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: FakeNavigationPlatformLocation, decorators: [{
301
- type: Injectable
302
- }], ctorParameters: () => [] });
315
+ i0.ɵɵngDeclareClassMetadata({
316
+ minVersion: "12.0.0",
317
+ version: "21.0.0-rc.0",
318
+ ngImport: i0,
319
+ type: FakeNavigationPlatformLocation,
320
+ decorators: [{
321
+ type: Injectable
322
+ }],
323
+ ctorParameters: () => []
324
+ });
303
325
 
304
326
  const FAKE_NAVIGATION = new InjectionToken('fakeNavigation', {
305
- providedIn: 'root',
306
- factory: () => {
307
- const config = inject(MOCK_PLATFORM_LOCATION_CONFIG, { optional: true });
308
- const baseFallback = 'http://_empty_/';
309
- const startUrl = new URL(config?.startUrl || baseFallback, baseFallback);
310
- const fakeNavigation = new _FakeNavigation(inject(DOCUMENT), startUrl.href);
311
- fakeNavigation.setSynchronousTraversalsForTesting(true);
312
- return fakeNavigation;
313
- },
327
+ providedIn: 'root',
328
+ factory: () => {
329
+ const config = inject(MOCK_PLATFORM_LOCATION_CONFIG, {
330
+ optional: true
331
+ });
332
+ const baseFallback = 'http://_empty_/';
333
+ const startUrl = new URL(config?.startUrl || baseFallback, baseFallback);
334
+ const fakeNavigation = new _FakeNavigation(inject(DOCUMENT), startUrl.href);
335
+ fakeNavigation.setSynchronousTraversalsForTesting(true);
336
+ return fakeNavigation;
337
+ }
314
338
  });
315
- /**
316
- * Return a provider for the `FakeNavigation` in place of the real Navigation API.
317
- */
318
339
  function provideFakePlatformNavigation() {
319
- return [
320
- {
321
- provide: PlatformNavigation,
322
- useFactory: () => inject(FAKE_NAVIGATION),
323
- },
324
- { provide: PlatformLocation, useClass: FakeNavigationPlatformLocation },
325
- ];
340
+ return [{
341
+ provide: PlatformNavigation,
342
+ useFactory: () => inject(FAKE_NAVIGATION)
343
+ }, {
344
+ provide: PlatformLocation,
345
+ useClass: FakeNavigationPlatformLocation
346
+ }];
326
347
  }
327
348
 
328
- /**
329
- * A spy for {@link Location} that allows tests to fire simulated location events.
330
- *
331
- * @publicApi
332
- */
333
349
  class SpyLocation {
334
- urlChanges = [];
335
- _history = [new LocationState('', '', null)];
336
- _historyIndex = 0;
337
- /** @internal */
338
- _subject = new Subject();
339
- /** @internal */
340
- _basePath = '';
341
- /** @internal */
342
- _locationStrategy = null;
343
- /** @internal */
344
- _urlChangeListeners = [];
345
- /** @internal */
346
- _urlChangeSubscription = null;
347
- /** @docs-private */
348
- ngOnDestroy() {
350
+ urlChanges = [];
351
+ _history = [new LocationState('', '', null)];
352
+ _historyIndex = 0;
353
+ _subject = new Subject();
354
+ _basePath = '';
355
+ _locationStrategy = null;
356
+ _urlChangeListeners = [];
357
+ _urlChangeSubscription = null;
358
+ ngOnDestroy() {
359
+ this._urlChangeSubscription?.unsubscribe();
360
+ this._urlChangeListeners = [];
361
+ }
362
+ setInitialPath(url) {
363
+ this._history[this._historyIndex].path = url;
364
+ }
365
+ setBaseHref(url) {
366
+ this._basePath = url;
367
+ }
368
+ path() {
369
+ return this._history[this._historyIndex].path;
370
+ }
371
+ getState() {
372
+ return this._history[this._historyIndex].state;
373
+ }
374
+ isCurrentPathEqualTo(path, query = '') {
375
+ const givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
376
+ const currPath = this.path().endsWith('/') ? this.path().substring(0, this.path().length - 1) : this.path();
377
+ return currPath == givenPath + (query.length > 0 ? '?' + query : '');
378
+ }
379
+ simulateUrlPop(pathname) {
380
+ this._subject.next({
381
+ 'url': pathname,
382
+ 'pop': true,
383
+ 'type': 'popstate'
384
+ });
385
+ }
386
+ simulateHashChange(pathname) {
387
+ const path = this.prepareExternalUrl(pathname);
388
+ this.pushHistory(path, '', null);
389
+ this.urlChanges.push('hash: ' + pathname);
390
+ this._subject.next({
391
+ 'url': pathname,
392
+ 'pop': true,
393
+ 'type': 'popstate'
394
+ });
395
+ this._subject.next({
396
+ 'url': pathname,
397
+ 'pop': true,
398
+ 'type': 'hashchange'
399
+ });
400
+ }
401
+ prepareExternalUrl(url) {
402
+ if (url.length > 0 && !url.startsWith('/')) {
403
+ url = '/' + url;
404
+ }
405
+ return this._basePath + url;
406
+ }
407
+ go(path, query = '', state = null) {
408
+ path = this.prepareExternalUrl(path);
409
+ this.pushHistory(path, query, state);
410
+ const locationState = this._history[this._historyIndex - 1];
411
+ if (locationState.path == path && locationState.query == query) {
412
+ return;
413
+ }
414
+ const url = path + (query.length > 0 ? '?' + query : '');
415
+ this.urlChanges.push(url);
416
+ this._notifyUrlChangeListeners(path + _normalizeQueryParams(query), state);
417
+ }
418
+ replaceState(path, query = '', state = null) {
419
+ path = this.prepareExternalUrl(path);
420
+ const history = this._history[this._historyIndex];
421
+ history.state = state;
422
+ if (history.path == path && history.query == query) {
423
+ return;
424
+ }
425
+ history.path = path;
426
+ history.query = query;
427
+ const url = path + (query.length > 0 ? '?' + query : '');
428
+ this.urlChanges.push('replace: ' + url);
429
+ this._notifyUrlChangeListeners(path + _normalizeQueryParams(query), state);
430
+ }
431
+ forward() {
432
+ if (this._historyIndex < this._history.length - 1) {
433
+ this._historyIndex++;
434
+ this._subject.next({
435
+ 'url': this.path(),
436
+ 'state': this.getState(),
437
+ 'pop': true,
438
+ 'type': 'popstate'
439
+ });
440
+ }
441
+ }
442
+ back() {
443
+ if (this._historyIndex > 0) {
444
+ this._historyIndex--;
445
+ this._subject.next({
446
+ 'url': this.path(),
447
+ 'state': this.getState(),
448
+ 'pop': true,
449
+ 'type': 'popstate'
450
+ });
451
+ }
452
+ }
453
+ historyGo(relativePosition = 0) {
454
+ const nextPageIndex = this._historyIndex + relativePosition;
455
+ if (nextPageIndex >= 0 && nextPageIndex < this._history.length) {
456
+ this._historyIndex = nextPageIndex;
457
+ this._subject.next({
458
+ 'url': this.path(),
459
+ 'state': this.getState(),
460
+ 'pop': true,
461
+ 'type': 'popstate'
462
+ });
463
+ }
464
+ }
465
+ onUrlChange(fn) {
466
+ this._urlChangeListeners.push(fn);
467
+ this._urlChangeSubscription ??= this.subscribe(v => {
468
+ this._notifyUrlChangeListeners(v.url, v.state);
469
+ });
470
+ return () => {
471
+ const fnIndex = this._urlChangeListeners.indexOf(fn);
472
+ this._urlChangeListeners.splice(fnIndex, 1);
473
+ if (this._urlChangeListeners.length === 0) {
349
474
  this._urlChangeSubscription?.unsubscribe();
350
- this._urlChangeListeners = [];
351
- }
352
- setInitialPath(url) {
353
- this._history[this._historyIndex].path = url;
354
- }
355
- setBaseHref(url) {
356
- this._basePath = url;
357
- }
358
- path() {
359
- return this._history[this._historyIndex].path;
360
- }
361
- getState() {
362
- return this._history[this._historyIndex].state;
363
- }
364
- isCurrentPathEqualTo(path, query = '') {
365
- const givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
366
- const currPath = this.path().endsWith('/')
367
- ? this.path().substring(0, this.path().length - 1)
368
- : this.path();
369
- return currPath == givenPath + (query.length > 0 ? '?' + query : '');
370
- }
371
- simulateUrlPop(pathname) {
372
- this._subject.next({ 'url': pathname, 'pop': true, 'type': 'popstate' });
373
- }
374
- simulateHashChange(pathname) {
375
- const path = this.prepareExternalUrl(pathname);
376
- this.pushHistory(path, '', null);
377
- this.urlChanges.push('hash: ' + pathname);
378
- // the browser will automatically fire popstate event before each `hashchange` event, so we need
379
- // to simulate it.
380
- this._subject.next({ 'url': pathname, 'pop': true, 'type': 'popstate' });
381
- this._subject.next({ 'url': pathname, 'pop': true, 'type': 'hashchange' });
382
- }
383
- prepareExternalUrl(url) {
384
- if (url.length > 0 && !url.startsWith('/')) {
385
- url = '/' + url;
386
- }
387
- return this._basePath + url;
388
- }
389
- go(path, query = '', state = null) {
390
- path = this.prepareExternalUrl(path);
391
- this.pushHistory(path, query, state);
392
- const locationState = this._history[this._historyIndex - 1];
393
- if (locationState.path == path && locationState.query == query) {
394
- return;
395
- }
396
- const url = path + (query.length > 0 ? '?' + query : '');
397
- this.urlChanges.push(url);
398
- this._notifyUrlChangeListeners(path + _normalizeQueryParams(query), state);
399
- }
400
- replaceState(path, query = '', state = null) {
401
- path = this.prepareExternalUrl(path);
402
- const history = this._history[this._historyIndex];
403
- history.state = state;
404
- if (history.path == path && history.query == query) {
405
- return;
406
- }
407
- history.path = path;
408
- history.query = query;
409
- const url = path + (query.length > 0 ? '?' + query : '');
410
- this.urlChanges.push('replace: ' + url);
411
- this._notifyUrlChangeListeners(path + _normalizeQueryParams(query), state);
412
- }
413
- forward() {
414
- if (this._historyIndex < this._history.length - 1) {
415
- this._historyIndex++;
416
- this._subject.next({
417
- 'url': this.path(),
418
- 'state': this.getState(),
419
- 'pop': true,
420
- 'type': 'popstate',
421
- });
422
- }
423
- }
424
- back() {
425
- if (this._historyIndex > 0) {
426
- this._historyIndex--;
427
- this._subject.next({
428
- 'url': this.path(),
429
- 'state': this.getState(),
430
- 'pop': true,
431
- 'type': 'popstate',
432
- });
433
- }
434
- }
435
- historyGo(relativePosition = 0) {
436
- const nextPageIndex = this._historyIndex + relativePosition;
437
- if (nextPageIndex >= 0 && nextPageIndex < this._history.length) {
438
- this._historyIndex = nextPageIndex;
439
- this._subject.next({
440
- 'url': this.path(),
441
- 'state': this.getState(),
442
- 'pop': true,
443
- 'type': 'popstate',
444
- });
445
- }
446
- }
447
- onUrlChange(fn) {
448
- this._urlChangeListeners.push(fn);
449
- this._urlChangeSubscription ??= this.subscribe((v) => {
450
- this._notifyUrlChangeListeners(v.url, v.state);
451
- });
452
- return () => {
453
- const fnIndex = this._urlChangeListeners.indexOf(fn);
454
- this._urlChangeListeners.splice(fnIndex, 1);
455
- if (this._urlChangeListeners.length === 0) {
456
- this._urlChangeSubscription?.unsubscribe();
457
- this._urlChangeSubscription = null;
458
- }
459
- };
460
- }
461
- /** @internal */
462
- _notifyUrlChangeListeners(url = '', state) {
463
- this._urlChangeListeners.forEach((fn) => fn(url, state));
464
- }
465
- subscribe(onNext, onThrow, onReturn) {
466
- return this._subject.subscribe({
467
- next: onNext,
468
- error: onThrow ?? undefined,
469
- complete: onReturn ?? undefined,
470
- });
471
- }
472
- normalize(url) {
473
- return null;
474
- }
475
- pushHistory(path, query, state) {
476
- if (this._historyIndex > 0) {
477
- this._history.splice(this._historyIndex + 1);
478
- }
479
- this._history.push(new LocationState(path, query, state));
480
- this._historyIndex = this._history.length - 1;
481
- }
482
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: SpyLocation, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
483
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: SpyLocation });
475
+ this._urlChangeSubscription = null;
476
+ }
477
+ };
478
+ }
479
+ _notifyUrlChangeListeners(url = '', state) {
480
+ this._urlChangeListeners.forEach(fn => fn(url, state));
481
+ }
482
+ subscribe(onNext, onThrow, onReturn) {
483
+ return this._subject.subscribe({
484
+ next: onNext,
485
+ error: onThrow ?? undefined,
486
+ complete: onReturn ?? undefined
487
+ });
488
+ }
489
+ normalize(url) {
490
+ return null;
491
+ }
492
+ pushHistory(path, query, state) {
493
+ if (this._historyIndex > 0) {
494
+ this._history.splice(this._historyIndex + 1);
495
+ }
496
+ this._history.push(new LocationState(path, query, state));
497
+ this._historyIndex = this._history.length - 1;
498
+ }
499
+ static ɵfac = i0.ɵɵngDeclareFactory({
500
+ minVersion: "12.0.0",
501
+ version: "21.0.0-rc.0",
502
+ ngImport: i0,
503
+ type: SpyLocation,
504
+ deps: [],
505
+ target: i0.ɵɵFactoryTarget.Injectable
506
+ });
507
+ static ɵprov = i0.ɵɵngDeclareInjectable({
508
+ minVersion: "12.0.0",
509
+ version: "21.0.0-rc.0",
510
+ ngImport: i0,
511
+ type: SpyLocation
512
+ });
484
513
  }
485
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: SpyLocation, decorators: [{
486
- type: Injectable
487
- }] });
514
+ i0.ɵɵngDeclareClassMetadata({
515
+ minVersion: "12.0.0",
516
+ version: "21.0.0-rc.0",
517
+ ngImport: i0,
518
+ type: SpyLocation,
519
+ decorators: [{
520
+ type: Injectable
521
+ }]
522
+ });
488
523
  class LocationState {
489
- path;
490
- query;
491
- state;
492
- constructor(path, query, state) {
493
- this.path = path;
494
- this.query = query;
495
- this.state = state;
496
- }
524
+ path;
525
+ query;
526
+ state;
527
+ constructor(path, query, state) {
528
+ this.path = path;
529
+ this.query = query;
530
+ this.state = state;
531
+ }
497
532
  }
498
533
 
499
- /**
500
- * A mock implementation of {@link LocationStrategy} that allows tests to fire simulated
501
- * location events.
502
- *
503
- * @publicApi
504
- */
505
534
  class MockLocationStrategy extends LocationStrategy {
506
- internalBaseHref = '/';
507
- internalPath = '/';
508
- internalTitle = '';
509
- urlChanges = [];
510
- /** @internal */
511
- _subject = new Subject();
512
- stateChanges = [];
513
- constructor() {
514
- super();
515
- }
516
- simulatePopState(url) {
517
- this.internalPath = url;
518
- this._subject.next(new _MockPopStateEvent(this.path()));
519
- }
520
- path(includeHash = false) {
521
- return this.internalPath;
522
- }
523
- prepareExternalUrl(internal) {
524
- if (internal.startsWith('/') && this.internalBaseHref.endsWith('/')) {
525
- return this.internalBaseHref + internal.substring(1);
526
- }
527
- return this.internalBaseHref + internal;
528
- }
529
- pushState(ctx, title, path, query) {
530
- // Add state change to changes array
531
- this.stateChanges.push(ctx);
532
- this.internalTitle = title;
533
- const url = path + (query.length > 0 ? '?' + query : '');
534
- this.internalPath = url;
535
- const externalUrl = this.prepareExternalUrl(url);
536
- this.urlChanges.push(externalUrl);
537
- }
538
- replaceState(ctx, title, path, query) {
539
- // Reset the last index of stateChanges to the ctx (state) object
540
- this.stateChanges[(this.stateChanges.length || 1) - 1] = ctx;
541
- this.internalTitle = title;
542
- const url = path + (query.length > 0 ? '?' + query : '');
543
- this.internalPath = url;
544
- const externalUrl = this.prepareExternalUrl(url);
545
- this.urlChanges.push('replace: ' + externalUrl);
546
- }
547
- onPopState(fn) {
548
- this._subject.subscribe({ next: fn });
549
- }
550
- getBaseHref() {
551
- return this.internalBaseHref;
552
- }
553
- back() {
554
- if (this.urlChanges.length > 0) {
555
- this.urlChanges.pop();
556
- this.stateChanges.pop();
557
- const nextUrl = this.urlChanges.length > 0 ? this.urlChanges[this.urlChanges.length - 1] : '';
558
- this.simulatePopState(nextUrl);
559
- }
560
- }
561
- forward() {
562
- throw 'not implemented';
563
- }
564
- getState() {
565
- return this.stateChanges[(this.stateChanges.length || 1) - 1];
566
- }
567
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: MockLocationStrategy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
568
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: MockLocationStrategy });
535
+ internalBaseHref = '/';
536
+ internalPath = '/';
537
+ internalTitle = '';
538
+ urlChanges = [];
539
+ _subject = new Subject();
540
+ stateChanges = [];
541
+ constructor() {
542
+ super();
543
+ }
544
+ simulatePopState(url) {
545
+ this.internalPath = url;
546
+ this._subject.next(new _MockPopStateEvent(this.path()));
547
+ }
548
+ path(includeHash = false) {
549
+ return this.internalPath;
550
+ }
551
+ prepareExternalUrl(internal) {
552
+ if (internal.startsWith('/') && this.internalBaseHref.endsWith('/')) {
553
+ return this.internalBaseHref + internal.substring(1);
554
+ }
555
+ return this.internalBaseHref + internal;
556
+ }
557
+ pushState(ctx, title, path, query) {
558
+ this.stateChanges.push(ctx);
559
+ this.internalTitle = title;
560
+ const url = path + (query.length > 0 ? '?' + query : '');
561
+ this.internalPath = url;
562
+ const externalUrl = this.prepareExternalUrl(url);
563
+ this.urlChanges.push(externalUrl);
564
+ }
565
+ replaceState(ctx, title, path, query) {
566
+ this.stateChanges[(this.stateChanges.length || 1) - 1] = ctx;
567
+ this.internalTitle = title;
568
+ const url = path + (query.length > 0 ? '?' + query : '');
569
+ this.internalPath = url;
570
+ const externalUrl = this.prepareExternalUrl(url);
571
+ this.urlChanges.push('replace: ' + externalUrl);
572
+ }
573
+ onPopState(fn) {
574
+ this._subject.subscribe({
575
+ next: fn
576
+ });
577
+ }
578
+ getBaseHref() {
579
+ return this.internalBaseHref;
580
+ }
581
+ back() {
582
+ if (this.urlChanges.length > 0) {
583
+ this.urlChanges.pop();
584
+ this.stateChanges.pop();
585
+ const nextUrl = this.urlChanges.length > 0 ? this.urlChanges[this.urlChanges.length - 1] : '';
586
+ this.simulatePopState(nextUrl);
587
+ }
588
+ }
589
+ forward() {
590
+ throw 'not implemented';
591
+ }
592
+ getState() {
593
+ return this.stateChanges[(this.stateChanges.length || 1) - 1];
594
+ }
595
+ static ɵfac = i0.ɵɵngDeclareFactory({
596
+ minVersion: "12.0.0",
597
+ version: "21.0.0-rc.0",
598
+ ngImport: i0,
599
+ type: MockLocationStrategy,
600
+ deps: [],
601
+ target: i0.ɵɵFactoryTarget.Injectable
602
+ });
603
+ static ɵprov = i0.ɵɵngDeclareInjectable({
604
+ minVersion: "12.0.0",
605
+ version: "21.0.0-rc.0",
606
+ ngImport: i0,
607
+ type: MockLocationStrategy
608
+ });
569
609
  }
570
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0-next.9", ngImport: i0, type: MockLocationStrategy, decorators: [{
571
- type: Injectable
572
- }], ctorParameters: () => [] });
610
+ i0.ɵɵngDeclareClassMetadata({
611
+ minVersion: "12.0.0",
612
+ version: "21.0.0-rc.0",
613
+ ngImport: i0,
614
+ type: MockLocationStrategy,
615
+ decorators: [{
616
+ type: Injectable
617
+ }],
618
+ ctorParameters: () => []
619
+ });
573
620
  class _MockPopStateEvent {
574
- newUrl;
575
- pop = true;
576
- type = 'popstate';
577
- constructor(newUrl) {
578
- this.newUrl = newUrl;
579
- }
621
+ newUrl;
622
+ pop = true;
623
+ type = 'popstate';
624
+ constructor(newUrl) {
625
+ this.newUrl = newUrl;
626
+ }
580
627
  }
581
628
 
582
- /**
583
- * Returns mock providers for the `Location` and `LocationStrategy` classes.
584
- * The mocks are helpful in tests to fire simulated location events.
585
- *
586
- * @publicApi
587
- */
588
629
  function provideLocationMocks() {
589
- return [
590
- { provide: Location, useClass: SpyLocation },
591
- { provide: LocationStrategy$1, useClass: MockLocationStrategy },
592
- ];
630
+ return [{
631
+ provide: Location,
632
+ useClass: SpyLocation
633
+ }, {
634
+ provide: LocationStrategy$1,
635
+ useClass: MockLocationStrategy
636
+ }];
593
637
  }
594
638
 
595
- export { MOCK_PLATFORM_LOCATION_CONFIG, MockLocationStrategy, MockPlatformLocation, SpyLocation, provideLocationMocks, provideFakePlatformNavigation as ɵprovideFakePlatformNavigation };
639
+ export { MOCK_PLATFORM_LOCATION_CONFIG, MockLocationStrategy, MockPlatformLocation, SpyLocation, provideLocationMocks, FakeNavigationPlatformLocation as ɵFakeNavigationPlatformLocation, provideFakePlatformNavigation as ɵprovideFakePlatformNavigation };
596
640
  //# sourceMappingURL=testing.mjs.map