Skip to content

Commit 4759961

Browse files
motiz88mjackson
authored andcommitted
[fixed] URLUtils recognize values containing \n
1 parent 5d674c9 commit 4759961

File tree

2 files changed

+193
-6
lines changed

2 files changed

+193
-6
lines changed

modules/URLUtils.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function stringifyQuery(query) {
77
return qs.stringify(query, { arrayFormat: 'brackets' });
88
}
99

10-
var queryMatcher = /\?(.*)$/;
10+
var queryMatcher = /\?([\s\S]*)$/;
1111

1212
export function getPathname(path) {
1313
return path.replace(queryMatcher, '');
@@ -50,7 +50,7 @@ function _compilePattern(pattern) {
5050
regexpSource += '([^/?#]+)';
5151
paramNames.push(match[1]);
5252
} else if (match[0] === '*') {
53-
regexpSource += '(.*?)';
53+
regexpSource += '([\\s\\S]*?)';
5454
paramNames.push('splat');
5555
} else if (match[0] === '(') {
5656
regexpSource += '(?:';
@@ -110,7 +110,7 @@ export function matchPattern(pattern, pathname) {
110110
var captureRemaining = tokens[tokens.length - 1] !== '*';
111111

112112
if (captureRemaining)
113-
regexpSource += '(.*?)';
113+
regexpSource += '([\\s\\S]*?)';
114114

115115
var match = pathname.match(new RegExp('^' + regexpSource + '$', 'i'));
116116

@@ -123,6 +123,8 @@ export function matchPattern(pattern, pathname) {
123123
} else {
124124
remainingPathname = pathname.replace(match[0], '');
125125
}
126+
} else {
127+
remainingPathname = paramValues = null;
126128
}
127129

128130
return {
@@ -136,6 +138,19 @@ export function getParamNames(pattern) {
136138
return compilePattern(pattern).paramNames;
137139
}
138140

141+
export function getParams(pattern, pathname) {
142+
var { paramNames, paramValues } = matchPattern(pattern, stripLeadingSlashes(pathname));
143+
144+
if (paramValues != null) {
145+
return paramNames.reduce(function (memo, paramName, index) {
146+
memo[paramName] = paramValues[index];
147+
return memo;
148+
}, {});
149+
}
150+
151+
return null;
152+
}
153+
139154
/**
140155
* Returns a version of the given pattern with params interpolated. Throws
141156
* if there is a dynamic segment of the pattern for which there is no param.

modules/__tests__/URLUtils-test.js

Lines changed: 175 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import expect from 'expect';
2-
import { getPathname, getQueryString, formatPattern } from '../URLUtils';
2+
import { getPathname, getQueryString, getParamNames, getParams, formatPattern } from '../URLUtils';
33

44
describe('getPathname', function () {
55
it('returns the pathname portion of a path', function () {
@@ -13,8 +13,180 @@ describe('getQueryString', function () {
1313
});
1414
});
1515

16-
describe('matchPattern', function () {
17-
it('ignores trailing slashes');
16+
describe('getParamNames', function () {
17+
describe('when a pattern contains no dynamic segments', function () {
18+
it('returns an empty array', function () {
19+
expect(getParamNames('a/b/c')).toEqual([]);
20+
});
21+
});
22+
23+
describe('when a pattern contains :a and :b dynamic segments', function () {
24+
it('returns the correct names', function () {
25+
expect(getParamNames('/comments/:a/:b/edit')).toEqual([ 'a', 'b' ]);
26+
});
27+
});
28+
29+
describe('when a pattern has a *', function () {
30+
it('uses the name "splat"', function () {
31+
expect(getParamNames('/files/*.jpg')).toEqual([ 'splat' ]);
32+
});
33+
});
34+
});
35+
36+
describe('getParams', function () {
37+
describe('when a pattern does not have dynamic segments', function () {
38+
var pattern = 'a/b/c';
39+
40+
describe('and the path matches', function () {
41+
it('returns an empty object', function () {
42+
expect(getParams(pattern, pattern)).toEqual({});
43+
});
44+
});
45+
46+
describe('and the path does not match', function () {
47+
it('returns null', function () {
48+
expect(getParams(pattern, 'd/e/f')).toBe(null);
49+
});
50+
});
51+
});
52+
53+
describe('when a pattern has dynamic segments', function () {
54+
var pattern = 'comments/:id.:ext/edit';
55+
56+
describe('and the path matches', function () {
57+
it('returns an object with the params', function () {
58+
expect(getParams(pattern, 'comments/abc.js/edit')).toEqual({ id: 'abc', ext: 'js' });
59+
});
60+
});
61+
62+
describe('and the pattern is optional', function () {
63+
var pattern = 'comments/(:id)/edit';
64+
65+
describe('and the path matches with supplied param', function () {
66+
it('returns an object with the params', function () {
67+
expect(getParams(pattern, 'comments/123/edit')).toEqual({ id: '123' });
68+
});
69+
});
70+
71+
describe('and the path matches without supplied param', function () {
72+
it('returns an object with an undefined param', function () {
73+
expect(getParams(pattern, 'comments//edit')).toEqual({ id: undefined });
74+
});
75+
});
76+
});
77+
78+
describe('and the pattern and forward slash are optional', function () {
79+
var pattern = 'comments(/:id)/edit';
80+
81+
describe('and the path matches with supplied param', function () {
82+
it('returns an object with the params', function () {
83+
expect(getParams(pattern, 'comments/123/edit')).toEqual({ id: '123' });
84+
});
85+
});
86+
87+
describe('and the path matches without supplied param', function () {
88+
it('returns an object with an undefined param', function () {
89+
expect(getParams(pattern, 'comments/edit')).toEqual({ id: undefined });
90+
});
91+
});
92+
});
93+
94+
describe('and the path does not match', function () {
95+
it('returns null', function () {
96+
expect(getParams(pattern, 'users/123')).toBe(null);
97+
});
98+
});
99+
100+
describe('and the path matches with a segment containing a .', function () {
101+
it('returns an object with the params', function () {
102+
expect(getParams(pattern, 'comments/foo.bar/edit')).toEqual({ id: 'foo', ext: 'bar' });
103+
});
104+
});
105+
});
106+
107+
describe('when a pattern has characters that have special URL encoding', function () {
108+
var pattern = 'one, two';
109+
110+
describe('and the path matches', function () {
111+
it('returns an empty object', function () {
112+
expect(getParams(pattern, 'one, two')).toEqual({});
113+
});
114+
});
115+
116+
describe('and the path does not match', function () {
117+
it('returns null', function () {
118+
expect(getParams(pattern, 'one two')).toBe(null);
119+
});
120+
});
121+
});
122+
123+
describe('when a pattern has dynamic segments and characters that have special URL encoding', function () {
124+
var pattern = '/comments/:id/edit now';
125+
126+
describe('and the path matches', function () {
127+
it('returns an object with the params', function () {
128+
expect(getParams(pattern, '/comments/abc/edit now')).toEqual({ id: 'abc' });
129+
});
130+
});
131+
132+
describe('and the path does not match', function () {
133+
it('returns null', function () {
134+
expect(getParams(pattern, '/users/123')).toBe(null);
135+
});
136+
});
137+
});
138+
139+
describe('when a pattern has a *', function () {
140+
describe('and the path matches', function () {
141+
it('returns an object with the params', function () {
142+
expect(getParams('/files/*', '/files/my/photo.jpg')).toEqual({ splat: 'my/photo.jpg' });
143+
expect(getParams('/files/*', '/files/my/photo.jpg.zip')).toEqual({ splat: 'my/photo.jpg.zip' });
144+
expect(getParams('/files/*.jpg', '/files/my/photo.jpg')).toEqual({ splat: 'my/photo' });
145+
expect(getParams('/files/*.jpg', '/files/my/new\nline.jpg')).toEqual({ splat: 'my/new\nline' });
146+
});
147+
});
148+
149+
describe('and the path does not match', function () {
150+
it('returns null', function () {
151+
expect(getParams('/files/*.jpg', '/files/my/photo.png')).toBe(null);
152+
});
153+
});
154+
});
155+
156+
describe('when a pattern has an optional group', function () {
157+
var pattern = '/archive(/:name)';
158+
159+
describe('and the path matches', function () {
160+
it('returns an object with the params', function () {
161+
expect(getParams(pattern, '/archive/foo')).toEqual({ name: 'foo' });
162+
expect(getParams(pattern, '/archive')).toEqual({ name: undefined });
163+
});
164+
});
165+
166+
describe('and the path does not match', function () {
167+
it('returns null', function () {
168+
expect(getParams(pattern, '/archiv')).toBe(null);
169+
});
170+
});
171+
});
172+
173+
describe('when a param has dots', function () {
174+
var pattern = '/:query/with/:domain';
175+
176+
describe('and the path matches', function () {
177+
it('returns an object with the params', function () {
178+
expect(getParams(pattern, '/foo/with/foo.app')).toEqual({ query: 'foo', domain: 'foo.app' });
179+
expect(getParams(pattern, '/foo.ap/with/foo')).toEqual({ query: 'foo.ap', domain: 'foo' });
180+
expect(getParams(pattern, '/foo.ap/with/foo.app')).toEqual({ query: 'foo.ap', domain: 'foo.app' });
181+
});
182+
});
183+
184+
describe('and the path does not match', function () {
185+
it('returns null', function () {
186+
expect(getParams(pattern, '/foo.ap')).toBe(null);
187+
});
188+
});
189+
});
18190
});
19191

20192
describe('formatPattern', function () {

0 commit comments

Comments
 (0)