This repository has been archived by the owner on Dec 30, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 157
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(sanitize-results): add a module to sanitize results
- Loading branch information
Showing
2 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
import sanitizeResults from '../sanitize-results'; | ||
|
||
test('it ensures results are provided in the correct format', () => { | ||
expect(() => { | ||
sanitizeResults(); | ||
}).toThrow(new TypeError('Results should be provided as an array.')); | ||
|
||
expect(() => { | ||
sanitizeResults({}); | ||
}).toThrow(new TypeError('Results should be provided as an array.')); | ||
}); | ||
|
||
test('it ensures tags are provided in the correct format', () => { | ||
expect(() => { | ||
sanitizeResults([]); | ||
}).toThrow( | ||
new TypeError('preTag and postTag should be provided as strings.') | ||
); | ||
|
||
expect(() => { | ||
sanitizeResults([], 'pre'); | ||
}).toThrow( | ||
new TypeError('preTag and postTag should be provided as strings.') | ||
); | ||
|
||
expect(() => { | ||
sanitizeResults([], {}, 'post'); | ||
}).toThrow( | ||
new TypeError('preTag and postTag should be provided as strings.') | ||
); | ||
}); | ||
|
||
test('it should escape HTML of highlighted values', () => { | ||
const results = [ | ||
{ | ||
name: 'name', | ||
_highlightResult: { | ||
name: { | ||
value: "<script>alert('Yay')</script>", | ||
matchLevel: 'full', | ||
}, | ||
}, | ||
}, | ||
]; | ||
|
||
const sanitized = sanitizeResults(results, 'pre', 'post'); | ||
|
||
expect(sanitized).toEqual([ | ||
{ | ||
name: 'name', | ||
_highlightResult: { | ||
name: { | ||
value: '<script>alert('Yay')</script>', | ||
matchLevel: 'full', | ||
}, | ||
}, | ||
}, | ||
]); | ||
}); | ||
|
||
test('it should escape HTML of snippetted values', () => { | ||
const results = [ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
name: { | ||
value: "<script>alert('Yay')</script>", | ||
matchLevel: 'full', | ||
}, | ||
}, | ||
}, | ||
]; | ||
|
||
const sanitized = sanitizeResults(results, 'pre', 'post'); | ||
|
||
expect(sanitized).toEqual([ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
name: { | ||
value: '<script>alert('Yay')</script>', | ||
matchLevel: 'full', | ||
}, | ||
}, | ||
}, | ||
]); | ||
}); | ||
|
||
test('it should not escape HTML of non highlighted attributes', () => { | ||
const results = [ | ||
{ | ||
name: '<h1>Test</h1>', | ||
}, | ||
]; | ||
|
||
const sanitized = sanitizeResults(results, 'pre', 'post'); | ||
expect(sanitized).toEqual(results); | ||
}); | ||
|
||
test('it should replace pre-post tags in highlighted values', () => { | ||
const results = [ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
name: { | ||
value: 'My __ais-highlight__resu__/ais-highlight__lt', | ||
matchLevel: 'full', | ||
}, | ||
}, | ||
}, | ||
]; | ||
|
||
const sanitized = sanitizeResults( | ||
results, | ||
'__ais-highlight__', | ||
'__/ais-highlight__' | ||
); | ||
expect(sanitized).toEqual([ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
name: { | ||
value: 'My <em>resu</em>lt', | ||
matchLevel: 'full', | ||
}, | ||
}, | ||
}, | ||
]); | ||
}); | ||
|
||
test('it should replace multiple occurences of pre-post tags in highlighted values', () => { | ||
const results = [ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
name: { | ||
value: | ||
'__ais-highlight__My__/ais-highlight__ __ais-highlight__resu__/ais-highlight__lt', | ||
matchLevel: 'full', | ||
}, | ||
}, | ||
}, | ||
]; | ||
|
||
const sanitized = sanitizeResults( | ||
results, | ||
'__ais-highlight__', | ||
'__/ais-highlight__' | ||
); | ||
expect(sanitized).toEqual([ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
name: { | ||
value: '<em>My</em> <em>resu</em>lt', | ||
matchLevel: 'full', | ||
}, | ||
}, | ||
}, | ||
]); | ||
}); | ||
|
||
test('it should handle nested attributes', () => { | ||
const results = [ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
tags: [ | ||
{ | ||
value: '__ais-highlight__Ta__/ais-highlight__g 2', | ||
matchLevel: 'full', | ||
}, | ||
{ | ||
value: '__ais-highlight__Ta__/ais-highlight__g 2', | ||
matchLevel: 'full', | ||
}, | ||
], | ||
}, | ||
}, | ||
]; | ||
|
||
const sanitized = sanitizeResults( | ||
results, | ||
'__ais-highlight__', | ||
'__/ais-highlight__' | ||
); | ||
expect(sanitized).toEqual([ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
tags: [ | ||
{ | ||
value: '<em>Ta</em>g 2', | ||
matchLevel: 'full', | ||
}, | ||
{ | ||
value: '<em>Ta</em>g 2', | ||
matchLevel: 'full', | ||
}, | ||
], | ||
}, | ||
}, | ||
]); | ||
}); | ||
|
||
test('it should handle deeply nested attributes', () => { | ||
const results = [ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
info: { | ||
tags: [ | ||
{ | ||
value: '__ais-highlight__Ta__/ais-highlight__g 2', | ||
matchLevel: 'full', | ||
}, | ||
{ | ||
value: '__ais-highlight__Ta__/ais-highlight__g 2', | ||
matchLevel: 'full', | ||
}, | ||
], | ||
}, | ||
}, | ||
}, | ||
]; | ||
|
||
const sanitized = sanitizeResults( | ||
results, | ||
'__ais-highlight__', | ||
'__/ais-highlight__' | ||
); | ||
expect(sanitized).toEqual([ | ||
{ | ||
name: 'name', | ||
_snippetResult: { | ||
info: { | ||
tags: [ | ||
{ | ||
value: '<em>Ta</em>g 2', | ||
matchLevel: 'full', | ||
}, | ||
{ | ||
value: '<em>Ta</em>g 2', | ||
matchLevel: 'full', | ||
}, | ||
], | ||
}, | ||
}, | ||
}, | ||
]); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import escapeHtml from 'escape-html'; | ||
|
||
export default function(results, preTag, postTag, tagName = 'em') { | ||
if (!Array.isArray(results)) { | ||
throw new TypeError('Results should be provided as an array.'); | ||
} | ||
|
||
if (typeof preTag !== 'string' || typeof postTag !== 'string') { | ||
throw new TypeError('preTag and postTag should be provided as strings.'); | ||
} | ||
|
||
const sanitized = []; | ||
for (const result of results) { | ||
if ('_highlightResult' in result) { | ||
result._highlightResult = sanitizeHighlights( | ||
result._highlightResult, | ||
preTag, | ||
postTag, | ||
tagName | ||
); | ||
} | ||
|
||
if ('_snippetResult' in result) { | ||
result._snippetResult = sanitizeHighlights( | ||
result._snippetResult, | ||
preTag, | ||
postTag, | ||
tagName | ||
); | ||
} | ||
|
||
sanitized.push(result); | ||
} | ||
|
||
return sanitized; | ||
} | ||
|
||
const sanitizeHighlights = function(data, preTag, postTag, tagName) { | ||
if (containsValue(data)) { | ||
const sanitized = Object.assign({}, data, { | ||
value: escapeHtml(data.value) | ||
.replace(new RegExp(preTag, 'g'), `<${tagName}>`) | ||
.replace(new RegExp(postTag, 'g'), `</${tagName}>`), | ||
}); | ||
|
||
return sanitized; | ||
} | ||
|
||
if (Array.isArray(data)) { | ||
const child = []; | ||
data.forEach(item => { | ||
child.push(sanitizeHighlights(item, preTag, postTag, tagName)); | ||
}); | ||
|
||
return child; | ||
} | ||
|
||
if (isObject(data)) { | ||
const keys = Object.keys(data); | ||
const child = {}; | ||
keys.forEach(key => { | ||
child[key] = sanitizeHighlights(data[key], preTag, postTag, tagName); | ||
}); | ||
|
||
return child; | ||
} | ||
|
||
return data; | ||
}; | ||
|
||
const containsValue = function(data) { | ||
return isObject(data) && 'matchLevel' in data && 'value' in data; | ||
}; | ||
|
||
const isObject = value => typeof value === 'object' && value !== null; |