Skip to content

Commit

Permalink
[fixed] URLUtils recognize values containing \n
Browse files Browse the repository at this point in the history
  • Loading branch information
motiz88 authored and mjackson committed Jun 19, 2015
1 parent 5d674c9 commit 4759961
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 6 deletions.
21 changes: 18 additions & 3 deletions modules/URLUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function stringifyQuery(query) {
return qs.stringify(query, { arrayFormat: 'brackets' });
}

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

export function getPathname(path) {
return path.replace(queryMatcher, '');
Expand Down Expand Up @@ -50,7 +50,7 @@ function _compilePattern(pattern) {
regexpSource += '([^/?#]+)';
paramNames.push(match[1]);
} else if (match[0] === '*') {
regexpSource += '(.*?)';
regexpSource += '([\\s\\S]*?)';
paramNames.push('splat');
} else if (match[0] === '(') {
regexpSource += '(?:';
Expand Down Expand Up @@ -110,7 +110,7 @@ export function matchPattern(pattern, pathname) {
var captureRemaining = tokens[tokens.length - 1] !== '*';

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

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

Expand All @@ -123,6 +123,8 @@ export function matchPattern(pattern, pathname) {
} else {
remainingPathname = pathname.replace(match[0], '');
}
} else {
remainingPathname = paramValues = null;
}

return {
Expand All @@ -136,6 +138,19 @@ export function getParamNames(pattern) {
return compilePattern(pattern).paramNames;
}

export function getParams(pattern, pathname) {
var { paramNames, paramValues } = matchPattern(pattern, stripLeadingSlashes(pathname));

if (paramValues != null) {
return paramNames.reduce(function (memo, paramName, index) {
memo[paramName] = paramValues[index];
return memo;
}, {});
}

return null;
}

/**
* Returns a version of the given pattern with params interpolated. Throws
* if there is a dynamic segment of the pattern for which there is no param.
Expand Down
178 changes: 175 additions & 3 deletions modules/__tests__/URLUtils-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import expect from 'expect';
import { getPathname, getQueryString, formatPattern } from '../URLUtils';
import { getPathname, getQueryString, getParamNames, getParams, formatPattern } from '../URLUtils';

describe('getPathname', function () {
it('returns the pathname portion of a path', function () {
Expand All @@ -13,8 +13,180 @@ describe('getQueryString', function () {
});
});

describe('matchPattern', function () {
it('ignores trailing slashes');
describe('getParamNames', function () {
describe('when a pattern contains no dynamic segments', function () {
it('returns an empty array', function () {
expect(getParamNames('a/b/c')).toEqual([]);
});
});

describe('when a pattern contains :a and :b dynamic segments', function () {
it('returns the correct names', function () {
expect(getParamNames('/comments/:a/:b/edit')).toEqual([ 'a', 'b' ]);
});
});

describe('when a pattern has a *', function () {
it('uses the name "splat"', function () {
expect(getParamNames('/files/*.jpg')).toEqual([ 'splat' ]);
});
});
});

describe('getParams', function () {
describe('when a pattern does not have dynamic segments', function () {
var pattern = 'a/b/c';

describe('and the path matches', function () {
it('returns an empty object', function () {
expect(getParams(pattern, pattern)).toEqual({});
});
});

describe('and the path does not match', function () {
it('returns null', function () {
expect(getParams(pattern, 'd/e/f')).toBe(null);
});
});
});

describe('when a pattern has dynamic segments', function () {
var pattern = 'comments/:id.:ext/edit';

describe('and the path matches', function () {
it('returns an object with the params', function () {
expect(getParams(pattern, 'comments/abc.js/edit')).toEqual({ id: 'abc', ext: 'js' });
});
});

describe('and the pattern is optional', function () {
var pattern = 'comments/(:id)/edit';

describe('and the path matches with supplied param', function () {
it('returns an object with the params', function () {
expect(getParams(pattern, 'comments/123/edit')).toEqual({ id: '123' });
});
});

describe('and the path matches without supplied param', function () {
it('returns an object with an undefined param', function () {
expect(getParams(pattern, 'comments//edit')).toEqual({ id: undefined });
});
});
});

describe('and the pattern and forward slash are optional', function () {
var pattern = 'comments(/:id)/edit';

describe('and the path matches with supplied param', function () {
it('returns an object with the params', function () {
expect(getParams(pattern, 'comments/123/edit')).toEqual({ id: '123' });
});
});

describe('and the path matches without supplied param', function () {
it('returns an object with an undefined param', function () {
expect(getParams(pattern, 'comments/edit')).toEqual({ id: undefined });
});
});
});

describe('and the path does not match', function () {
it('returns null', function () {
expect(getParams(pattern, 'users/123')).toBe(null);
});
});

describe('and the path matches with a segment containing a .', function () {
it('returns an object with the params', function () {
expect(getParams(pattern, 'comments/foo.bar/edit')).toEqual({ id: 'foo', ext: 'bar' });
});
});
});

describe('when a pattern has characters that have special URL encoding', function () {
var pattern = 'one, two';

describe('and the path matches', function () {
it('returns an empty object', function () {
expect(getParams(pattern, 'one, two')).toEqual({});
});
});

describe('and the path does not match', function () {
it('returns null', function () {
expect(getParams(pattern, 'one two')).toBe(null);
});
});
});

describe('when a pattern has dynamic segments and characters that have special URL encoding', function () {
var pattern = '/comments/:id/edit now';

describe('and the path matches', function () {
it('returns an object with the params', function () {
expect(getParams(pattern, '/comments/abc/edit now')).toEqual({ id: 'abc' });
});
});

describe('and the path does not match', function () {
it('returns null', function () {
expect(getParams(pattern, '/users/123')).toBe(null);
});
});
});

describe('when a pattern has a *', function () {
describe('and the path matches', function () {
it('returns an object with the params', function () {
expect(getParams('/files/*', '/files/my/photo.jpg')).toEqual({ splat: 'my/photo.jpg' });
expect(getParams('/files/*', '/files/my/photo.jpg.zip')).toEqual({ splat: 'my/photo.jpg.zip' });
expect(getParams('/files/*.jpg', '/files/my/photo.jpg')).toEqual({ splat: 'my/photo' });
expect(getParams('/files/*.jpg', '/files/my/new\nline.jpg')).toEqual({ splat: 'my/new\nline' });
});
});

describe('and the path does not match', function () {
it('returns null', function () {
expect(getParams('/files/*.jpg', '/files/my/photo.png')).toBe(null);
});
});
});

describe('when a pattern has an optional group', function () {
var pattern = '/archive(/:name)';

describe('and the path matches', function () {
it('returns an object with the params', function () {
expect(getParams(pattern, '/archive/foo')).toEqual({ name: 'foo' });
expect(getParams(pattern, '/archive')).toEqual({ name: undefined });
});
});

describe('and the path does not match', function () {
it('returns null', function () {
expect(getParams(pattern, '/archiv')).toBe(null);
});
});
});

describe('when a param has dots', function () {
var pattern = '/:query/with/:domain';

describe('and the path matches', function () {
it('returns an object with the params', function () {
expect(getParams(pattern, '/foo/with/foo.app')).toEqual({ query: 'foo', domain: 'foo.app' });
expect(getParams(pattern, '/foo.ap/with/foo')).toEqual({ query: 'foo.ap', domain: 'foo' });
expect(getParams(pattern, '/foo.ap/with/foo.app')).toEqual({ query: 'foo.ap', domain: 'foo.app' });
});
});

describe('and the path does not match', function () {
it('returns null', function () {
expect(getParams(pattern, '/foo.ap')).toBe(null);
});
});
});
});

describe('formatPattern', function () {
Expand Down

0 comments on commit 4759961

Please sign in to comment.