Skip to content

Commit

Permalink
feat: add strict mode for params
Browse files Browse the repository at this point in the history
closes #1
  • Loading branch information
fczbkk committed May 5, 2016
1 parent 60540ff commit d2ffa4e
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 45 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,22 @@ myMatch.test('http://google.com/?aaa=bbb&ccc=ddd'); // true
myMatch.test('http://google.com/?ccc=ddd&aaa=bbb'); // true
```

#### Strict mode for params

You can activate strict mode for checking params by prepending exclamation mark (`!`) in front of params pattern:

```javascript
myMatch = new UrlMatch('*://*/*?!aaa=*');
```

In strict mode, all param patterns must be matched and there can be no unmatched params:

```javascript
myMatch.test('http://google.com/?aaa=bbb'); // true
myMatch.test('http://google.com/'); // false (param missing)
myMatch.test('http://google.com/?aaa=bbb&ccc=ddd'); // false (too many params)
```

### Match URL fragments

NOTE: This functionality is not available in Chrome pattern matching.
Expand Down
38 changes: 27 additions & 11 deletions src/params.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@ export default class extends UrlPart {
}

sanitize (pattern = this.original_pattern) {
if (pattern === '*') {
// strict mode
if (typeof pattern === 'string' && pattern.substring(0, 1) === '!') {
pattern = pattern.substring(1);
this.is_strict = true;
}

if (pattern === '*' || pattern === '') {
pattern = null
}

const result = {};
const result = [];

if (exists(pattern)) {

Expand All @@ -48,25 +54,35 @@ export default class extends UrlPart {
// escape all brackets
val = val.replace(/[\[\](){}]/g, '\\$&');

result[key] = val;
result.push(key + val);
});

}

return result;
}

test (content = '', pattern = this.pattern) {
test (content = '', patterns = this.pattern) {
let result = true;

if (exists(pattern)) {
for (const key in pattern) {
const val = pattern[key];
const re = new RegExp('(^|\&)' + key + val + '(\&|$)');
if (!re.test(content)) {
result = false;
}
if (exists(patterns)) {

result = patterns.reduce((previous_result, pattern) => {
const re = new RegExp('(^|\&)' + pattern + '(\&|$)');
return previous_result && re.test(content);
}, result);

if (this.is_strict === true) {
const wrapped_patterns = patterns
.map((pattern) => `(${pattern})`)
.join('|');
const re = new RegExp('(^|\&)(' + wrapped_patterns + ')(\&|$)');

result = content.split('&').reduce((previous_result, pair) => {
return previous_result && re.test(pair);
}, result);
}

}

return result;
Expand Down
1 change: 1 addition & 0 deletions src/url-part.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default class {
constructor (pattern) {
this.original_pattern = pattern;
this.pattern = this.sanitize(pattern);
this.is_strict = false;
}

get default_value () {
Expand Down
126 changes: 92 additions & 34 deletions test/params.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,70 +60,70 @@ describe('Params', function() {
describe('sanitize', function() {

it('should return empty hash on empty', function() {
expect(params.sanitize()).toEqual({});
expect(params.sanitize()).toEqual([]);
});

it('should return empty hash on single asterisk', function() {
expect(params.sanitize('*')).toEqual({});
expect(params.sanitize('*')).toEqual([]);
});

it('should handle valueless pair', function() {
expect(params.sanitize('aaa')).toEqual({
'aaa': '=?'
});
expect(params.sanitize('aaa=')).toEqual({
'aaa': '=?'
});
expect(params.sanitize('aaa')).toEqual([
'aaa=?'
]);
expect(params.sanitize('aaa=')).toEqual([
'aaa=?'
]);
});

it('should break the single pair pattern down to key/val pairs', function() {
expect(params.sanitize('aaa=bbb')).toEqual({
'aaa': '=bbb'
});
expect(params.sanitize('aaa=bbb')).toEqual([
'aaa=bbb'
]);
});

it('should break the multi pair pattern down to key/val pairs', function() {
expect(params.sanitize('aaa=bbb&ccc=ddd')).toEqual({
'aaa': '=bbb',
'ccc': '=ddd'
});
expect(params.sanitize('aaa=bbb&ccc=ddd')).toEqual([
'aaa=bbb',
'ccc=ddd'
]);
});

it('should replace asterisks in keys with universal matches', function() {
expect(params.sanitize('*=bbb')).toEqual({
'.+': '=bbb'
});
expect(params.sanitize('*=bbb')).toEqual([
'.+=bbb'
]);
});

it('should replace asterisks in vals with universal matches', function() {
expect(params.sanitize('aaa=*')).toEqual({
'aaa': '=?.*'
});
expect(params.sanitize('aaa=*')).toEqual([
'aaa=?.*'
]);
});

it('should replace partial asterisks with universal matches', function() {
expect(params.sanitize('aaa*=*bbb&ccc*ddd=*eee*')).toEqual({
'aaa.*': '=.*bbb',
'ccc.*ddd': '=.*eee.*'
});
expect(params.sanitize('aaa*=*bbb&ccc*ddd=*eee*')).toEqual([
'aaa.*=.*bbb',
'ccc.*ddd=.*eee.*'
]);
});

it('should escape square brackets', function() {
expect(params.sanitize('aaa=[]')).toEqual({
'aaa': '=\\[\\]'
});
expect(params.sanitize('aaa=[]')).toEqual([
'aaa=\\[\\]'
]);
});

it('should escape nested square brackets', function() {
expect(params.sanitize('aaa=[[]]')).toEqual({
'aaa': '=\\[\\[\\]\\]'
});
expect(params.sanitize('aaa=[[]]')).toEqual([
'aaa=\\[\\[\\]\\]'
]);
});

it('should escape serialized JSON data', function() {
expect(params.sanitize('aaa=[[]]')).toEqual({
'aaa': '=\\[\\[\\]\\]'
});
expect(params.sanitize('aaa=[[]]')).toEqual([
'aaa=\\[\\[\\]\\]'
]);
});

});
Expand Down Expand Up @@ -230,5 +230,63 @@ describe('Params', function() {
expect(params.test('aaa=', pattern)).toBe(true);
expect(params.test('aaa=bbb', pattern)).toBe(false);
});

});

describe('strict mode', function () {

it('should set `is_strict` property', function () {
params.sanitize('!aaa=*');
expect(params.is_strict).toEqual(true);
});

it('should match known params when in strict mode', function () {
const pattern = params.sanitize('!aaa=*');
expect(params.test('aaa=bbb', pattern)).toBe(true);
});

it('should not match unknown params when in strict mode', function () {
const pattern = params.sanitize('!aaa=*');
expect(params.test('aaa=bbb&ccc=ddd', pattern)).toBe(false);
expect(params.test('ccc=ddd', pattern)).toBe(false);
});

it('should not match any params when empty in strict mode', function () {
const pattern = params.sanitize('!');
expect(params.test('aaa=bbb', pattern)).toBe(false);
});

it('should match empty params when empty in strict mode', function () {
const pattern = params.sanitize('!');
expect(params.test('', pattern)).toBe(true);
});

it('should match generic key param in strict mode', function () {
const pattern = params.sanitize('!*=aaa');
expect(params.test('bbb=aaa', pattern)).toBe(true);
expect(params.test('ccc=aaa', pattern)).toBe(true);
});

it('should match multiple key params in strict mode', function () {
const pattern = params.sanitize('!*=aaa');
expect(params.test('bbb=aaa&ccc=aaa', pattern)).toBe(true);
});

it('should match multiple generic key params in strict mode', function () {
const pattern = params.sanitize('!*=aaa&*=ccc');
expect(params.test('bbb=aaa&bbb=ccc', pattern)).toBe(true);
expect(params.test('bbb=aaa&bbb=ccc&xxx=yyy', pattern)).toBe(false);
});

it('should match multiple val params in strict mode', function () {
const pattern = params.sanitize('!aaa=*');
expect(params.test('aaa=bbb&aaa=ccc', pattern)).toBe(true);
});

it('should not match one of multiple params in strict mode', function () {
const pattern = params.sanitize('!aaa=*ccc');
expect(params.test('aaa=bbbccc&aaa=xxx', pattern)).toBe(false);
});

});
});

0 comments on commit d2ffa4e

Please sign in to comment.