7
7
*/
8
8
9
9
import { PRIMARY_OUTLET } from '../src/shared' ;
10
- import { DefaultUrlSerializer , UrlSegmentGroup , encode , serializePath } from '../src/url_tree' ;
10
+ import { DefaultUrlSerializer , UrlSegmentGroup , encodeUriQuery , encodeUriSegment , serializePath } from '../src/url_tree' ;
11
11
12
12
describe ( 'url serializer' , ( ) => {
13
13
const url = new DefaultUrlSerializer ( ) ;
@@ -189,7 +189,7 @@ describe('url serializer', () => {
189
189
describe ( 'encoding/decoding' , ( ) => {
190
190
it ( 'should encode/decode path segments and parameters' , ( ) => {
191
191
const u =
192
- `/${ encode ( "one two" ) } ;${ encode ( "p 1" ) } =${ encode ( "v 1" ) } ;${ encode ( "p 2" ) } =${ encode ( "v 2" ) } ` ;
192
+ `/${ encodeUriSegment ( "one two" ) } ;${ encodeUriSegment ( "p 1" ) } =${ encodeUriSegment ( "v 1" ) } ;${ encodeUriSegment ( "p 2" ) } =${ encodeUriSegment ( "v 2" ) } ` ;
193
193
const tree = url . parse ( u ) ;
194
194
195
195
expect ( tree . root . children [ PRIMARY_OUTLET ] . segments [ 0 ] . path ) . toEqual ( 'one two' ) ;
@@ -199,7 +199,8 @@ describe('url serializer', () => {
199
199
} ) ;
200
200
201
201
it ( 'should encode/decode "slash" in path segments and parameters' , ( ) => {
202
- const u = `/${ encode ( "one/two" ) } ;${ encode ( "p/1" ) } =${ encode ( "v/1" ) } /three` ;
202
+ const u =
203
+ `/${ encodeUriSegment ( "one/two" ) } ;${ encodeUriSegment ( "p/1" ) } =${ encodeUriSegment ( "v/1" ) } /three` ;
203
204
const tree = url . parse ( u ) ;
204
205
const segment = tree . root . children [ PRIMARY_OUTLET ] . segments [ 0 ] ;
205
206
expect ( segment . path ) . toEqual ( 'one/two' ) ;
@@ -210,7 +211,8 @@ describe('url serializer', () => {
210
211
} ) ;
211
212
212
213
it ( 'should encode/decode query params' , ( ) => {
213
- const u = `/one?${ encode ( "p 1" ) } =${ encode ( "v 1" ) } &${ encode ( "p 2" ) } =${ encode ( "v 2" ) } ` ;
214
+ const u =
215
+ `/one?${ encodeUriQuery ( "p 1" ) } =${ encodeUriQuery ( "v 1" ) } &${ encodeUriQuery ( "p 2" ) } =${ encodeUriQuery ( "v 2" ) } ` ;
214
216
const tree = url . parse ( u ) ;
215
217
216
218
expect ( tree . queryParams ) . toEqual ( { 'p 1' : 'v 1' , 'p 2' : 'v 2' } ) ;
@@ -219,28 +221,143 @@ describe('url serializer', () => {
219
221
expect ( url . serialize ( tree ) ) . toEqual ( u ) ;
220
222
} ) ;
221
223
224
+ it ( 'should decode spaces in query as %20 or +' , ( ) => {
225
+ const u1 = `/one?foo=bar baz` ;
226
+ const u2 = `/one?foo=bar+baz` ;
227
+ const u3 = `/one?foo=bar%20baz` ;
228
+
229
+ const u1p = url . parse ( u1 ) ;
230
+ const u2p = url . parse ( u2 ) ;
231
+ const u3p = url . parse ( u3 ) ;
232
+
233
+ expect ( url . serialize ( u1p ) ) . toBe ( url . serialize ( u2p ) ) ;
234
+ expect ( url . serialize ( u2p ) ) . toBe ( url . serialize ( u3p ) ) ;
235
+ expect ( u1p . queryParamMap . get ( 'foo' ) ) . toBe ( 'bar baz' ) ;
236
+ expect ( u2p . queryParamMap . get ( 'foo' ) ) . toBe ( 'bar baz' ) ;
237
+ expect ( u3p . queryParamMap . get ( 'foo' ) ) . toBe ( 'bar baz' ) ;
238
+ } ) ;
239
+
222
240
it ( 'should encode query params leaving sub-delimiters intact' , ( ) => {
223
- const percentChars = '/?#[] &+= ' ;
224
- const percentCharsEncoded = '%2F%3F%23%5B%5D% 26%2B%3D%20' ;
241
+ const percentChars = '/?#&+=[] ' ;
242
+ const percentCharsEncoded = '%2F%3F%23%26%2B%3D%5B%5D %20' ;
225
243
const intactChars = '!$\'()*,;:' ;
226
244
const params = percentChars + intactChars ;
227
245
const paramsEncoded = percentCharsEncoded + intactChars ;
228
246
const mixedCaseString = 'sTrInG' ;
229
247
230
- expect ( percentCharsEncoded ) . toEqual ( encode ( percentChars ) ) ;
231
- expect ( intactChars ) . toEqual ( encode ( intactChars ) ) ;
248
+ expect ( percentCharsEncoded ) . toEqual ( encodeUriQuery ( percentChars ) ) ;
249
+ expect ( intactChars ) . toEqual ( encodeUriQuery ( intactChars ) ) ;
232
250
// Verify it replaces repeated characters correctly
233
- expect ( paramsEncoded + paramsEncoded ) . toEqual ( encode ( params + params ) ) ;
251
+ expect ( paramsEncoded + paramsEncoded ) . toEqual ( encodeUriQuery ( params + params ) ) ;
234
252
// Verify it doesn't change the case of alpha characters
235
- expect ( mixedCaseString + paramsEncoded ) . toEqual ( encode ( mixedCaseString + params ) ) ;
253
+ expect ( mixedCaseString + paramsEncoded ) . toEqual ( encodeUriQuery ( mixedCaseString + params ) ) ;
236
254
} ) ;
237
255
238
256
it ( 'should encode/decode fragment' , ( ) => {
239
- const u = `/one#${ encodeURI ( " one two=three four" ) } ` ;
257
+ const u = `/one#${ encodeUriQuery ( ' one two=three four' ) } ` ;
240
258
const tree = url . parse ( u ) ;
241
259
242
260
expect ( tree . fragment ) . toEqual ( 'one two=three four' ) ;
243
- expect ( url . serialize ( tree ) ) . toEqual ( u ) ;
261
+ expect ( url . serialize ( tree ) ) . toEqual ( '/one#one%20two%3Dthree%20four' ) ;
262
+ } ) ;
263
+ } ) ;
264
+
265
+ describe ( 'special character encoding/decoding' , ( ) => {
266
+
267
+ // Tests specific to https://github.com/angular/angular/issues/10280
268
+ it ( 'should parse encoded parens in matrix params' , ( ) => {
269
+ const auxRoutesUrl = '/abc;foo=(other:val)' ;
270
+ const fooValueUrl = '/abc;foo=%28other:val%29' ;
271
+
272
+ const auxParsed = url . parse ( auxRoutesUrl ) . root ;
273
+ const fooParsed = url . parse ( fooValueUrl ) . root ;
274
+
275
+
276
+ // Test base case
277
+ expect ( auxParsed . children [ PRIMARY_OUTLET ] . segments . length ) . toBe ( 1 ) ;
278
+ expect ( auxParsed . children [ PRIMARY_OUTLET ] . segments [ 0 ] . path ) . toBe ( 'abc' ) ;
279
+ expect ( auxParsed . children [ PRIMARY_OUTLET ] . segments [ 0 ] . parameters ) . toEqual ( { foo : '' } ) ;
280
+ expect ( auxParsed . children [ 'other' ] . segments . length ) . toBe ( 1 ) ;
281
+ expect ( auxParsed . children [ 'other' ] . segments [ 0 ] . path ) . toBe ( 'val' ) ;
282
+
283
+ // Confirm matrix params are URL decoded
284
+ expect ( fooParsed . children [ PRIMARY_OUTLET ] . segments . length ) . toBe ( 1 ) ;
285
+ expect ( fooParsed . children [ PRIMARY_OUTLET ] . segments [ 0 ] . path ) . toBe ( 'abc' ) ;
286
+ expect ( fooParsed . children [ PRIMARY_OUTLET ] . segments [ 0 ] . parameters ) . toEqual ( {
287
+ foo : '(other:val)'
288
+ } ) ;
289
+ } ) ;
290
+
291
+ it ( 'should serialize encoded parens in matrix params' , ( ) => {
292
+ const testUrl = '/abc;foo=%28one%29' ;
293
+
294
+ const parsed = url . parse ( testUrl ) ;
295
+
296
+ expect ( url . serialize ( parsed ) ) . toBe ( '/abc;foo=%28one%29' ) ;
297
+ } ) ;
298
+
299
+ it ( 'should not serialize encoded parens in query params' , ( ) => {
300
+ const testUrl = '/abc?foo=%28one%29' ;
301
+
302
+ const parsed = url . parse ( testUrl ) ;
303
+
304
+ expect ( parsed . queryParams ) . toEqual ( { foo : '(one)' } ) ;
305
+
306
+ expect ( url . serialize ( parsed ) ) . toBe ( '/abc?foo=(one)' ) ;
307
+ } ) ;
308
+
309
+ // Test special characters in general
310
+
311
+ // From http://www.ietf.org/rfc/rfc3986.txt
312
+ const unreserved = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~` ;
313
+
314
+ it ( 'should encode a minimal set of special characters in queryParams and fragment' , ( ) => {
315
+ const notEncoded = unreserved + `:@!$'*,();` ;
316
+ const encode = ` +%&=#[]/?` ;
317
+ const encoded = `%20%2B%25%26%3D%23%5B%5D%2F%3F` ;
318
+
319
+ const parsed = url . parse ( '/foo' ) ;
320
+
321
+ parsed . queryParams = { notEncoded, encode} ;
322
+
323
+ expect ( url . serialize ( parsed ) ) . toBe ( `/foo?notEncoded=${ notEncoded } &encode=${ encoded } ` ) ;
324
+ } ) ;
325
+
326
+ it ( 'should encode a minimal set of special characters in fragment' , ( ) => {
327
+ const notEncoded = unreserved + `:@!$'*,();` ;
328
+ const encode = ` +%&=#[]/?` ;
329
+ const encoded = `%20%2B%25%26%3D%23%5B%5D%2F%3F` ;
330
+
331
+ const parsed = url . parse ( '/foo' ) ;
332
+
333
+ parsed . fragment = notEncoded + encode ;
334
+
335
+ expect ( url . serialize ( parsed ) ) . toBe ( `/foo#${ notEncoded } ${ encoded } ` ) ;
336
+ } ) ;
337
+
338
+ it ( 'should encode minimal special characters plus parens and semi-colon in matrix params' ,
339
+ ( ) => {
340
+ const notEncoded = unreserved + `:@!$'*,&` ;
341
+ const encode = ` /%=#()[];?+` ;
342
+ const encoded = `%20%2F%25%3D%23%28%29%5B%5D%3B%3F%2B` ;
343
+
344
+ const parsed = url . parse ( '/foo' ) ;
345
+
346
+ parsed . root . children [ PRIMARY_OUTLET ] . segments [ 0 ] . parameters = { notEncoded, encode} ;
347
+
348
+ expect ( url . serialize ( parsed ) ) . toBe ( `/foo;notEncoded=${ notEncoded } ;encode=${ encoded } ` ) ;
349
+ } ) ;
350
+
351
+ it ( 'should encode special characters in the path the same as matrix params' , ( ) => {
352
+ const notEncoded = unreserved + `:@!$'*,&` ;
353
+ const encode = ` /%=#()[];?+` ;
354
+ const encoded = `%20%2F%25%3D%23%28%29%5B%5D%3B%3F%2B` ;
355
+
356
+ const parsed = url . parse ( '/foo' ) ;
357
+
358
+ parsed . root . children [ PRIMARY_OUTLET ] . segments [ 0 ] . path = notEncoded + encode ;
359
+
360
+ expect ( url . serialize ( parsed ) ) . toBe ( `/${ notEncoded } ${ encoded } ` ) ;
244
361
} ) ;
245
362
} ) ;
246
363
0 commit comments